Java tutorial
/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.web.http.api.resources; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.enunciate.Facet; import org.codehaus.enunciate.jaxrs.ResponseCode; import org.codehaus.enunciate.jaxrs.StatusCodes; import org.pentaho.platform.api.engine.IContentGenerator; import org.pentaho.platform.api.engine.IContentInfo; import org.pentaho.platform.api.engine.IPluginManager; import org.pentaho.platform.api.engine.IPluginOperation; import org.pentaho.platform.api.engine.ObjectFactoryException; import org.pentaho.platform.api.engine.PluginBeanException; import org.pentaho.platform.api.repository2.unified.IUnifiedRepository; import org.pentaho.platform.api.repository2.unified.RepositoryFile; import org.pentaho.platform.api.repository2.unified.data.simple.SimpleRepositoryFileData; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.repository.RepositoryDownloadWhitelist; import org.pentaho.platform.repository.RepositoryFilenameUtils; import org.pentaho.platform.repository2.unified.webservices.ExecutableFileTypeDto; import org.pentaho.platform.util.RepositoryPathEncoder; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import java.io.FileNotFoundException; import java.io.IOException; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import static javax.ws.rs.core.MediaType.*; import static javax.ws.rs.core.Response.Status.NOT_FOUND; /** * The RepositoryResource service retrieves the repository files through various methods. Allows you to execute repository content. */ @Path("/repos") public class RepositoryResource extends AbstractJaxRSResource { protected IPluginManager pluginManager = PentahoSystem.get(IPluginManager.class); private static final Log logger = LogFactory.getLog(RepositoryResource.class); public static final String GENERATED_CONTENT_PERSPECTIVE = "generatedContent"; //$NON-NLS-1$ protected IUnifiedRepository repository = PentahoSystem.get(IUnifiedRepository.class); protected RepositoryDownloadWhitelist whitelist; @GET @Path("{pathId : .+}/content") @Produces({ WILDCARD }) @Facet(name = "Unsupported") public Response doGetFileOrDir(@PathParam("pathId") String pathId) throws FileNotFoundException { FileResource fileResource = new FileResource(httpServletResponse); fileResource.setWhitelist(whitelist); return fileResource.doGetFileOrDir(pathId); } /** * Takes a pathId to a file and generates a URI that represents the URL to call to generate content from that file. * * <p><b>Example Request:</b><br /> * GET pentaho/api/repos/public:steel%20wheels:Invoice%20(report).prpt/default * </p> * * @param pathId @param pathId * * @return URI that represents a forwarding URL to execute to generate content from the file {pathId}. * * <p><b>Example Response:</b></p> * <pre function="syntax.xml"> * This response does not contain data. * </pre> */ @GET @Path("{pathId : .+}/default") @Produces({ WILDCARD }) @StatusCodes({ @ResponseCode(code = 303, condition = "Successfully get the resource."), @ResponseCode(code = 404, condition = "Failed to find the resource.") }) public Response doExecuteDefault(@PathParam("pathId") String pathId) throws FileNotFoundException, MalformedURLException, URISyntaxException { String perspective = null; StringBuffer buffer = null; String url = null; String path = FileResource.idToPath(pathId); String extension = path.substring(path.lastIndexOf('.') + 1); IPluginManager pluginManager = PentahoSystem.get(IPluginManager.class, PentahoSessionHolder.getSession()); IContentInfo info = pluginManager.getContentTypeInfo(extension); for (IPluginOperation operation : info.getOperations()) { if (operation.getId().equalsIgnoreCase("RUN")) { //$NON-NLS-1$ perspective = operation.getPerspective(); break; } } if (perspective == null) { perspective = GENERATED_CONTENT_PERSPECTIVE; } buffer = httpServletRequest.getRequestURL(); String queryString = httpServletRequest.getQueryString(); url = buffer.substring(0, buffer.lastIndexOf("/") + 1) + perspective + //$NON-NLS-1$ ((queryString != null && queryString.length() > 0) ? "?" + httpServletRequest.getQueryString() : ""); return Response.seeOther((new URL(url)).toURI()).build(); } /** * Gets a resource identified by the compound key contextId and resourceId. This request may include additional parameters used to render the resource. * * <p><b>Example Request:</b><br /> * POST pentaho/api/repos/xanalyzer/service/ajax/lookupXmiId * <br /><b>POST data:</b> * <pre function="syntax.xml"> * catalog=t&cube=t&time=1389817320072 * </pre> * </p> * * @param contextId Identifies the context in which the resource should be retrieved. This value may be a repository file ID, repository file extension or plugin ID * @param resourceId Identifies a resource to be retrieved. This value may be a static file residing in a publicly visible plugin folder, repository file ID or content generator ID * @param formParams Any arguments needed to render the resource * * @return A jax-rs Response object with the appropriate status code, header, and body. In many cases this will trigger a streaming operation after it it is returned to the caller.. * * <p><b>Example Response:</b></p> * <pre function="syntax.xml"> * This response does not contain data. * </pre> */ @Path("/{contextId}/{resourceId : .+}") @POST @Consumes(APPLICATION_FORM_URLENCODED) @Produces({ WILDCARD }) @StatusCodes({ @ResponseCode(code = 200, condition = "Successfully get the resource."), @ResponseCode(code = 404, condition = "Failed to find the resource.") }) public Response doFormPost(@PathParam("contextId") String contextId, @PathParam("resourceId") String resourceId, final MultivaluedMap<String, String> formParams) throws ObjectFactoryException, PluginBeanException, IOException, URISyntaxException { httpServletRequest = JerseyUtil.correctPostRequest(formParams, httpServletRequest); if (logger.isDebugEnabled()) { for (Object key : httpServletRequest.getParameterMap().keySet()) { logger.debug("param [" + key + "]"); //$NON-NLS-1$ //$NON-NLS-2$ } } return doService(contextId, resourceId); } /** * Gets a resource identified by the compound key contextId and resourceId. This request may include additional parameters used to render the resource. * * <p><b>Example Request:</b><br /> * GET pentaho/api/repos/admin-plugin/resources/authenticationProviderModule/authenticationProviderAdmin.html * </p> * * @param contextId Identifies the context in which the resource should be retrieved. This value may be a repository file ID, repository file extension or plugin ID. * @param resourceId Identifies a resource to be retrieved. This value may be a static file residing in a publicly visible plugin folder, repository file ID or content generator ID. * * @return A jax-rs Response object with the appropriate status code, header, and body. * * <p><b>Example Response:</b></p> * <pre function="syntax.xml"> *<!DOCTYPE html> *<html xmlns:pho="http:/www.pentaho.com"> *<head> *<title>Report Parameter UI</title> *<link rel="stylesheet" type="text/css" href="authenticationProviderAdmin.css" /> *<link rel="stylesheet" type="text/css" href="../../../common-ui/resources/web/dojo/dijit/themes/pentaho/pentaho.css" /> *<script type="text/javascript" src="../../../../webcontext.js"></script> *<script type="text/javascript"> *require(["authenticationProviderAdmin"]); *</script> *</head> *<body class="soria" style="border: none"> * *<!-- tree dialog --> *<div id="ldapTreeDialog" data-dojo-type="dijit.Dialog" data-dojo-props='title:"LDAP Browser"' class="dialog"> *<div id="ldapTreeDialogContent" class="dialog-content ldap-tree-padding"> *<div id="ldapTree" data-dojo-props="autoExpand:true"></div> *</div> *<div class="dialog-buttons"> *<div class="container"> *<button id="btn_ldapTreeDialogOk" class="pentaho-button ok-button first"> </button> *<button id="btn_ldapTreeDialogCancel" class="pentaho-button cancel-button last"> </button> *</div> *</div> *</div> * *<!-- override dialog --> *<div id="ldapDirtyDialog" data-dojo-type="dijit.Dialog" class="dialog"> *<div class="dialog-content pentaho-padding-sm"> *<p class="message">You have unsaved changes. Do you want to continue?</p> *</div> *<div class="dialog-buttons"> *<div class="container"> *<button id="btn_ldapDirtyDialogNo" class="pentaho-button no-button first"> </button> *<button id="btn_ldapDirtyDialogYes" class="pentaho-button yes-button last"> </button> *</div> *</div> *</div> * *<!-- test dialog --> *<div id="ldapTestMsgDialog" data-dojo-type="dijit.Dialog" class="dialog"> *<div class="dialog-content pentaho-padding-sm"> *<p class="message"> </p> *</div> *<div class="dialog-buttons"> *<div class="container"> *<button id="btn_hideTest" class="pentaho-button close-button last"> </button> *</div> *</div> *</div> * *<!-- edit server connection --> *<div id="editServerDialog" data-dojo-type="dijit.Dialog" data-dojo-props='title:"Edit External Authentication Server Connection"' class="dialog"> *<div class="dialog-content pentaho-padding-sm"> *<p class="message">Changing server conneciton will remove all current authentication and premissions settings. Do you want to continue?</p> *</div> *<div class="dialog-buttons"> *<div class="container"> *<button id="btn_editServerDialogYesClick" class="pentaho-button ok-button first"> </button> *<button id="btn_editServerDialogNoClick" class="pentaho-button cancel-button last"> </button> *</div> *</div> *</div> * *<!-- edit authentication method --> *<div id="authenticationChangeDialog" data-dojo-type="dijit.Dialog" class="dialog" > *<div class="dialog-content pentaho-padding-sm"> *<p class="message">Changing the authentication method will remove all current authentication and premissions settings. Do you want to continue?</p> *</div> *<div class="dialog-buttons"> *<div class="container"> *<button id="btn_processAuthenticationMethodChange" class="pentaho-button yes-change-button first"> </button> *<button id="btn_authenticationChangeNoClick" class="pentaho-button no-button last"> </button> *</div> *</div> *</div> * * * *<!-- populator dialog --> *<div id="ldapPopTestDialog" data-dojo-type="dijit.Dialog" class="dialog"> *<div class="dialog-content pentaho-padding-sm"> *<div class="groupOption"> *<div class="ldapPopulatorGroupRoleAttributeLabel">Group Role Attribute:</div> *<div class="ldapPopulatorGroupRoleAttributeValue value"></div> *</div> *<div class="groupOption"> *<div class="ldapPopulatorGroupRoleSearchBaseLabel">Group Search Base:</div> *<div class="ldapPopulatorGroupRoleSearchBaseValue value"></div> *</div> *<div class="groupOption"> *<div class="ldapPopulatorGroupSearchFilterLabel">Group Search Filter:</div> *<div class="ldapPopulatorGroupSearchFilterValue value"></div> *</div> *<div class="groupOption"> *<div class="ldapPopulatorRolePrefixLabel">Role Prefix:</div> *<div class="ldapPopulatorRolePrefixValue value"></div> *</div> *<div class="groupOption"> *<div class="ldapUserLabel">User Name:</div> *</div> *<input id="ldapPopTestUserName" type="text" /> *<br /> *<div class="groupOption"> *<label class="ldapUserDomainLabel">User DN:</label> *</div> *<input id="ldapPopTestUserDn" type="text"/> *<br /> *</div> *<div class="dialog-buttons"> *<div class="container"> *<button id="btn_testPopulator" class="pentaho-button ok-button first"> </button> *<button id="btn_hideLdapPropsTest" class="pentaho-button cancel-button last"> </button> *</div> *</div> *</div> * * * *<!-- user test dialog --> *<div id="ldapUserTestDialog" data-dojo-type="dijit.Dialog" class="dialog"> *<div class="dialog-content pentaho-padding-sm"> *<p class="message">With the search base and search filter configuration search for a user name that exists in your LDAP server.</p> *<br/> *<div class="groupOption"> *<div class="ldapUserTestLabel">Search For User:</div> *</div> *<input class="ldapUserTestUserName" type="text" /> *<br /> *</div> *<div class="dialog-buttons"> *<div class="container"> *<button id="btn_testLdapUserSearch" class="pentaho-button ok-button first"> </button> *<button id="btn_hideLdapUserTestDialog" class="pentaho-button cancel-button last"> </button> *</div> *</div> *</div> * * *<div style="padding: 0px;"> *<div class="pentaho-fieldgroup-major titleLabel">Authentication</div> *<br/> *<!-- CONNECTION PARAMS --> *<div id="authenticationSelector"> * *<div class="authenticationMethodLabel authMethod">Authentication Method</div> *<div class="authText authenticationMethodDescriptionLabel"> *Select where user and their log in credentials will be managed: *</div> * *<div class="groupOption"> *<input checked="checked" name="securityProvider" type="radio" value="jackrabbit" /> *<div class="pentahoSecurityLabel authValue">Local - Use basic Pentaho authentication</div> *</div> *<div class="groupOption"> *<input name="securityProvider" type="radio" value="ldap" /> *<div class="ldapSecurityLabel authValue">External - Use LDAP / Active Directory server</div> *</div> *</div> *<br /> * * *<br /> *<div id="ldapConnection" style="display: none"> *<div class="ldapConnectionTitleLabel authMethod">LDAP Server Connection</div> * *<!-- to edit config --> *<div id="ldapConnectionEdit" style="display:block"> *<div class="authText ldapServerUrlLabel">Server URL:</div> *<input class="ldapServerUrlInput authValue adminField" type="text" /> * *<div class="authText ldapUserLabel">User Name:</div> *<input class="ldapUserInput authValue adminField" type="text" /> * *<div class="authText ldapPasswordLabel">Password:</div> *<input class="ldapPasswordInput authValue adminField" type="password" /> * *<br/><br/> *<div class="authText ldapTestConnectionLabel">Test connection to complete LDAP setup</div> *<br/> *<div class="securityConfigButton"> *<button id="testServerConnectionButton" class="pentaho-button testServerConnectionButton" > *</button> *</div> *</div> * *<!-- edited config --> *<div id="ldapConnectionEditor" style="display:none"> *<div class="authText ldapServerUrlLabel" >Server URL:</div> *<div class="groupOption"> *<div class="ldapServerUrlValue authValue"></div> *<div class="pentaho-editbutton" id="btn_editConnection" title="Edit connection"></div> *</div> *</div> * *<br /> * *<div> *<div id="ldapSettingsGroup" style="display: none"> * *<!-- Ldap administration configuration --> *<div id="ldapAdministration"> *<div class="ldapAdministrationTitleLabel authMethod">Pentaho System Administrator</div> *<div class="ldapAdministratiorUserLabel authText">Select user from LDAP server:</div> *<div class="groupOption"> *<input class="ldapAdministratorUserInput adminField" type="text" /> *<button class="adminButton" id="btn_ldapAdministratorUserInput"> </button> *</div> *<div class="ldapAdministrationRoleLabel authText">Select role from LDAP server:</div> *<div class="groupOption"> *<input class="ldapAdministratorRoleInput adminField" type="text" /> *<button class="adminButton" id="btn_ldapAdministratorRoleInput" > </button> *</div> *</div> *<br/><br/> * *<!-- ldap configuration --> *<div class="ldapConfigurationTitle authMethod">LDAP Configuration</div> *<div class="authText" id="customLdapProviderLabel" >Other</div> *<div class="groupOption"> *<select id="ldapTypeSelector"> *<option class="ldapTypeSelectorApacheOption" selected="selected" value="ldapApacheConfiguration">Apache DS</option> *<option class="ldapTypeSelectorCustomOption" value="ldapCustomConfiguration">Custom</option> *</select> *</div> * *<!-- ldap apache configuration --> *<div id="ldapApacheConfiguration" class="ldapApacheConfiguration configuration" style="display: none;"> *<!-- User Base --> *<div class="ldapUserBaseLabel authText">User Base:</div> *<div class="groupOption"> *<input class="ldapUserSearchBaseInput adminField" type="text" /> *<button class="adminButton" id="btn_ldapUserSearchBaseInput"> </button> *</div> *<!-- Group Base --> *<div class="ldapGroupBaseLabel authText">Group Base:</div> *<div class="groupOption"> *<input class="ldapGroupBaseInput adminField" type="text" /> *<button class="adminButton" id="btn_ldapGroupBaseInput" > </button> *</div> * *<div style="display: none"> *<!-- This stuff is hidden but populated for save functions --> *<input class="ldapUserSearchFilterInput" /> * *<input class="ldapRoleBaseInput" /> *<input class="ldapRoleSearchBaseInput" /> *<input class="ldapRoleSearchFilterInput" /> * *<input class="ldapPopulatorGroupRoleAttributeInput" /> *<input class="ldapPopulatorGroupSearchFilterInput" /> *<input class="ldapPopulatorGroupRoleSearchBaseInput" /> *<input class="ldapPopulatorRolePrefixInput" /> *<input class="ldapPopulatorSubtreeInput" name="ldapPopulatorSubtreeInput" type="radio" value="false" /> *<input class="ldapPopulatorUpperCaseInput" name="ldapPopulatorUpperCaseInput" type="radio" value="false" /> *</div> *</div> * *<div id="ldapMicrosoftConfiguration" class="microsoftConfigPanel configuration" style="display: none;"> *<div class="ldapUserBaseLabel authText">User Base:</div> *<div class="groupOption"> *<input class="ldapUserSearchBaseInput adminField" type="text" /> *<button class="adminButton" id="btn_ldapUserSearchBaseInput2" > </button> *</div> * *<div class="ldapGroupBaseLabel authText">Group Base:</div> *<div class="groupOption"> *<input class="ldapGroupBaseInput adminField" type="text" /> *<button class="adminButton" id="btn_ldapGroupBaseInput2"> </button> *</div> * *<div style="display: none"> *<!-- This stuff is hidden but populated for test and save functions --> *<input class="ldapUserSearchFilterInput" /> * *<input class="ldapRoleBaseInput" /> *<input class="ldapRoleSearchBaseInput" /> *<input class="ldapRoleSearchFilterInput" /> * *<input class="ldapPopulatorGroupRoleAttributeInput" /> *<input class="ldapPopulatorGroupSearchFilterInput" /> *<input class="ldapPopulatorGroupRoleSearchBaseInput" /> *<input class="ldapPopulatorRolePrefixInput" /> *<input class="ldapPopulatorSubtreeInput" name="ldapPopulatorSubtreeInput" type="radio" value="false" /> *<input class="ldapPopulatorUpperCaseInput" name="ldapPopulatorUpperCaseInput" type="radio" value="false" /> *</div> *</div> * *<!-- ldap custom configuration --> *<div id="ldapCustomConfiguration" class="ldapCustomConfiguration configuration" style="display: none;"> *<!-- user search configuration --> *<br/> *<span class="ldapCustomUserSearchTitle authMethod">User Search</span> *<br/> *<div> *<div class="ldapUserSearchBaseLabel authText">Search Base:</div> *<input class="ldapUserSearchBaseInput adminField" type="text" /> * *<div class="ldapUserSearchFilderLabel authText">Search Filter:</div> *<input class="ldapUserSearchFilterInput adminField" type="text" /> * *<br/> <br/> * *<div class="securityConfigButton"> *<button class="pentaho-button test-button" id="btn_showLdapUserTestDialog"> </button> *</div> *</div> *<br/> <br/> *<!-- roles configuration --> *<span class="ldapRolesTitle authMethod">Roles</span> *<br/> *<div> *<div class="ldapRoleBaseLabel authText">Role Attribute:</div> *<input class="ldapRoleBaseInput adminField" type="text" /> * *<div class="ldapRoleSearchFilterLabel authText">Role Search Filter:</div> *<input class="ldapRoleSearchFilterInput adminField" type="text" /> * *<div class="ldapRoleSearchBaseLabel authText">Role Search Base:</div> *<input class="ldapRoleSearchBaseInput adminField" type="text" /> * *<br/> <br/> * *<div class="securityConfigButton"> *<button class="pentaho-button test-button" id="btn_testAuthoritiesSearch"> </button> *</div> *</div> *<br/> <br/> *<span class="ldapPopulatorTitle authMethod">Populator</span> *<br/> *<div> *<div class="ldapPopulatorGroupRoleAttributeLabel authText">Group Role Attribute:</div> *<input class="ldapPopulatorGroupRoleAttributeInput adminField" type="text" /> * *<div class="ldapPopulatorGroupRoleSearchBaseLabel authText">Group Search Base:</div> *<input class="ldapPopulatorGroupRoleSearchBaseInput adminField" type="text" /> * *<div class="ldapPopulatorGroupSearchFilterLabel authText">Group Search Filter:</div> *<input class="ldapPopulatorGroupSearchFilterInput adminField" type="text" /> * *<div class="ldapPopulatorRolePrefixLabel authText">Role Prefix:</div> *<input class="ldapPopulatorRolePrefixInput adminField" type="text" /> * *<div class="ldapPopulatorUpperCaseLabel authText">Convert To Upper Case:</div> *<div class="ldapPopulatorUpperCaseDescription groupOption"> *<input name="ldapPopulatorUpperCaseInput" class="ldapPopulatorUpperCaseInput" type="radio" value="true" /> *<label class="yes-button">Yes</label> *<input name="ldapPopulatorUpperCaseInput" class="ldapPopulatorUpperCaseInput" type="radio" checked="checked" value="false" /> *<label class="no-button">No</label> *</div> *<div class="ldapPopulatorSubtreeLabel authText">Subtree:</div> *<div class="ldapPopulatorSubtreeDescription groupOption"> *<input name="ldapPopulatorSubtreeInput" class="ldapPopulatorSubtreeInput" type="radio" value="true" /> *<label class="yes-button">Yes</label> *<input name="ldapPopulatorSubtreeInput" class="ldapPopulatorSubtreeInput" type="radio" checked="checked" value="false" /> *<label class="no-button">No</label> *</div> *<br/> *<div class="securityConfigButton"> *<button class="pentaho-button test-button" id="btn_showPopulatorTestDialog"> </button> *</div> *</div> *</div> *</div> *</div> *</div> *</div> * * *<footer> *<br/><br/> *<div id="buttonDivSave" class="securityConfigButton" style="display: none;"> *<button id="saveConfigButton" class="pentaho-button" >Save</button> *</div> *</footer> *</body> *</html> * </pre> */ @Path("/{contextId}/{resourceId : .+}") @GET @Produces({ WILDCARD }) @StatusCodes({ @ResponseCode(code = 200, condition = "Successfully get the resource."), @ResponseCode(code = 404, condition = "Failed to find the resource.") }) public Response doGet(@PathParam("contextId") String contextId, @PathParam("resourceId") String resourceId) throws ObjectFactoryException, PluginBeanException, IOException, URISyntaxException { if (logger.isDebugEnabled()) { for (Object key : httpServletRequest.getParameterMap().keySet()) { logger.debug("param [" + key + "]"); //$NON-NLS-1$ //$NON-NLS-2$ } } return doService(contextId, resourceId); } /** * Retrieves the list of supported content type in the platform * * @return list of <code> ExecutableFileTypeDto </code> */ @Path("/executableTypes") @GET @Produces({ APPLICATION_XML, APPLICATION_JSON }) @Facet(name = "Unsupported") public Response getExecutableTypes() { ArrayList<ExecutableFileTypeDto> executableTypes = new ArrayList<ExecutableFileTypeDto>(); for (String contentType : pluginManager.getContentTypes()) { IContentInfo contentInfo = pluginManager.getContentTypeInfo(contentType); ExecutableFileTypeDto executableFileType = new ExecutableFileTypeDto(); executableFileType.setDescription(contentInfo.getDescription()); executableFileType.setExtension(contentInfo.getExtension()); executableFileType.setTitle(contentInfo.getTitle()); executableFileType.setCanSchedule(hasOperationId(contentInfo.getOperations(), "SCHEDULE_NEW")); executableFileType.setCanEdit(hasOperationId(contentInfo.getOperations(), "EDIT")); executableTypes.add(executableFileType); } final GenericEntity<List<ExecutableFileTypeDto>> entity = new GenericEntity<List<ExecutableFileTypeDto>>( executableTypes) { }; return Response.ok(entity).build(); } private boolean hasOperationId(final List<IPluginOperation> operations, final String operationId) { if (operations != null && StringUtils.isNotBlank(operationId)) { for (IPluginOperation operation : operations) { if (operation != null && StringUtils.isNotBlank(operation.getId())) { if (operation.getId().equals(operationId) && StringUtils.isNotBlank(operation.getPerspective())) { return true; } } } } return false; } protected Response doService(String contextId, String resourceId) throws ObjectFactoryException, PluginBeanException, IOException, URISyntaxException { ctxt("Is [{0}] a repository file id?", contextId); //$NON-NLS-1$ if (contextId.startsWith(":") || contextId.matches("^[A-z]\t:.*")) { //$NON-NLS-1$ // // The context is a repository file (A) // final RepositoryFile file = repository.getFile(FileResource.idToPath(contextId)); if (file == null) { logger.error(MessageFormat.format("Repository file [{0}] not found", contextId)); return Response.serverError().build(); } Response response = null; ctxt("Yep, [{0}] is a repository file id", contextId); //$NON-NLS-1$ final String ext = RepositoryFilenameUtils.getExtension(file.getName()); String pluginId = pluginManager.getPluginIdForType(ext); if (pluginId == null) { // A.3.a (faux content generator for .url files) response = getUrlResponse(file, resourceId); if (response != null) { return response; } else { logger.error(MessageFormat.format("No plugin was found to service content of type [{0}]", ext)); return Response.serverError().build(); } } // A.1. response = getPluginFileResponse(pluginId, resourceId); if (response != null) { return response; } // A.2. response = getRepositoryFileResponse(file.getPath(), resourceId); if (response != null) { return response; } // A.3.b (real content generator) CGFactory fac = new RepositoryFileCGFactory(resourceId, file); response = getContentGeneratorResponse(fac); if (response != null) { return response; } } else { ctxt("Nope, [{0}] is not a repository file id", contextId); //$NON-NLS-1$ ctxt("Is [{0}] is a repository file extension?", contextId); //$NON-NLS-1$ String pluginId = pluginManager.getPluginIdForType(contextId); if (pluginId != null) { // // The context is a file extension (B) // ctxt("Yep, [{0}] is a repository file extension", contextId); //$NON-NLS-1$ // B.1. Response response = getPluginFileResponse(pluginId, resourceId); if (response != null) { return response; } // B.3. CGFactory fac = new ContentTypeCGFactory(resourceId, contextId); response = getContentGeneratorResponse(fac); if (response != null) { return response; } } else { ctxt("Nope, [{0}] is not a repository file extension", contextId); //$NON-NLS-1$ ctxt("Is [{0}] is a plugin id?", contextId); //$NON-NLS-1$ if (pluginManager.getRegisteredPlugins().contains(contextId)) { // // The context is a plugin id (C) // ctxt("Yep, [{0}] is a plugin id", contextId); //$NON-NLS-1$ pluginId = contextId; // C.1. Response response = getPluginFileResponse(pluginId, resourceId); if (response != null) { return response; } // C.3. CGFactory fac = new DirectCGFactory(resourceId, contextId); response = getContentGeneratorResponse(fac); if (response != null) { return response; } } else { ctxt("Nope, [{0}] is not a plugin id", contextId); //$NON-NLS-1$ logger.warn(MessageFormat.format("Failed to resolve context [{0}]", contextId)); //$NON-NLS-1$ } } } logger.warn(MessageFormat.format("End of the resolution chain. No resource [{0}] found in context [{1}].", resourceId, contextId)); return Response.status(NOT_FOUND).build(); } abstract class CGFactory implements ContentGeneratorDescriptor { String contentGeneratorId; String command; public CGFactory(String contentGeneratorPath) { if (contentGeneratorPath.contains("/")) { //$NON-NLS-1$ contentGeneratorId = contentGeneratorPath.substring(0, contentGeneratorPath.indexOf('/')); command = contentGeneratorPath.substring(contentGeneratorPath.indexOf('/') + 1); debug("decomposing path [{0}] into content generator id [{1}] and command [{2}]", contentGeneratorPath, //$NON-NLS-1$ contentGeneratorId, command); } else { contentGeneratorId = contentGeneratorPath; } } public String getContentGeneratorId() { return contentGeneratorId; } public String getCommand() { return command; } abstract IContentGenerator create(); abstract GeneratorStreamingOutput getStreamingOutput(IContentGenerator cg); } class RepositoryFileCGFactory extends ContentTypeCGFactory { RepositoryFile file; public RepositoryFileCGFactory(String contentGeneratorPath, RepositoryFile file) { super(contentGeneratorPath, file.getName().substring(file.getName().lastIndexOf('.') + 1)); this.file = file; } @Override GeneratorStreamingOutput getStreamingOutput(IContentGenerator cg) { return new GeneratorStreamingOutput(cg, this, httpServletRequest, httpServletResponse, acceptableMediaTypes, file, command); } } class ContentTypeCGFactory extends CGFactory { String repoFileExt; public ContentTypeCGFactory(String contentGeneratorPath, String repoFileExt) { super(contentGeneratorPath); this.repoFileExt = repoFileExt; } @Override public IContentGenerator create() { return pluginManager.getContentGenerator(repoFileExt, contentGeneratorId); } @Override GeneratorStreamingOutput getStreamingOutput(IContentGenerator cg) { return new GeneratorStreamingOutput(cg, this, httpServletRequest, httpServletResponse, acceptableMediaTypes, null, command); } @Override public String getServicingFileType() { return repoFileExt; } @Override public String getPluginId() { return PentahoSystem.get(IPluginManager.class).getPluginIdForType(repoFileExt); } } class DirectCGFactory extends CGFactory { String pluginId; public DirectCGFactory(String contentGeneratorPath, String pluginId) { super(contentGeneratorPath); this.pluginId = pluginId; } @Override IContentGenerator create() { return pluginManager.getContentGenerator(null, contentGeneratorId); } @Override GeneratorStreamingOutput getStreamingOutput(IContentGenerator cg) { return new GeneratorStreamingOutput(cg, this, httpServletRequest, httpServletResponse, acceptableMediaTypes, null, command); } @Override public String getServicingFileType() { return null; } @Override public String getPluginId() { return pluginId; } } protected Response getUrlResponse(RepositoryFile file, String resourceId) throws MalformedURLException, URISyntaxException { String ext = file.getName().substring(file.getName().indexOf('.') + 1); if (!(ext.equals("url") && resourceId.equals("generatedContent"))) { return null; //$NON-NLS-1$ //$NON-NLS-2$ } String url = extractUrl(file); if (!url.trim().startsWith("http")) { //$NON-NLS-1$ // if path is relative, prepend FQSURL url = PentahoSystem.getApplicationContext().getFullyQualifiedServerURL() + url; } return Response.seeOther((new URL(url)).toURI()).build(); } protected Response getContentGeneratorResponse(CGFactory fac) { rsc("Is [{0}] a content generator ID?", fac.getContentGeneratorId()); //$NON-NLS-1$ final IContentGenerator contentGenerator; try { contentGenerator = fac.create(); } catch (NoSuchBeanDefinitionException e) { rsc("Nope, [{0}] is not a content generator ID.", fac.getContentGeneratorId()); //$NON-NLS-1$ return null; } if (contentGenerator == null) { rsc("Nope, [{0}] is not a content generator ID.", fac.getContentGeneratorId()); //$NON-NLS-1$ return null; } rsc("Yep, [{0}] is a content generator ID. Executing (where command path is {1})..", fac.getContentGeneratorId(), fac.getCommand()); //$NON-NLS-1$ GeneratorStreamingOutput gso = fac.getStreamingOutput(contentGenerator); return Response.ok(gso).build(); } protected Response getPluginFileResponse(String pluginId, String filePath) throws IOException { rsc("Is [{0}] a path to a plugin file?", filePath); //$NON-NLS-1$ if (pluginManager.isPublic(pluginId, filePath)) { PluginResource pluginResource = new PluginResource(httpServletResponse); Response readFileResponse = pluginResource.readFile(pluginId, filePath); // TODO: should we assume forbidden means move on in the resolution chain, or abort?? if (readFileResponse.getStatus() != Status.NOT_FOUND.getStatusCode()) { rsc("Yep, [{0}] is a path to a static plugin file", filePath); //$NON-NLS-1$ return readFileResponse; } } rsc("Nope, [{0}] is not a path to a static plugin file", filePath); //$NON-NLS-1$ return null; } protected Response getRepositoryFileResponse(String filePath, String relPath) throws IOException { rsc("Is [{0}] a relative path to a repository file, relative to [{1}]?", relPath, filePath); //$NON-NLS-1$ FileResource fileResource = new FileResource(httpServletResponse); fileResource.setWhitelist(whitelist); String path = RepositoryFilenameUtils .separatorsToRepository(RepositoryFilenameUtils.concat(filePath, "../" + relPath)); //$NON-NLS-1$ Response response = fileResource .doGetFileOrDir(RepositoryPathEncoder.encodeRepositoryPath(path).substring(1)); if (response.getStatus() != Status.NOT_FOUND.getStatusCode()) { rsc("Yep, [{0}] is a repository file", path); //$NON-NLS-1$ return response; } rsc("Nope, [{0}] is not a repository file", path); //$NON-NLS-1$ return null; } private void ctxt(String msg, Object... args) { debug("[RESOLVING CONTEXT ID] ==> " + msg, args); //$NON-NLS-1$ } private void rsc(String msg, Object... args) { debug("[RESOLVING RESOURCE ID] ==> " + msg, args); //$NON-NLS-1$ } private void debug(String msg, Object... args) { logger.debug(MessageFormat.format(msg, args)); } protected String extractUrl(RepositoryFile file) { SimpleRepositoryFileData data = null; data = repository.getDataForRead(file.getId(), SimpleRepositoryFileData.class); StringWriter writer = new StringWriter(); try { IOUtils.copy(data.getInputStream(), writer); } catch (IOException e) { return ""; //$NON-NLS-1$ } String props = writer.toString(); StringTokenizer tokenizer = new StringTokenizer(props, "\n"); //$NON-NLS-1$ while (tokenizer.hasMoreTokens()) { String line = tokenizer.nextToken(); int pos = line.indexOf('='); if (pos > 0) { String propname = line.substring(0, pos); String value = line.substring(pos + 1); if ((value != null) && (value.length() > 0) && (value.charAt(value.length() - 1) == '\r')) { value = value.substring(0, value.length() - 1); } if ("URL".equalsIgnoreCase(propname)) { //$NON-NLS-1$ return value; } } } // No URL found return ""; //$NON-NLS-1$ } public RepositoryDownloadWhitelist getWhitelist() { return whitelist; } public void setWhitelist(RepositoryDownloadWhitelist whitelist) { this.whitelist = whitelist; } }