Java tutorial
/*! * Copyright 2002 - 2018 Webdetails, a Hitachi Vantara company. All rights reserved. * * This software was developed by Webdetails and is provided under the terms * of the Mozilla Public License, Version 2.0, or any later version. You may not use * this file except in compliance with the license. If you need a copy of the license, * please go to http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails. * * Software distributed under the Mozilla Public License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to * the license for the specific language governing your rights and limitations. */ package org.pentaho.cdf; import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_XML; import static javax.ws.rs.core.MediaType.TEXT_PLAIN; import static javax.ws.rs.core.MediaType.TEXT_HTML; import static pt.webdetails.cpf.utils.MimeTypes.CSV; import static pt.webdetails.cpf.utils.MimeTypes.JAVASCRIPT; import static pt.webdetails.cpf.utils.MimeTypes.XLS; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONException; import org.json.JSONObject; import org.owasp.encoder.Encode; import org.pentaho.cdf.context.ContextEngine; import org.pentaho.cdf.embed.EmbeddedHeadersGenerator; import org.pentaho.cdf.environment.CdfEngine; import org.pentaho.cdf.export.Export; import org.pentaho.cdf.export.ExportCSV; import org.pentaho.cdf.export.ExportExcel; import org.pentaho.cdf.export.IExport; import org.pentaho.cdf.util.Parameter; import org.pentaho.cdf.xactions.ActionEngine; import org.pentaho.platform.api.engine.IPluginResourceLoader; import org.pentaho.platform.engine.core.system.PentahoRequestContextHolder; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.util.web.MimeHelper; import pt.webdetails.cpf.Util; import pt.webdetails.cpf.repository.api.IReadAccess; import pt.webdetails.cpf.utils.CharsetHelper; import pt.webdetails.cpf.utils.PluginIOUtils; @Path("/pentaho-cdf/api") public class CdfApi { private static final Log logger = LogFactory.getLog(CdfApi.class); private static final String HTTP = "http"; private static final String HTTPS = "https"; private static final int DEFAULT_HTTP_PORT = 80; private static final int DEFAULT_HTTPS_PORT = 443; @GET @Path("/ping") @Produces(TEXT_PLAIN) public Response doGetPing() throws InvalidCdfOperationException { return Response.ok("{\"ping\":\"ok\"}").build(); } @POST @Path("/ping") @Produces(TEXT_PLAIN) public Response doPostPing() throws InvalidCdfOperationException { return Response.ok("{\"ping\":\"ok\"}").build(); } @GET @Path("/getResource") @Consumes({ APPLICATION_XML, APPLICATION_JSON, APPLICATION_FORM_URLENCODED }) public void getResource(@QueryParam(Parameter.RESOURCE) String resource, @QueryParam(Parameter.PATH) String path, @Context HttpServletResponse response) throws Exception { try { if (!StringUtils.isEmpty(resource) && StringUtils.isEmpty(path)) { // legacy calls used resource param; 5.0 calls use path param path = resource; } path = path != null && path.endsWith("/content") ? path.substring(0, path.indexOf("/content")) : path; response.setHeader("Content-Type", MimeHelper.getMimeTypeFromFileName(path)); final IPluginResourceLoader resLoader = PentahoSystem.get(IPluginResourceLoader.class, null); final String formats = resLoader.getPluginSetting(this.getClass(), CdfConstants.PLUGIN_SETTINGS_DOWNLOADABLE_FORMATS); List<String> allowedFormats = Arrays.asList(StringUtils.split(formats, ',')); String extension = path.replaceAll(".*\\.(.*)", "$1"); if (allowedFormats.indexOf(extension) < 0) { // We can't provide this type of file throw new SecurityException("Not allowed"); } IReadAccess systemAccess = CdfEngine.getPluginSystemReader(null); if (!systemAccess.fileExists(path)) { logger.warn("resource does not exist: " + path); return; } PluginIOUtils.writeOutAndFlush(response.getOutputStream(), systemAccess.getFileInputStream(path)); response.getOutputStream().flush(); } catch (Exception e) { logger.error(e); response.sendError(HttpServletResponse.SC_FORBIDDEN); } } @POST @Path("/getResource") public void postResource(@QueryParam(Parameter.RESOURCE) String resource, @QueryParam(Parameter.PATH) String path, @Context HttpServletResponse response) throws Exception { getResource(resource, path, response); } @GET @Path("/getContext") @Consumes({ APPLICATION_XML, APPLICATION_JSON, APPLICATION_FORM_URLENCODED }) public String getContext(@QueryParam(Parameter.PATH) @DefaultValue(StringUtils.EMPTY) String path, @QueryParam(Parameter.ACTION) @DefaultValue(StringUtils.EMPTY) String action, @QueryParam(Parameter.VIEW) @DefaultValue(StringUtils.EMPTY) String view, @Context HttpServletRequest servletRequest) { int inactiveInterval = servletRequest.getSession().getMaxInactiveInterval(); return ContextEngine.getInstance().getContext(path, Parameter.asHashMap(servletRequest), inactiveInterval); } @GET @Path("/clearCache") public void clearCache(@Context HttpServletResponse servletResponse) { try { ContextEngine.clearCache(); PluginIOUtils.writeOutAndFlush(servletResponse.getOutputStream(), "Cache Cleared"); } catch (IOException e) { logger.error("failed to clear CDF cache"); } } @POST @Path("/export") @Consumes({ APPLICATION_XML, APPLICATION_JSON, APPLICATION_FORM_URLENCODED }) public void doPostExport(@FormParam(Parameter.SOLUTION) String solution, @FormParam(Parameter.PATH) String path, @FormParam(Parameter.ACTION) String action, @FormParam(Parameter.CONTENT_TYPE) @DefaultValue(TEXT_HTML) String contentType, @FormParam(Parameter.EXPORT_TYPE) @DefaultValue(IExport.DEFAULT_EXPORT_TYPE) String exportType, @Context HttpServletRequest request, @Context HttpServletResponse response) throws Exception { export(solution, path, action, contentType, exportType, request, response); } @GET @Path("/export") @Consumes({ APPLICATION_XML, APPLICATION_JSON, APPLICATION_FORM_URLENCODED }) public void export(@QueryParam(Parameter.SOLUTION) String solution, @QueryParam(Parameter.PATH) String path, @QueryParam(Parameter.ACTION) String action, @QueryParam(Parameter.CONTENT_TYPE) @DefaultValue(TEXT_HTML) String contentType, @QueryParam(Parameter.EXPORT_TYPE) @DefaultValue(IExport.DEFAULT_EXPORT_TYPE) String exportType, @Context HttpServletRequest request, @Context HttpServletResponse response) throws Exception { String value = determineCorrectPath(solution, action, path); if (ActionEngine.getInstance().executeAction(value, contentType, request, response, PentahoSessionHolder.getSession(), Parameter.asHashMap(request))) { Export export; if (IExport.EXPORT_TYPE_CSV.equalsIgnoreCase(exportType)) { export = new ExportCSV(response.getOutputStream()); response.setHeader("Content-Type", CSV); } else { export = new ExportExcel(response.getOutputStream()); response.setHeader("Content-Type", XLS); } response.setHeader("Cache-Control", "max-age=0, no-store"); response.setHeader("content-disposition", "attachment; filename=" + "export" + export.getExtension()); export.exportFile(new JSONObject(response.getOutputStream())); } } @GET @Path("/callAction") public void callAction(@QueryParam(Parameter.SOLUTION) String solution, @QueryParam(Parameter.PATH) String path, @QueryParam(Parameter.ACTION) String action, @QueryParam(Parameter.CONTENT_TYPE) @DefaultValue(TEXT_HTML) String contentType, @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) throws Exception { String value = determineCorrectPath(solution, action, path); ActionEngine.getInstance().executeAction(value, contentType, servletRequest, servletResponse, PentahoSessionHolder.getSession(), Parameter.asHashMap(servletRequest)); } @GET @Path("/getJSONSolution") @Consumes({ APPLICATION_XML, APPLICATION_JSON }) @Produces(APPLICATION_JSON) public void getJSONSolution(@QueryParam(Parameter.SOLUTION) String solution, @QueryParam(Parameter.PATH) @DefaultValue("/") String path, @QueryParam(Parameter.ACTION) String action, @QueryParam(Parameter.DEPTH) @DefaultValue("-1") int depth, @QueryParam(Parameter.SHOW_HIDDEN_FILES) @DefaultValue("false") boolean showHiddenFiles, @QueryParam(Parameter.MODE) @DefaultValue("*") String mode, @Context HttpServletResponse servletResponse) throws InvalidCdfOperationException { servletResponse.setContentType(APPLICATION_JSON); servletResponse.setCharacterEncoding(CharsetHelper.getEncoding()); try { writeJSONSolution(determineCorrectPath(solution, action, path), depth, showHiddenFiles, mode, servletResponse); } catch (Exception e) { logger.error("Error retrieving json solution", e); throw new InvalidCdfOperationException(e.getMessage()); } } @GET @Path("/viewAction") public void doGetViewAction(@QueryParam(Parameter.SOLUTION) String solution, @QueryParam(Parameter.PATH) String path, @QueryParam(Parameter.ACTION) String action, @QueryParam(Parameter.CONTENT_TYPE) @DefaultValue(TEXT_HTML) String contentType, @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) throws Exception { doPostViewAction(solution, path, action, contentType, null, null, null, null, servletRequest, servletResponse); } @POST @Path("/viewAction") public void doPostViewAction(@QueryParam(Parameter.SOLUTION) String solution, @QueryParam(Parameter.PATH) String path, @QueryParam(Parameter.ACTION) String action, @QueryParam(Parameter.CONTENT_TYPE) @DefaultValue(TEXT_HTML) String contentType, @FormParam(Parameter.QUERY_TYPE) String queryType, @FormParam(Parameter.QUERY) String query, @FormParam(Parameter.CATALOG) String catalog, @FormParam(Parameter.JNDI) String jndi, @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) throws Exception { String value = determineCorrectPath(solution, action, path); HashMap<String, String> paramMap = new HashMap<String, String>(); if (!StringUtils.isEmpty(queryType) && !paramMap.containsKey(Parameter.QUERY_TYPE)) { paramMap.put(Parameter.QUERY_TYPE, queryType); } if (!StringUtils.isEmpty(query) && !paramMap.containsKey(Parameter.QUERY)) { paramMap.put(Parameter.QUERY, query); } if (!StringUtils.isEmpty(catalog) && !paramMap.containsKey(Parameter.CATALOG)) { paramMap.put(Parameter.CATALOG, catalog); } if (!StringUtils.isEmpty(jndi) && !paramMap.containsKey(Parameter.JNDI)) { paramMap.put(Parameter.JNDI, jndi); } boolean success = ActionEngine.getInstance().executeAction(value, contentType, servletRequest, servletResponse, PentahoSessionHolder.getSession(), paramMap); if (success) { servletResponse.getOutputStream().flush(); } } private String determineCorrectPath(String solution, String action, String path) { if (!StringUtils.isEmpty(solution) || !StringUtils.isEmpty(action)) { // legacy call using solution, path, action request parameters return Util.joinPath(solution, path, action); } else if (!StringUtils.isEmpty(path)) { // 5.0 call using path return path; } return StringUtils.EMPTY; } @GET @Path("/cdf-embed.js") @Produces(JAVASCRIPT) public void getCdfEmbeddedContext(@Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) throws Exception { buildCdfEmbedContext(servletRequest.getProtocol(), servletRequest.getServerName(), servletRequest.getServerPort(), servletRequest.getSession().getMaxInactiveInterval(), servletRequest.getParameter("locale"), servletRequest, servletResponse); } // CDE will call buildCdfEmbedContext via InterPluginCall public void buildCdfEmbedContext(@QueryParam("protocol") String protocol, @QueryParam("name") String name, @QueryParam("port") int port, @QueryParam("inactiveInterval") int inactiveInterval, @QueryParam("locale") String locale, @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) throws Exception { buildCdfEmbedContextSecure(protocol, name, port, inactiveInterval, locale, servletRequest.isSecure(), servletRequest, servletResponse); } // CDE will call buildCdfEmbedContext via InterPluginCall public void buildCdfEmbedContextSecure(@QueryParam("protocol") String protocol, @QueryParam("name") String name, @QueryParam("port") int port, @QueryParam("inactiveInterval") int inactiveInterval, @QueryParam("locale") String locale, @QueryParam("secure") boolean secure, @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) throws Exception { try { EmbeddedHeadersGenerator embeddedHeadersGenerator = new EmbeddedHeadersGenerator( buildFullServerUrl(protocol, name, port, secure), getConfiguration("", Parameter.asHashMap(servletRequest), inactiveInterval)); if (!StringUtils.isEmpty(locale)) { embeddedHeadersGenerator.setLocale(new Locale(locale)); } PluginIOUtils.writeOutAndFlush(servletResponse.getOutputStream(), embeddedHeadersGenerator.generate()); } catch (IOException ex) { logger.error("getCdfEmbeddedContext: " + ex.getMessage(), ex); throw ex; } } protected String getConfiguration(String path, HashMap<String, String> parameterMap, int inactiveInterval) throws JSONException { return ContextEngine.getInstance().getConfig(path, parameterMap, inactiveInterval); } /** * Builds a full URL based in the given parameters * @param protocol The protocol version, which can be HTTP/0.9, HTTP/1.0, HTTP/1.1 or HTTP/2.0 * @param serverName The hostname specified by the Host http header * @param serverPort The server port to which the connection was established * @param secure Indicates if the HTTPS scheme was used * @return */ protected String buildFullServerUrl(String protocol, String serverName, int serverPort, boolean secure) { // the http protocol is completely unnecessary here since it has no relation to the scheme used and not used in the construction of the url // the http protocol specifies the protocol version while the scheme specifies if http, https, ftp or another scheme was used // it is up to the http client (web browser or other) to tell the server which version of the protocol is going to be used String scheme = secure ? HTTPS : HTTP; String port = (!secure && serverPort == DEFAULT_HTTP_PORT) || (secure && serverPort == DEFAULT_HTTPS_PORT) ? "" : ":" + serverPort; return scheme + "://" + Encode.forJavaScriptBlock(Encode.forHtmlUnquotedAttribute(serverName)) + port + PentahoRequestContextHolder.getRequestContext().getContextPath(); } protected void writeJSONSolution(String path, int depth, boolean showHiddenFiles, String mode, HttpServletResponse servletResponse) throws Exception { PluginIOUtils.writeOutAndFlush(servletResponse.getOutputStream(), NavigateComponent.getJSONSolution(path, depth, showHiddenFiles, mode).toString(2)); } }