/*!
* 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.servlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IPluginResourceLoader;
import org.pentaho.platform.api.engine.IServiceManager;
import org.pentaho.platform.api.engine.ServiceException;
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.plugin.services.pluginmgr.PluginUtil;
import org.pentaho.platform.web.servlet.messages.Messages;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@SuppressWarnings( "serial" )
/**
* This is the plugin variant of the GwtRpcProxyServlet. This servlet routes incoming GWT RPC
* requests to POJOs found in a plugin lib.
*/
public class GwtRpcPluginProxyServlet extends AbstractGwtRpcProxyServlet {
private static final Log logger = LogFactory.getLog( GwtRpcPluginProxyServlet.class );
private static final Pattern pentahoBasePattern = Pattern.compile( "^/.*/WEBAPP_ROOT/" ); //$NON-NLS-1$
@Override
protected Object resolveDispatchTarget( String servletContextPath ) {
IServiceManager serviceManager = PentahoSystem.get( IServiceManager.class, PentahoSessionHolder.getSession() );
String key = getDispatchKey();
if ( null == serviceManager.getServiceConfig( "gwt", key ) ) { //$NON-NLS-1$
String errMsg =
Messages.getInstance().getErrorString( "GwtRpcPluginProxyServlet.ERROR_0001_SERVICE_NOT_FOUND", key ); //$NON-NLS-1$
logger.error( errMsg );
throw new GwtRpcProxyException( errMsg );
}
Object targetBean = null;
try {
targetBean = serviceManager.getServiceBean( "gwt", key ); //$NON-NLS-1$
} catch ( ServiceException e ) {
throw new GwtRpcProxyException( Messages.getInstance().getErrorString(
"GwtRpcPluginProxyServlet.ERROR_0002_FAILED_TO_GET_BEAN_REFERENCE", key ), e ); //$NON-NLS-1$
}
return targetBean;
}
/**
* The request path is broken up for processing like so: Ex: given a request for serialization file at
* '/pentaho/content/data-access/resources/gwt/{strongName}'
*
* @param serializationPolicyFilename
* the name of the serialization policy file that GWT is looking for
* @param appContextPath
* according to the example url, this would be '/pentaho'
* @param servletContextPath
* according the the example url, this would be '/content/data-access/resources/gwt/{strongName}'
* @return a URL to the serialization policy file, or <code>null</code> if none applies, in which case the default
* serialization policy will apply
*/
protected URL getSerializationPolicyUrl( String serializationPolicyFilename, String appContextPath,
String servletContextPath ) {
// We will use the pluginContextPath to determine the service plugin for the serialization policy file
//
ClassLoader serviceClassloader = PluginUtil.getClassLoaderForService( servletContextPath );
if ( serviceClassloader == null ) {
// if we get here, then the service is not supplied by a plugin and thus we cannot hope to find
// the appropriate serialization policy
logger.error( Messages.getInstance().getErrorString(
"GwtRpcPluginProxyServlet.ERROR_0005_FAILED_TO_FIND_PLUGIN", appContextPath ) ); //$NON-NLS-1$
}
// We know what plugin is supposed to have the serialization policy file, now go find it
// in the plugin's filesystem
//
IPluginResourceLoader resLoader =
PentahoSystem.get( IPluginResourceLoader.class, PentahoSessionHolder.getSession() );
List<URL> urls = resLoader.findResources( serviceClassloader, serializationPolicyFilename );
if ( urls.size() < 1 ) {
logger.error( Messages.getInstance().getErrorString(
"GwtRpcPluginProxyServlet.ERROR_0006_FAILED_TO_FIND_FILE", serializationPolicyFilename ) ); //$NON-NLS-1$
return null;
}
if ( urls.size() > 1 ) {
logger.warn( Messages.getInstance().getString(
"GwtRpcPluginProxyServlet.WARN_MULTIPLE_RESOURCES_FOUND", serializationPolicyFilename ) ); //$NON-NLS-1$
}
return urls.get( 0 );
}
@Override
protected SerializationPolicy doGetSerializationPolicy( HttpServletRequest request, String moduleBaseURL,
String strongName ) {
/*
* The request path is broken up for processing like so: Ex: given a request for serialization file at
* '/pentaho/content/data-access/resources/gwt/{strongName}' * modulePath = ????
* '/pentaho/content/data-access/resources/gwt/{strongName}' * appContextPath = '/pentaho' * servletContextPath =
* '/content/data-access/resources/gwt/{strongName}' * pluginContextPath = '/data-access/resources/gwt/{strongName}'
*/
SerializationPolicy serializationPolicy = null;
String appContextPath = request.getContextPath();
String modulePath = null;
if ( moduleBaseURL != null ) {
try {
modulePath = new URL( moduleBaseURL ).getPath();
} catch ( MalformedURLException ex ) {
logger.error( Messages.getInstance().getErrorString(
"GwtRpcPluginProxyServlet.ERROR_0004_MALFORMED_URL", moduleBaseURL ), ex ); //$NON-NLS-1$
// cannot proceed, default serialization policy will apply
return null;
}
}
if ( modulePath.contains( "WEBAPP_ROOT" ) ) {
modulePath = scrubWebAppRoot( modulePath );
}
String servletContextPath = modulePath.substring( appContextPath.length() );
// Special logic to use a spring defined SerializationPolicy for a plugin.
String pluginId = PluginUtil.getPluginIdFromPath( servletContextPath );
serializationPolicy =
PentahoSystem.get( SerializationPolicy.class, PentahoSessionHolder.getSession(), Collections.singletonMap(
"plugin", pluginId ) );
if ( serializationPolicy != null ) {
return serializationPolicy;
}
String serializationPolicyFilename = SerializationPolicyLoader.getSerializationPolicyFileName( strongName );
URL serializationPolicyUrl =
getSerializationPolicyUrl( serializationPolicyFilename, appContextPath, servletContextPath );
if ( serializationPolicyUrl == null ) {
// default serialization policy will apply
return null;
}
InputStream rpcFileInputStream = null;
try {
rpcFileInputStream = serializationPolicyUrl.openStream();
if ( rpcFileInputStream != null ) {
serializationPolicy = SerializationPolicyLoader.loadFromStream( rpcFileInputStream, null );
}
} catch ( IOException e ) {
logger.error( Messages.getInstance().getErrorString(
"GwtRpcPluginProxyServlet.ERROR_0007_FAILED_TO_OPEN_FILE", serializationPolicyFilename ), e ); //$NON-NLS-1$
} catch ( ParseException e ) {
logger.error( Messages.getInstance().getErrorString(
"GwtRpcPluginProxyServlet.ERROR_0008_FAILED_TO_PARSE_FILE", serializationPolicyFilename ), e ); //$NON-NLS-1$
} finally {
if ( rpcFileInputStream != null ) {
try {
rpcFileInputStream.close();
} catch ( IOException e ) { // do nothing }
}
}
}
// if null, the default serialization policy will apply
// Note: caching is handled by the parent class
return serializationPolicy;
}
protected String scrubWebAppRoot( String modulePath ) {
Matcher matcher = pentahoBasePattern.matcher( modulePath );
if ( matcher.find() ) {
String garbagePathPart = matcher.group();
int idx = modulePath.indexOf( garbagePathPart );
String contextPath = PentahoRequestContextHolder.getRequestContext().getContextPath();
final String rewrittenModulePath =
new StringBuffer( modulePath ).replace( idx, idx + garbagePathPart.length(), contextPath ).toString();
return rewrittenModulePath;
}
return modulePath;
}
}