/* * 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 2009 Pentaho Corporation. All rights reserved. * * Created May 21, 2009 * @author Aaron Phillips */ package org.pentaho.platform.web.servlet; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; 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.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 com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException; import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.server.rpc.RPC; import com.google.gwt.user.server.rpc.RPCRequest; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.google.gwt.user.server.rpc.SerializationPolicy; import com.google.gwt.user.server.rpc.SerializationPolicyLoader; import com.google.gwt.user.server.rpc.impl.StandardSerializationPolicy; public class GwtRpcPluginProxyServlet extends RemoteServiceServlet { private static final long serialVersionUID = -7652708468110168124L; private static final Log logger = LogFactory.getLog(GwtRpcPluginProxyServlet.class); private static final ThreadLocal<Object> perThreadTargetBean = new ThreadLocal<Object>(); /** * Returns the dispatch key for this request. This name is the part of the request path * beyond the servlet base path. I.e. if the GwtRpcPluginProxyServlet is mapped to the "/gwtrpc" * context in web.xml, then this method will return "testservice" given a request to * "http://localhost:8080/pentaho/gwtrpc/testservice". * @return the part of the request url used to dispatch the request */ public String getDispatchKey() { HttpServletRequest request = getThreadLocalRequest(); //path info will give us what we want with String requestPathInfo = request.getPathInfo(); if (requestPathInfo.startsWith("/")) { //$NON-NLS-1$ requestPathInfo = requestPathInfo.substring(1); } return requestPathInfo; } @Override public String processCall(String payload) throws SerializationException { Map<Class<?>, Boolean> whitelist = new HashMap<Class<?>, Boolean>(); whitelist.put(ServiceException.class, Boolean.TRUE); StandardSerializationPolicy policy = new StandardSerializationPolicy(whitelist); IServiceManager serviceManager = PentahoSystem.get(IServiceManager.class, PentahoSessionHolder.getSession()); String serviceId = getDispatchKey(); if (null == serviceManager.getServiceConfig("gwt", serviceId)) { //$NON-NLS-1$ String errMsg = Messages.getInstance().getErrorString("GwtRpcPluginProxyServlet.ERROR_0001_SERVICE_NOT_FOUND", serviceId); //$NON-NLS-1$ logger.error(errMsg); return RPC.encodeResponseForFailure(null, new ServiceException(errMsg), policy); } Object targetBean = null; try { targetBean = serviceManager.getServiceBean("gwt", serviceId); //$NON-NLS-1$ } catch (ServiceException e) { logger.error( Messages.getInstance().getErrorString("GwtRpcPluginProxyServlet.ERROR_0002_FAILED_TO_GET_BEAN_REFERENCE", serviceId), e); //$NON-NLS-1$ return RPC.encodeResponseForFailure(null, e, policy); } perThreadTargetBean.set(targetBean); ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); try { //temporarily swap out the context classloader to the plugin's classloader, //so the RPC class can do a Class.forName and find the service class specified //in the request Thread.currentThread().setContextClassLoader(targetBean.getClass().getClassLoader()); RPCRequest rpcRequest = RPC.decodeRequest(payload, null, this); onAfterRequestDeserialized(rpcRequest); // don't require the server side to implement the service interface Method method = rpcRequest.getMethod(); try { Method m = targetBean.getClass().getMethod(method.getName(), method.getParameterTypes()); if (m != null) { method = m; } } catch (Exception e) { e.printStackTrace(); } return RPC.invokeAndEncodeResponse(targetBean, method, rpcRequest.getParameters(), rpcRequest .getSerializationPolicy()); } catch (IncompatibleRemoteServiceException ex) { logger.error(Messages.getInstance().getErrorString( "GwtRpcPluginProxyServlet.ERROR_0003_RPC_INVOCATION_FAILED", targetBean.getClass().getName()), ex); //$NON-NLS-1$ return RPC.encodeResponseForFailure(null, ex); } finally { //reset the context classloader if (origLoader != null) { Thread.currentThread().setContextClassLoader(origLoader); } } } @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}' * * * 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; } } String servletContextPath = modulePath.substring(appContextPath.length()); //We will use the pluginContextPath to determine the service plugin for the serialization policy file // String pluginServiceContextPath = servletContextPath.substring(servletContextPath.indexOf('/', 1)); ClassLoader serviceClassloader = PluginUtil.getClassLoaderForService(pluginServiceContextPath); 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$ } String serializationPolicyFile = SerializationPolicyLoader.getSerializationPolicyFileName(strongName); InputStream rpcFileInputStream = null; //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, serializationPolicyFile); if (urls.size() < 1) { logger.error(Messages.getInstance().getErrorString( "GwtRpcPluginProxyServlet.ERROR_0006_FAILED_TO_FIND_FILE", serializationPolicyFile)); //$NON-NLS-1$ } else if (urls.size() > 1) { logger .warn(Messages.getInstance().getString("GwtRpcPluginProxyServlet.WARN_MULTIPLE_RESOURCES_FOUND", serializationPolicyFile)); //$NON-NLS-1$ } else { try { rpcFileInputStream = urls.get(0).openStream(); if (rpcFileInputStream != null) { serializationPolicy = SerializationPolicyLoader.loadFromStream(rpcFileInputStream, null); } } catch (IOException e) { logger.error(Messages.getInstance().getErrorString( "GwtRpcPluginProxyServlet.ERROR_0007_FAILED_TO_OPEN_FILE", serializationPolicyFile), e); //$NON-NLS-1$ } catch (ParseException e) { logger.error(Messages.getInstance().getErrorString( "GwtRpcPluginProxyServlet.ERROR_0008_FAILED_TO_PARSE_FILE", serializationPolicyFile), 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; } }