/* * 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 2005 - 2009 Pentaho Corporation. All rights reserved. * */ package org.pentaho.platform.web.servlet; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.platform.api.engine.ICacheManager; import org.pentaho.platform.api.engine.IContentGenerator; import org.pentaho.platform.api.engine.IMessageFormatter; import org.pentaho.platform.api.engine.IMimeTypeListener; import org.pentaho.platform.api.engine.IOutputHandler; import org.pentaho.platform.api.engine.IParameterProvider; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IPluginManager; import org.pentaho.platform.engine.core.solution.SimpleParameterProvider; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.util.messages.LocaleHelper; import org.pentaho.platform.util.web.MimeHelper; import org.pentaho.platform.util.web.SimpleUrlFactory; import org.pentaho.platform.web.http.HttpOutputHandler; import org.pentaho.platform.web.http.PentahoHttpSessionHelper; import org.pentaho.platform.web.http.request.HttpRequestParameterProvider; import org.pentaho.platform.web.http.session.HttpSessionParameterProvider; import org.pentaho.platform.web.servlet.messages.Messages; public class GenericServlet extends ServletBase { private static final long serialVersionUID = 6713118348911206464L; private static final Log logger = LogFactory.getLog(GenericServlet.class); // private String settingsPath = ISolutionRepository.SEPARATOR + "settings.xml"; //$NON-NLS-1$ private static final String CACHE_FILE = "file"; //$NON-NLS-1$ private static ICacheManager cache = PentahoSystem.getCacheManager(null); static { cache.addCacheRegion(CACHE_FILE); } @Override public Log getLogger() { return GenericServlet.logger; } @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { PentahoSystem.systemEntryPoint(); IOutputHandler outputHandler = null; // BISERVER-2767 - grabbing the current class loader so we can replace it at the end ClassLoader origContextClassloader = Thread.currentThread().getContextClassLoader(); try { InputStream in = request.getInputStream(); String servletPath = request.getServletPath(); String pathInfo = request.getPathInfo(); String contentGeneratorId = ""; //$NON-NLS-1$ String urlPath = ""; //$NON-NLS-1$ SimpleParameterProvider pathParams = new SimpleParameterProvider(); if (pathInfo == null) { contentGeneratorId = servletPath.substring(1); urlPath = contentGeneratorId; } else { String path = pathInfo.substring(1); int slashPos = path.indexOf('/'); if (slashPos != -1) { pathParams.setParameter("path", pathInfo.substring(slashPos + 1)); //$NON-NLS-1$ contentGeneratorId = path.substring(0, slashPos); } else { contentGeneratorId = path; } urlPath = "content/" + contentGeneratorId; //$NON-NLS-1$ } pathParams.setParameter("query", request.getQueryString()); //$NON-NLS-1$ pathParams.setParameter("contentType", request.getContentType()); //$NON-NLS-1$ pathParams.setParameter("inputstream", in); //$NON-NLS-1$ pathParams.setParameter("httpresponse", response); //$NON-NLS-1$ pathParams.setParameter("httprequest", request); //$NON-NLS-1$ pathParams.setParameter("remoteaddr", request.getRemoteAddr()); //$NON-NLS-1$ if (PentahoSystem.debug) { debug("GenericServlet contentGeneratorId=" + contentGeneratorId); //$NON-NLS-1$ debug("GenericServlet urlPath=" + urlPath); //$NON-NLS-1$ } IPentahoSession session = getPentahoSession(request); IPluginManager pluginManager = PentahoSystem.get(IPluginManager.class, session); if (pluginManager == null) { OutputStream out = response.getOutputStream(); String message = Messages.getInstance().getErrorString("GenericServlet.ERROR_0001_BAD_OBJECT", IPluginManager.class.getSimpleName()); //$NON-NLS-1$ error(message); out.write(message.getBytes()); return; } // TODO make doing the HTTP headers configurable per content generator SimpleParameterProvider headerParams = new SimpleParameterProvider(); Enumeration names = request.getHeaderNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); String value = request.getHeader(name); headerParams.setParameter(name, value); } if (pathInfo != null) { String pluginId = pluginManager.getServicePlugin(pathInfo); if (pluginId != null && pluginManager.isStaticResource(pathInfo)) { boolean cacheOn = "true".equals(pluginManager.getPluginSetting(pluginId, "settings/cache", "false")); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ String maxAge = (String)pluginManager.getPluginSetting(pluginId, "settings/max-age", null); //$NON-NLS-1$ allowBrowserCache(maxAge, pathParams); String mimeType = MimeHelper.getMimeTypeFromFileName(pathInfo); response.setContentType(mimeType); OutputStream out = response.getOutputStream(); // do we have this resource cached? ByteArrayOutputStream byteStream = null; if (cacheOn) { byteStream = (ByteArrayOutputStream) cache.getFromRegionCache(CACHE_FILE, pathInfo); } if (byteStream != null) { IOUtils.write(byteStream.toByteArray(), out); return; } InputStream resourceStream = pluginManager.getStaticResource(pathInfo); if (resourceStream != null) { try { byteStream = new ByteArrayOutputStream(); IOUtils.copy(resourceStream, byteStream); // if cache is enabled, drop file in cache if (cacheOn) { cache.putInRegionCache(CACHE_FILE, pathInfo, byteStream); } // write it out IOUtils.write(byteStream.toByteArray(), out); return; } finally { IOUtils.closeQuietly(resourceStream); } } logger.error(Messages.getInstance().getErrorString("GenericServlet.ERROR_0004_RESOURCE_NOT_FOUND", pluginId, pathInfo)); //$NON-NLS-1$ response.sendError(404); return; } } IContentGenerator contentGenerator = pluginManager.getContentGenerator(contentGeneratorId, session); if (contentGenerator == null) { OutputStream out = response.getOutputStream(); String message = Messages.getInstance().getErrorString("GenericServlet.ERROR_0002_BAD_GENERATOR", contentGeneratorId); //$NON-NLS-1$ error(message); out.write(message.getBytes()); return; } // set the classloader of the current thread to the class loader of // the plugin so that it can load its libraries Thread.currentThread().setContextClassLoader(contentGenerator.getClass().getClassLoader()); // String proxyClass = PentahoSystem.getSystemSetting( module+"/plugin.xml" , "plugin/content-generators/"+contentGeneratorId, // "content generator not found"); IParameterProvider requestParameters = new HttpRequestParameterProvider(request); // see if this is an upload // File uploading is a service provided by UploadFileServlet where appropriate protections // are in place to prevent uploads that are too large. // boolean isMultipart = ServletFileUpload.isMultipartContent(request); // if (isMultipart) { // requestParameters = new SimpleParameterProvider(); // // Create a factory for disk-based file items // FileItemFactory factory = new DiskFileItemFactory(); // // // Create a new file upload handler // ServletFileUpload upload = new ServletFileUpload(factory); // // // Parse the request // List<?> /* FileItem */items = upload.parseRequest(request); // Iterator<?> iter = items.iterator(); // while (iter.hasNext()) { // FileItem item = (FileItem) iter.next(); // // if (item.isFormField()) { // ((SimpleParameterProvider) requestParameters).setParameter(item.getFieldName(), item.getString()); // } else { // String name = item.getName(); // ((SimpleParameterProvider) requestParameters).setParameter(name, item.getInputStream()); // } // } // } response.setCharacterEncoding(LocaleHelper.getSystemEncoding()); IMimeTypeListener listener = new HttpMimeTypeListener(request, response); outputHandler = getOutputHandler(response, true); outputHandler.setMimeTypeListener(listener); IParameterProvider sessionParameters = new HttpSessionParameterProvider(session); Map<String, IParameterProvider> parameterProviders = new HashMap<String, IParameterProvider>(); parameterProviders.put(IParameterProvider.SCOPE_REQUEST, requestParameters); parameterProviders.put(IParameterProvider.SCOPE_SESSION, sessionParameters); parameterProviders.put("headers", headerParams); //$NON-NLS-1$ parameterProviders.put("path", pathParams); //$NON-NLS-1$ SimpleUrlFactory urlFactory = new SimpleUrlFactory(PentahoSystem.getApplicationContext().getBaseUrl() + urlPath + "?"); //$NON-NLS-1$ List<String> messages = new ArrayList<String>(); contentGenerator.setOutputHandler(outputHandler); contentGenerator.setMessagesList(messages); contentGenerator.setParameterProviders(parameterProviders); contentGenerator.setSession(session); contentGenerator.setUrlFactory(urlFactory); // String contentType = request.getContentType(); // SimpleStreamSource input = new SimpleStreamSource( "input", contentType, in, null ); //$NON-NLS-1$ // contentGenerator.setInput(input); contentGenerator.createContent(); if (PentahoSystem.debug) { debug("Generic Servlet content generate successfully"); //$NON-NLS-1$ } } catch (Exception e) { StringBuffer buffer = new StringBuffer(); error(Messages.getInstance().getErrorString("GenericServlet.ERROR_0002_BAD_GENERATOR", request.getQueryString()), e); //$NON-NLS-1$ List errorList = new ArrayList(); String msg = e.getMessage(); errorList.add(msg); PentahoSystem.get(IMessageFormatter.class, PentahoHttpSessionHelper.getPentahoSession(request)).formatFailureMessage( "text/html", null, buffer, errorList); //$NON-NLS-1$ response.getOutputStream().write(buffer.toString().getBytes(LocaleHelper.getSystemEncoding())); } finally { // reset the classloader of the current thread Thread.currentThread().setContextClassLoader(origContextClassloader); PentahoSystem.systemExitPoint(); } } protected void allowBrowserCache(String maxAge, IParameterProvider pathParams) { if (maxAge == null || "0".equals(maxAge)) { //$NON-NLS-1$ return; } if (maxAge != null) { HttpServletResponse response = (HttpServletResponse) pathParams.getParameter("httpresponse"); //$NON-NLS-1$ if (response != null) { response.setHeader("Cache-Control", "max-age=" + maxAge); //$NON-NLS-1$ //$NON-NLS-2$ } } } protected IOutputHandler getOutputHandler(HttpServletResponse response, boolean allowFeedback) throws IOException { OutputStream out = response.getOutputStream(); HttpOutputHandler handler = new HttpOutputHandler(response, out, allowFeedback); return handler; } }