blob: a1022e212feddb402a0ac8f204585a3eb07ee9f7 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}
}