/* license-start * * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 3. * * 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 General Public License for more details, at <http://www.gnu.org/licenses/>. * * Contributors: * Crispico - Initial API and implementation * * license-end */ /******************************************************************************* * Copyright (c) 2005-2008 Cognos Incorporated, IBM Corporation and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Cognos Incorporated - initial API and implementation * IBM Corporation - bug fixes and enhancements *******************************************************************************/ package org.eclipse.equinox.servletbridge; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; /** * The BridgeServlet provides a means to bridge the servlet and OSGi * runtimes. This class has 3 main responsibilities: * 1) Control the lifecycle of the associated FrameworkLauncher in line with its own lifecycle * 2) Provide a servlet "hook" that allows all servlet requests to be delegated to the registered servlet * 3) Provide means to manually control the framework lifecycle */ public class BridgeServlet extends HttpServlet { static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri"; //$NON-NLS-1$ static final String INCLUDE_SERVLET_PATH_ATTRIBUTE = "javax.servlet.include.servlet_path"; //$NON-NLS-1$ static final String INCLUDE_PATH_INFO_ATTRIBUTE = "javax.servlet.include.path_info"; //$NON-NLS-1$ private static final long serialVersionUID = 2825667412474494674L; private static BridgeServlet instance; private HttpServlet delegate; private FrameworkLauncher framework; private int delegateReferenceCount; private boolean enableFrameworkControls; /** * init() is called by the Servlet Container and used to instantiate the frameworkLauncher which MUST be an instance of FrameworkLauncher. * After instantiating the framework init, deploy, and start are called. */ public void init() throws ServletException { super.init(); String enableFrameworkControlsParameter = getServletConfig().getInitParameter("enableFrameworkControls"); //$NON-NLS-1$ enableFrameworkControls = (enableFrameworkControlsParameter != null && enableFrameworkControlsParameter.equals("true")); //$NON-NLS-1$ String frameworkLauncherClassParameter = getServletConfig().getInitParameter("frameworkLauncherClass"); //$NON-NLS-1$ if (frameworkLauncherClassParameter != null) { try { Class frameworkLauncherClass = this.getClass().getClassLoader().loadClass(frameworkLauncherClassParameter); framework = (FrameworkLauncher) frameworkLauncherClass.newInstance(); } catch (Exception e) { throw new ServletException(e); } } else { framework = new FrameworkLauncher(); } boolean frameworkStarted = false; setInstance(this); try { framework.init(getServletConfig()); framework.deploy(); framework.start(); frameworkStarted = true; } finally { if (!frameworkStarted) setInstance(null); } } /** * destroy() is called by the Servlet Container and used to first stop and then destroy the framework. */ public void destroy() { framework.stop(); framework.destroy(); setInstance(null); super.destroy(); } /** * service is called by the Servlet Container and will first determine if the request is a * framework control and will otherwise try to delegate to the registered servlet delegate * */ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String pathInfo = req.getPathInfo(); // Check if this is being handled by an extension mapping if (pathInfo == null && isExtensionMapping(req.getServletPath())) req = new ExtensionMappingRequest(req); if (req.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) == null) { if (enableFrameworkControls) { if (pathInfo != null && pathInfo.startsWith("/sp_")) { //$NON-NLS-1$ if (serviceFrameworkControls(req, resp)) { return; } } } } else { String includePathInfo = (String) req.getAttribute(INCLUDE_PATH_INFO_ATTRIBUTE); // Check if this is being handled by an extension mapping if (includePathInfo == null || includePathInfo.length() == 0) { String servletPath = (String) req.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE); if (isExtensionMapping(servletPath)) req = new IncludedExtensionMappingRequest(req); } } ClassLoader original = Thread.currentThread().getContextClassLoader(); HttpServlet servletReference = acquireDelegateReference(); if (servletReference == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "BridgeServlet: " + req.getRequestURI()); //$NON-NLS-1$ return; } try { Thread.currentThread().setContextClassLoader(framework.getFrameworkContextClassLoader()); servletReference.service(req, resp); } finally { releaseDelegateReference(); Thread.currentThread().setContextClassLoader(original); } } private boolean isExtensionMapping(String servletPath) { if (servletPath == null) return false; String lastSegment = servletPath; int lastSlash = servletPath.lastIndexOf('/'); if (lastSlash != -1) lastSegment = servletPath.substring(lastSlash + 1); return lastSegment.indexOf('.') != -1; } /** * serviceFrameworkControls currently supports the following commands (identified by the request's pathinfo) * sp_deploy - Copies the contents of /platform to the install area * sp_undeploy - Removes the copy of Eclipse from the install area * sp_redeploy - Resets the platform (e.g. stops, undeploys, deploys, starts) * sp_start - Starts a deployed platform * sp_stop - Stops the platform */ private boolean serviceFrameworkControls(HttpServletRequest req, HttpServletResponse resp) throws IOException { String pathInfo = req.getPathInfo(); if (pathInfo.equals("/sp_start")) { //$NON-NLS-1$ framework.start(); resp.getWriter().write("Platform Started"); //$NON-NLS-1$ return true; } else if (pathInfo.equals("/sp_stop")) { //$NON-NLS-1$ framework.stop(); resp.getWriter().write("Platform Stopped"); //$NON-NLS-1$ return true; } else if (pathInfo.equals("/sp_deploy")) { //$NON-NLS-1$ framework.deploy(); resp.getWriter().write("Platform Deployed"); //$NON-NLS-1$ return true; } else if (pathInfo.equals("/sp_undeploy")) { //$NON-NLS-1$ framework.undeploy(); resp.getWriter().write("Platform Undeployed"); //$NON-NLS-1$ return true; } else if (pathInfo.equals("/sp_reset")) { //$NON-NLS-1$ framework.stop(); framework.start(); resp.getWriter().write("Platform Reset"); //$NON-NLS-1$ return true; } else if (pathInfo.equals("/sp_redeploy")) { //$NON-NLS-1$ framework.stop(); framework.undeploy(); framework.deploy(); framework.start(); resp.getWriter().write("Platform Redeployed"); //$NON-NLS-1$ return true; } else if (pathInfo.equals("/sp_test")) { //$NON-NLS-1$ if (delegate == null) resp.getWriter().write("Servlet delegate not registered."); //$NON-NLS-1$ else resp.getWriter().write("Servlet delegate registered - " + delegate.getClass().getName()); //$NON-NLS-1$ return true; } return false; } private static synchronized void setInstance(BridgeServlet servlet) { if ((instance != null) && (servlet != null)) throw new IllegalStateException("instance already set"); //$NON-NLS-1$ instance = servlet; } private synchronized void releaseDelegateReference() { --delegateReferenceCount; notifyAll(); } private synchronized HttpServlet acquireDelegateReference() { if (delegate != null) ++delegateReferenceCount; return delegate; } /** * registerServletDelegate is the hook method called from inside the OSGi runtime to register * a servlet for which all future servlet calls will be delegated. If not null and no delegate * is currently registered, init(ServletConfig) will be called on the servletDelegate before * returning. * @param servletDelegate - the servlet to register for delegation */ public static synchronized void registerServletDelegate(HttpServlet servletDelegate) { if (instance == null) { // shutdown already return; } if (servletDelegate == null) throw new NullPointerException("cannot register a null servlet delegate"); //$NON-NLS-1$ synchronized (instance) { if (instance.delegate != null) throw new IllegalStateException("A Servlet Proxy is already registered"); //$NON-NLS-1$ try { servletDelegate.init(instance.getServletConfig()); } catch (ServletException e) { instance.getServletContext().log("Error initializing servlet delegate", e); //$NON-NLS-1$ return; } instance.delegate = servletDelegate; } } /** * unregisterServletDelegate is the hook method called from inside the OSGi runtime to unregister a delegate. * If the servletDelegate matches the current registered delegate destroy() is called on the servletDelegate. * destroy() will not be called until the delegate is finished servicing any previous requests. * @param servletDelegate - the servlet to unregister */ public static synchronized void unregisterServletDelegate(HttpServlet servletDelegate) { if (instance == null) { // shutdown already return; } synchronized (instance) { if (instance.delegate == null) throw new IllegalStateException("No servlet delegate is registered"); //$NON-NLS-1$ if (instance.delegate != servletDelegate) throw new IllegalStateException("Servlet delegate does not match registered servlet delegate"); //$NON-NLS-1$ HttpServlet oldProxy = instance.delegate; instance.delegate = null; while (instance.delegateReferenceCount != 0) { try { instance.wait(); } catch (InterruptedException e) { // keep waiting for all requests to finish } } oldProxy.destroy(); } } static class ExtensionMappingRequest extends HttpServletRequestWrapper { public ExtensionMappingRequest(HttpServletRequest req) { super(req); } public String getPathInfo() { return super.getServletPath(); } public String getServletPath() { return ""; //$NON-NLS-1$ } } static class IncludedExtensionMappingRequest extends HttpServletRequestWrapper { public IncludedExtensionMappingRequest(HttpServletRequest req) { super(req); } public Object getAttribute(String attributeName) { if (attributeName.equals(INCLUDE_SERVLET_PATH_ATTRIBUTE)) { return ""; //$NON-NLS-1$ } else if (attributeName.equals(INCLUDE_PATH_INFO_ATTRIBUTE)) { String servletPath = (String) super.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE); return servletPath; } return super.getAttribute(attributeName); } } }