diff --git a/org.adempiere.eclipse.equinox.http.servlet/.classpath b/org.adempiere.eclipse.equinox.http.servlet/.classpath
new file mode 100644
index 0000000000..ad32c83a78
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/org.adempiere.eclipse.equinox.http.servlet/.project b/org.adempiere.eclipse.equinox.http.servlet/.project
new file mode 100644
index 0000000000..a8ef04aac0
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/.project
@@ -0,0 +1,28 @@
+
+
+ org.adempiere.eclipse.equinox.http.servlet
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.jdt.core.prefs b/org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..7169546ef0
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Fri Dec 24 14:42:06 MYT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.pde.core.prefs b/org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000000..4ccb207004
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,4 @@
+#Fri Dec 24 14:42:06 MYT 2010
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/org.adempiere.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF b/org.adempiere.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..a76b0bf5ca
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0,J2SE-1.3
+Bundle-SymbolicName: org.adempiere.eclipse.equinox.http.servlet
+Eclipse-LazyStart: true
+Eclipse-SourceReferences: scm:cvs:pserver:dev.eclipse.org:/cvsroot/rt:
+ org.eclipse.equinox/compendium/bundles/org.eclipse.equinox.http.servl
+ et;tag=v20100503
+Bundle-Activator: org.eclipse.equinox.http.servlet.internal.Activator
+Export-Package: org.eclipse.equinox.http.servlet;version="1.1.0"
+Bundle-Version: 1.1.0.v20100503
+Bundle-ActivationPolicy: lazy
+Bundle-Vendor: %providerName
+Bundle-Name: %bundleName
+Comment-Header: Both Eclipse-LazyStart and Bundle-ActivationPolicy are
+ specified for compatibility with 3.2
+Import-Package: javax.servlet;version="2.3",javax.servlet.http;version
+ ="2.3",org.osgi.framework;version="1.3.0",org.osgi.service.http;versi
+ on="1.2.0"
+Bundle-ManifestVersion: 2
diff --git a/org.adempiere.eclipse.equinox.http.servlet/about.html b/org.adempiere.eclipse.equinox.http.servlet/about.html
new file mode 100644
index 0000000000..d7e1cdf1e2
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/about.html
@@ -0,0 +1,28 @@
+
+
+
+
+About
+
+
+About This Content
+
+January 30, 2007
+License
+
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at http://www.eclipse.org/legal/epl-v10.html.
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
+
\ No newline at end of file
diff --git a/org.adempiere.eclipse.equinox.http.servlet/build.properties b/org.adempiere.eclipse.equinox.http.servlet/build.properties
new file mode 100644
index 0000000000..34d2e4d2da
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/org.adempiere.eclipse.equinox.http.servlet/plugin.properties b/org.adempiere.eclipse.equinox.http.servlet/plugin.properties
new file mode 100644
index 0000000000..1c93bb6abc
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/plugin.properties
@@ -0,0 +1,4 @@
+##Source Bundle Localization
+#Thu Jun 24 08:34:29 EDT 2010
+bundleName=Http Services Servlet Source
+providerName=Eclipse.org - Equinox
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/ExtendedHttpService.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/ExtendedHttpService.java
new file mode 100644
index 0000000000..d270f7e163
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/ExtendedHttpService.java
@@ -0,0 +1,55 @@
+package org.eclipse.equinox.http.servlet;
+
+import java.util.Dictionary;
+import javax.servlet.Filter;
+import javax.servlet.ServletException;
+import org.osgi.service.http.*;
+
+/**
+ * @since 1.1
+ */
+public interface ExtendedHttpService extends HttpService {
+ /**
+ * @param alias name in the URI namespace at which the filter is registered
+ * @param filter the filter object to register
+ * @param initparams initialization arguments for the filter or
+ * null
if there are none. This argument is used by the
+ * filter's FilterConfig
object.
+ * @param context the HttpContext
object for the registered
+ * filter, or null
if a default HttpContext
is
+ * to be created and used.
+ * @throws javax.servlet.ServletException if the filter's init
+ * method throws an exception, or the given filter object has
+ * already been registered at a different alias.
+ * @throws java.lang.IllegalArgumentException if any of the arguments are
+ * invalid
+ */
+ public void registerFilter(String alias, Filter filter, Dictionary initparams, HttpContext context) throws ServletException, NamespaceException;
+
+ /**
+ * Unregisters a previous filter registration done by the
+ * registerFilter
method.
+ *
+ *
+ * After this call, the registered filter will no
+ * longer be available. The Http Service must call the destroy
+ * method of the filter before returning.
+ *
+ * If the bundle which performed the registration is stopped or otherwise
+ * "unget"s the Http Service without calling {@link #unregisterFilter} then the Http
+ * Service must automatically unregister the filter registration. However, the
+ * destroy
method of the filter will not be called in this case since
+ * the bundle may be stopped.
+ * {@link #unregisterFilter} must be explicitly called to cause the
+ * destroy
method of the filter to be called. This can be done
+ * in the BundleActivator.stop
method of the
+ * bundle registering the filter.
+ *
+ * @param filter the filter object to unregister
+ * @throws java.lang.IllegalArgumentException if there is no registration
+ * for the filter or the calling bundle was not the bundle which
+ * registered the filter.
+ */
+ public void unregisterFilter(Filter filter);
+
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceServlet.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceServlet.java
new file mode 100644
index 0000000000..a511ae9c6d
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceServlet.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * 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.http.servlet;
+
+import org.eclipse.equinox.http.servlet.internal.ProxyServlet;
+
+/**
+ * The HttpServiceServlet is the "public" side of a Servlet that when registered (and init() called) in a servlet container
+ * will in-turn register and provide an OSGi Http Service implementation.
+ * This class is not meant for extending or even using directly and is purely meant for registering
+ * in a servlet container.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class HttpServiceServlet extends ProxyServlet {
+ private static final long serialVersionUID = -3647550992964861187L;
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Activator.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Activator.java
new file mode 100644
index 0000000000..66cf277e9c
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Activator.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import java.util.*;
+import javax.servlet.ServletConfig;
+import org.eclipse.equinox.http.servlet.ExtendedHttpService;
+import org.osgi.framework.*;
+import org.osgi.service.http.HttpService;
+
+public class Activator implements BundleActivator {
+
+ private static final String DEFAULT_SERVICE_DESCRIPTION = "Equinox Servlet Bridge"; //$NON-NLS-1$
+ private static final String DEFAULT_SERVICE_VENDOR = "Eclipse.org"; //$NON-NLS-1$
+ private static final String[] HTTP_SERVICES_CLASSES = new String[] {HttpService.class.getName(), ExtendedHttpService.class.getName()};
+
+ private static BundleContext context;
+ private static Map serviceRegistrations = new HashMap();
+
+ public void start(BundleContext bundleContext) throws Exception {
+ startHttpServiceProxy(bundleContext);
+ }
+
+ public void stop(BundleContext bundleContext) throws Exception {
+ stopHttpServiceProxy(bundleContext);
+ }
+
+ private static synchronized void startHttpServiceProxy(BundleContext bundleContext) {
+ context = bundleContext;
+ Object[] proxyServlets = serviceRegistrations.keySet().toArray();
+ for (int i = 0; i < proxyServlets.length; ++i) {
+ ServiceRegistration registration = registerHttpService((ProxyServlet) proxyServlets[i]);
+ serviceRegistrations.put(proxyServlets[i], registration);
+ }
+ }
+
+ private static synchronized void stopHttpServiceProxy(BundleContext bundleContext) {
+ Object[] proxyServlets = serviceRegistrations.keySet().toArray();
+ for (int i = 0; i < proxyServlets.length; ++i) {
+ ServiceRegistration registration = (ServiceRegistration) serviceRegistrations.put(proxyServlets[i], null);
+ registration.unregister();
+ }
+ context = null;
+ }
+
+ static synchronized void addProxyServlet(ProxyServlet proxyServlet) {
+ ServiceRegistration registration = null;
+ if (context != null)
+ registration = registerHttpService(proxyServlet);
+
+ serviceRegistrations.put(proxyServlet, registration);
+ }
+
+ private static ServiceRegistration registerHttpService(ProxyServlet proxyServlet) {
+ HttpServiceFactory factory = new HttpServiceFactory(proxyServlet);
+ Dictionary serviceProperties = new Hashtable(2);
+ ServletConfig config = proxyServlet.getServletConfig();
+ Enumeration initparameterNames = config.getInitParameterNames();
+ while (initparameterNames.hasMoreElements()) {
+ String name = (String) initparameterNames.nextElement();
+ serviceProperties.put(name, config.getInitParameter(name));
+ }
+
+ if (serviceProperties.get(Constants.SERVICE_VENDOR) == null)
+ serviceProperties.put(Constants.SERVICE_VENDOR, DEFAULT_SERVICE_VENDOR);
+
+ if (serviceProperties.get(Constants.SERVICE_DESCRIPTION) == null)
+ serviceProperties.put(Constants.SERVICE_DESCRIPTION, DEFAULT_SERVICE_DESCRIPTION);
+
+ return context.registerService(HTTP_SERVICES_CLASSES, factory, serviceProperties);
+ }
+
+ static synchronized void removeProxyServlet(ProxyServlet proxyServlet) {
+ ServiceRegistration registration = (ServiceRegistration) serviceRegistrations.remove(proxyServlet);
+ if (registration != null)
+ registration.unregister();
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultHttpContext.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultHttpContext.java
new file mode 100644
index 0000000000..b4d43fc5ab
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultHttpContext.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import java.io.IOException;
+import java.net.URL;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+
+public class DefaultHttpContext implements HttpContext {
+
+ private Bundle bundle;
+
+ public DefaultHttpContext(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ // default behaviour assumes the container has already performed authentication
+ return true;
+ }
+
+ public URL getResource(String name) {
+ return bundle.getResource(name);
+ }
+
+ public String getMimeType(String name) {
+ return null;
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterChainImpl.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterChainImpl.java
new file mode 100644
index 0000000000..49b50629f7
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterChainImpl.java
@@ -0,0 +1,30 @@
+package org.eclipse.equinox.http.servlet.internal;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class FilterChainImpl implements FilterChain {
+
+ private List matchingFilterRegistrations;
+ private ServletRegistration registration;
+ private int filterIndex = 0;
+ private int filterCount;
+
+ public FilterChainImpl(List matchingFilterRegistrations, ServletRegistration registration) {
+ this.matchingFilterRegistrations = matchingFilterRegistrations;
+ this.registration = registration;
+ this.filterCount = matchingFilterRegistrations.size();
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+ if (filterIndex < filterCount) {
+ FilterRegistration filterRegistration = (FilterRegistration) matchingFilterRegistrations.get(filterIndex++);
+ filterRegistration.doFilter((HttpServletRequest) request, (HttpServletResponse) response, this);
+ return;
+ }
+ registration.service((HttpServletRequest) request, (HttpServletResponse) response);
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterConfigImpl.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterConfigImpl.java
new file mode 100644
index 0000000000..388b780996
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterConfigImpl.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal;
+
+import java.util.*;
+import javax.servlet.*;
+
+public class FilterConfigImpl implements FilterConfig {
+
+ private static final Dictionary EMPTY_PARAMS = new Hashtable(0);
+ private static final String FILTER_NAME = "filter-name"; //$NON-NLS-1$
+ private Filter filter;
+ private Dictionary initparams;
+ private ServletContext servletContext;
+
+ public FilterConfigImpl(Filter filter, Dictionary initparams, ServletContext servletContext) {
+ this.filter = filter;
+ this.initparams = (initparams != null) ? initparams : EMPTY_PARAMS;
+ this.servletContext = servletContext;
+ }
+
+ /*
+ * @see javax.servlet.FilterConfig#getFilterName()
+ *
+ * The OSGi Http Service does not specify a way to set a filter name at the API level. This
+ * implementation will try to use the value of the "filter-name" initial parameter if available.
+ */
+ public String getFilterName() {
+ String filterName = (String) initparams.get(FILTER_NAME);
+ return (filterName != null) ? filterName : filter.getClass().getName();
+ }
+
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
+
+ public String getInitParameter(String name) {
+ return (String) initparams.get(name);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return initparams.keys();
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterRegistration.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterRegistration.java
new file mode 100644
index 0000000000..0faa00dd2b
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterRegistration.java
@@ -0,0 +1,112 @@
+package org.eclipse.equinox.http.servlet.internal;
+
+import java.io.IOException;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.osgi.service.http.HttpContext;
+
+//This class wraps the filter object registered in the HttpService.registerFilter call, to manage the context classloader when handleRequests are being asked.
+public class FilterRegistration extends Registration implements Comparable {
+
+ private static long nextSequenceNumber = 1L;
+
+ private final Filter filter; //The actual filter object registered against the http service. All filter requests will eventually be delegated to it.
+ private final HttpContext httpContext; //The context used during the registration of the filter
+ private final ClassLoader registeredContextClassLoader;
+ private final String prefix;
+ private final String suffix;
+ private final int priority;
+ private final long sequenceNumber;
+
+ public FilterRegistration(Filter filter, HttpContext context, String alias, int priority) {
+ this.filter = filter;
+ this.httpContext = context;
+ registeredContextClassLoader = Thread.currentThread().getContextClassLoader();
+
+ int lastSlash = alias.lastIndexOf('/');
+ String lastSegment = alias.substring(alias.lastIndexOf('/') + 1);
+ if (lastSegment.startsWith("*.")) { //$NON-NLS-1$
+ prefix = alias.substring(0, lastSlash);
+ suffix = lastSegment.substring(1);
+ } else {
+ prefix = alias.equals("/") ? "" : alias; //$NON-NLS-1$//$NON-NLS-2$
+ suffix = null;
+ }
+
+ this.priority = priority;
+ synchronized (FilterRegistration.class) {
+ this.sequenceNumber = nextSequenceNumber++;
+ }
+ }
+
+ public void destroy() {
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(registeredContextClassLoader);
+ super.destroy();
+ filter.destroy();
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ //Delegate the init call to the actual filter
+ public void init(FilterConfig filterConfig) throws ServletException {
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(registeredContextClassLoader);
+ filter.init(filterConfig);
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ //Delegate the handling of the request to the actual filter
+ public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(registeredContextClassLoader);
+ if (httpContext.handleSecurity(request, response))
+ filter.doFilter(request, response, chain);
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public HttpContext getHttpContext() {
+ return httpContext;
+ }
+
+ public boolean matches(String dispatchPathInfo) {
+ if (!dispatchPathInfo.startsWith(prefix))
+ return false;
+
+ // perfect match
+ if (prefix.length() == dispatchPathInfo.length())
+ return suffix == null;
+
+ // check the next character is a path separator
+ if (dispatchPathInfo.charAt(prefix.length()) != '/')
+ return false;
+
+ // check for an extension match
+ if (suffix == null)
+ return true;
+
+ return dispatchPathInfo.endsWith(suffix) && dispatchPathInfo.length() > prefix.length() + suffix.length();
+ }
+
+ public int compareTo(Object other) {
+ FilterRegistration otherFilterRegistration = (FilterRegistration) other;
+ int priorityDifference = priority - otherFilterRegistration.priority;
+ if (priorityDifference != 0)
+ return -priorityDifference;
+
+ return (sequenceNumber > otherFilterRegistration.sequenceNumber) ? 1 : -1;
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceFactory.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceFactory.java
new file mode 100644
index 0000000000..3e147a4e14
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceFactory.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import org.osgi.framework.*;
+
+// Factory to create http services. This is because the service needs to be customized for each bundle in order to implement the default resource lookups.
+public class HttpServiceFactory implements ServiceFactory {
+
+ private ProxyServlet proxy;
+
+ public HttpServiceFactory(ProxyServlet proxy) {
+ this.proxy = proxy;
+ }
+
+ public Object getService(Bundle bundle, ServiceRegistration registration) {
+ return new HttpServiceImpl(bundle, proxy);
+ }
+
+ public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+ ((HttpServiceImpl) service).shutdown();
+
+ }
+
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
new file mode 100644
index 0000000000..0d99739af1
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 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.http.servlet.internal;
+
+import java.util.*;
+import javax.servlet.*;
+import org.eclipse.equinox.http.servlet.ExtendedHttpService;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.*;
+
+public class HttpServiceImpl implements HttpService, ExtendedHttpService {
+
+ private Bundle bundle; //The bundle associated with this instance of http service
+
+ private ProxyServlet proxy; //The proxy that does the dispatching of the incoming requests
+
+ private Set aliases = new HashSet(); //Aliases registered against this particular instance of the service
+ private Set filters = new HashSet(); //Filters registered against this particular instance of the service
+
+ private boolean shutdown = false; // We prevent use of this instance if HttpServiceFactory.ungetService has called unregisterAliases.
+
+ public HttpServiceImpl(Bundle bundle, ProxyServlet proxy) {
+ this.bundle = bundle;
+ this.proxy = proxy;
+ }
+
+ //Clean up method
+ synchronized void shutdown() {
+ for (Iterator it = aliases.iterator(); it.hasNext();) {
+ String alias = (String) it.next();
+ proxy.unregister(alias, false);
+ }
+ aliases.clear();
+
+ for (Iterator it = filters.iterator(); it.hasNext();) {
+ Filter filter = (Filter) it.next();
+ proxy.unregisterFilter(filter, false);
+ }
+ filters.clear();
+ shutdown = true;
+ }
+
+ private void checkShutdown() {
+ if (shutdown)
+ throw new IllegalStateException("Service instance is already shutdown"); //$NON-NLS-1$
+ }
+
+ /**
+ * @see HttpService#registerServlet(String, Servlet, Dictionary, HttpContext)
+ */
+ public synchronized void registerServlet(String alias, Servlet servlet, Dictionary initparams, HttpContext context) throws ServletException, NamespaceException {
+ checkShutdown();
+ if (context == null) {
+ context = createDefaultHttpContext();
+ }
+ proxy.registerServlet(alias, servlet, initparams, context);
+ aliases.add(alias);
+ }
+
+ /**
+ * @see HttpService#registerResources(String, String, HttpContext)
+ */
+ public synchronized void registerResources(String alias, String name, HttpContext context) throws NamespaceException {
+ checkShutdown();
+ if (context == null) {
+ context = createDefaultHttpContext();
+ }
+ proxy.registerResources(alias, name, context);
+ aliases.add(alias);
+ }
+
+ /**
+ * @see HttpService#unregister(String)
+ */
+ public synchronized void unregister(String alias) {
+ checkShutdown();
+ if (aliases.remove(alias)) {
+ proxy.unregister(alias, true);
+ } else {
+ throw new IllegalArgumentException("Alias not found: " + alias); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * @see HttpService#createDefaultHttpContext()
+ */
+ public synchronized HttpContext createDefaultHttpContext() {
+ checkShutdown();
+ return new DefaultHttpContext(bundle);
+ }
+
+ public void registerFilter(String alias, Filter filter, Dictionary initparams, HttpContext context) throws ServletException {
+ checkShutdown();
+ if (context == null) {
+ context = createDefaultHttpContext();
+ }
+ proxy.registerFilter(alias, filter, initparams, context);
+ filters.add(filter);
+ }
+
+ public void unregisterFilter(Filter filter) {
+ checkShutdown();
+ if (filters.remove(filter)) {
+ proxy.unregisterFilter(filter, true);
+ } else {
+ throw new IllegalArgumentException("Filter not found."); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServletRequestAdaptor.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServletRequestAdaptor.java
new file mode 100644
index 0000000000..761ff36b5f
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServletRequestAdaptor.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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
+ * Angelo Zerr - give the capability to use resources (JSP, HTML, Servlet...)
+ * from the Bridge webapp with HTTP Filter.
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.osgi.service.http.HttpContext;
+
+public class HttpServletRequestAdaptor extends HttpServletRequestWrapper {
+
+ private String alias;
+ private Servlet servlet;
+ private boolean isRequestDispatcherInclude;
+ private boolean calledByFilter;
+
+ static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri"; //$NON-NLS-1$
+ static final String INCLUDE_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.include.context_path"; //$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$
+
+ public HttpServletRequestAdaptor(HttpServletRequest req, String alias, Servlet servlet, FilterChain filterChain) {
+ super(req);
+ this.alias = alias;
+ this.servlet = servlet;
+ isRequestDispatcherInclude = req.getAttribute(HttpServletRequestAdaptor.INCLUDE_REQUEST_URI_ATTRIBUTE) != null;
+ this.calledByFilter = (filterChain != null);
+ }
+
+ public String getAuthType() {
+ String authType = (String) super.getAttribute(HttpContext.AUTHENTICATION_TYPE);
+ if (authType != null)
+ return authType;
+
+ return super.getAuthType();
+ }
+
+ public String getRemoteUser() {
+ String remoteUser = (String) super.getAttribute(HttpContext.REMOTE_USER);
+ if (remoteUser != null)
+ return remoteUser;
+
+ return super.getRemoteUser();
+ }
+
+ public String getPathInfo() {
+ String pathInfo = getPathInfo(super.getPathInfo(), super.getContextPath(), super.getRequestURI(), this.calledByFilter);
+ if (isRequestDispatcherInclude)
+ return pathInfo;
+
+ if (alias.equals("/")) { //$NON-NLS-1$
+ return pathInfo;
+ }
+ pathInfo = pathInfo.substring(alias.length());
+ if (pathInfo.length() == 0)
+ return null;
+
+ return pathInfo;
+ }
+
+ public String getServletPath() {
+ if (isRequestDispatcherInclude)
+ return super.getServletPath();
+
+ if (alias.equals("/")) { //$NON-NLS-1$
+ return ""; //$NON-NLS-1$
+ }
+ return alias;
+ }
+
+ public String getContextPath() {
+ if (isRequestDispatcherInclude)
+ return super.getContextPath();
+ if (!calledByFilter)
+ // Not called with HTTP Filter, servlet case.
+ return super.getContextPath() + super.getServletPath();
+ // HTTP Filter case.
+ return super.getContextPath();
+ }
+
+ public Object getAttribute(String attributeName) {
+ if (isRequestDispatcherInclude) {
+ if (attributeName.equals(HttpServletRequestAdaptor.INCLUDE_CONTEXT_PATH_ATTRIBUTE)) {
+ String contextPath = (String) super.getAttribute(HttpServletRequestAdaptor.INCLUDE_CONTEXT_PATH_ATTRIBUTE);
+ if (contextPath == null || contextPath.equals("/")) //$NON-NLS-1$
+ contextPath = ""; //$NON-NLS-1$
+
+ String servletPath = (String) super.getAttribute(HttpServletRequestAdaptor.INCLUDE_SERVLET_PATH_ATTRIBUTE);
+ if (servletPath == null || servletPath.equals("/")) //$NON-NLS-1$
+ servletPath = ""; //$NON-NLS-1$
+
+ return contextPath + servletPath;
+ } else if (attributeName.equals(HttpServletRequestAdaptor.INCLUDE_SERVLET_PATH_ATTRIBUTE)) {
+ if (alias.equals("/")) { //$NON-NLS-1$
+ return ""; //$NON-NLS-1$
+ }
+ return alias;
+ } else if (attributeName.equals(HttpServletRequestAdaptor.INCLUDE_PATH_INFO_ATTRIBUTE)) {
+ String pathInfo = (String) super.getAttribute(HttpServletRequestAdaptor.INCLUDE_PATH_INFO_ATTRIBUTE);
+ if (alias.equals("/")) { //$NON-NLS-1$
+ return pathInfo;
+ }
+
+ pathInfo = pathInfo.substring(alias.length());
+ if (pathInfo.length() == 0)
+ return null;
+
+ return pathInfo;
+ }
+ }
+
+ return super.getAttribute(attributeName);
+ }
+
+ public RequestDispatcher getRequestDispatcher(String arg0) {
+ return new RequestDispatcherAdaptor(super.getRequestDispatcher(super.getServletPath() + arg0));
+ }
+
+ public static String getDispatchPathInfo(HttpServletRequest req, FilterChain filterChain) {
+ if (req.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null)
+ return (String) req.getAttribute(INCLUDE_PATH_INFO_ATTRIBUTE);
+ return getPathInfo(req.getPathInfo(), req.getContextPath(), req.getRequestURI(), filterChain);
+ }
+
+ /**
+ * Returns the path info pathInfo if not null otherwise compute it (in Filter case) by using contextPath + requestURI.
+ *
+ * @param pathInfo the path info coming from the request.getPathInfo().
+ * @param contextPath the context path coming from the request.getContextPath().
+ * @param requestURI the request URI coming from the request.getRequestURI().
+ * @param filterChain the filter chain or null if this method is not called with Filter.
+ * @return the path info pathInfo if not null otherwise compute it (in Filter case) by using contextPath + requestURI.
+ */
+ private static String getPathInfo(String pathInfo, String contextPath, String requestURI, FilterChain filterChain) {
+ return getPathInfo(pathInfo, contextPath, requestURI, filterChain != null);
+ }
+
+ /**
+ * Returns the path info pathInfo if not null otherwise compute it (in Filter case) by using contextPath + requestURI.
+ *
+ * @param pathInfo the path info coming from the request.getPathInfo().
+ * @param contextPath the context path coming from the request.getContextPath().
+ * @param requestURI the request URI coming from the request.getRequestURI().
+ * @param calledByFilter true if this method is called with Filter and false otherwise.
+ * @return the path info pathInfo if not null otherwise compute it (in Filter case) by using contextPath + requestURI.
+ */
+ private static String getPathInfo(String pathInfo, String contextPath, String requestURI, boolean calledByFilter) {
+ if (pathInfo != null) {
+ return pathInfo;
+ }
+ if (!calledByFilter) {
+ // Not called with HTTP Filter, servlet case.
+ return null;
+ }
+
+ // HTTP Filter case, pathInfo is null, compute it by using contextPath and requestURI
+ return requestURI.substring(contextPath.length(), requestURI.length());
+ }
+
+ public static String getDispatchServletPath(HttpServletRequest req) {
+ if (req.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null) {
+ String servletPath = (String) req.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE);
+ return (servletPath == null) ? "" : servletPath; //$NON-NLS-1$
+ }
+ return req.getServletPath();
+ }
+
+ public HttpSession getSession() {
+ HttpSession session = super.getSession();
+ if (session != null)
+ return new HttpSessionAdaptor(session, servlet);
+
+ return null;
+ }
+
+ public HttpSession getSession(boolean create) {
+ HttpSession session = super.getSession(create);
+ if (session != null)
+ return new HttpSessionAdaptor(session, servlet);
+
+ return null;
+ }
+
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpSessionAdaptor.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpSessionAdaptor.java
new file mode 100644
index 0000000000..1784a25810
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpSessionAdaptor.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import java.util.Enumeration;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+
+// This class adapts HttpSessions in order to return the right ServletContext
+public class HttpSessionAdaptor implements HttpSession {
+
+ private HttpSession session;
+ private Servlet servlet;
+
+ public HttpSessionAdaptor(HttpSession session, Servlet servlet) {
+ this.session = session;
+ this.servlet = servlet;
+ }
+
+ public ServletContext getServletContext() {
+ return servlet.getServletConfig().getServletContext();
+ }
+
+ public Object getAttribute(String arg0) {
+ return session.getAttribute(arg0);
+ }
+
+ public Enumeration getAttributeNames() {
+ return session.getAttributeNames();
+ }
+
+ public long getCreationTime() {
+ return session.getCreationTime();
+ }
+
+ public String getId() {
+ return session.getId();
+ }
+
+ public long getLastAccessedTime() {
+ return session.getLastAccessedTime();
+ }
+
+ public int getMaxInactiveInterval() {
+ return session.getMaxInactiveInterval();
+ }
+
+ /**@deprecated*/
+ public javax.servlet.http.HttpSessionContext getSessionContext() {
+ return session.getSessionContext();
+ }
+
+ /**@deprecated*/
+ public Object getValue(String arg0) {
+ return session.getValue(arg0);
+ }
+
+ /**@deprecated*/
+ public String[] getValueNames() {
+ return session.getValueNames();
+ }
+
+ public void invalidate() {
+ session.invalidate();
+ }
+
+ public boolean isNew() {
+ return session.isNew();
+ }
+
+ /**@deprecated*/
+ public void putValue(String arg0, Object arg1) {
+ session.putValue(arg0, arg1);
+ }
+
+ public void removeAttribute(String arg0) {
+ session.removeAttribute(arg0);
+ }
+
+ /**@deprecated*/
+ public void removeValue(String arg0) {
+ session.removeValue(arg0);
+ }
+
+ public void setAttribute(String arg0, Object arg1) {
+ session.setAttribute(arg0, arg1);
+ }
+
+ public void setMaxInactiveInterval(int arg0) {
+ session.setMaxInactiveInterval(arg0);
+ }
+
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyContext.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyContext.java
new file mode 100644
index 0000000000..cfa906fc41
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyContext.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import java.io.File;
+import java.util.*;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import org.osgi.service.http.HttpContext;
+
+/**
+ * The ProxyContext provides something similar to a ServletContext for all servlets and resources under a particular ProxyServlet.
+ * In particular it holds and represent the concept of "context path" through the Proxy Servlets servlet path.
+ * The Http Service also requires a ServletContext namespaced by each individual HttpContext. The ProxyContext provides support for the
+ * attribute map of a ServletContext again namespaced by HttpContext as specified in the Http Service specification. The ContextAttributes
+ * are reference counted so that when the HttpContext is no longer referenced the associated context attributes can be
+ * garbage collected and the context temp dir deleteted.
+ */
+public class ProxyContext {
+ private static final String JAVAX_SERVLET_CONTEXT_TEMPDIR = "javax.servlet.context.tempdir"; //$NON-NLS-1$
+
+ private String servletPath;
+ private HashMap attributesMap = new HashMap();
+ File proxyContextTempDir;
+
+ public ProxyContext(ServletContext servletContext) {
+ File tempDir = (File) servletContext.getAttribute(JAVAX_SERVLET_CONTEXT_TEMPDIR);
+ if (tempDir != null) {
+ proxyContextTempDir = new File(tempDir, "proxytemp"); //$NON-NLS-1$
+ deleteDirectory(proxyContextTempDir);
+ proxyContextTempDir.mkdirs();
+ }
+ }
+
+ public void destroy() {
+ if (proxyContextTempDir != null)
+ deleteDirectory(proxyContextTempDir);
+ }
+
+ synchronized void initializeServletPath(HttpServletRequest req, FilterChain filterChain) {
+ if (servletPath == null) {
+ if (filterChain != null)
+ servletPath = "/";
+ else
+ servletPath = HttpServletRequestAdaptor.getDispatchServletPath(req);
+ }
+ }
+
+ synchronized String getServletPath() {
+ return servletPath;
+ }
+
+ synchronized void createContextAttributes(HttpContext httpContext) {
+ ContextAttributes attributes = (ContextAttributes) attributesMap.get(httpContext);
+ if (attributes == null) {
+ attributes = new ContextAttributes(httpContext);
+ attributesMap.put(httpContext, attributes);
+ }
+ attributes.addReference();
+ }
+
+ synchronized void destroyContextAttributes(HttpContext httpContext) {
+ ContextAttributes attributes = (ContextAttributes) attributesMap.get(httpContext);
+ attributes.removeReference();
+ if (attributes.referenceCount() == 0) {
+ attributesMap.remove(httpContext);
+ attributes.destroy();
+ }
+ }
+
+ synchronized Dictionary getContextAttributes(HttpContext httpContext) {
+ return (Dictionary) attributesMap.get(httpContext);
+ }
+
+ /**
+ * deleteDirectory is a convenience method to recursively delete a directory
+ * @param directory - the directory to delete.
+ * @return was the delete succesful
+ */
+ protected static boolean deleteDirectory(File directory) {
+ if (directory.exists() && directory.isDirectory()) {
+ File[] files = directory.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory()) {
+ deleteDirectory(files[i]);
+ } else {
+ files[i].delete();
+ }
+ }
+ }
+ return directory.delete();
+ }
+
+ public class ContextAttributes extends Hashtable {
+ private static final long serialVersionUID = 1916670423277243587L;
+ private int referenceCount;
+
+ public ContextAttributes(HttpContext httpContext) {
+ if (proxyContextTempDir != null) {
+ File contextTempDir = new File(proxyContextTempDir, "hc_" + httpContext.hashCode()); //$NON-NLS-1$
+ contextTempDir.mkdirs();
+ put(JAVAX_SERVLET_CONTEXT_TEMPDIR, contextTempDir);
+ }
+ }
+
+ public void destroy() {
+ File contextTempDir = (File) get(JAVAX_SERVLET_CONTEXT_TEMPDIR);
+ if (contextTempDir != null)
+ deleteDirectory(contextTempDir);
+ }
+
+ public void addReference() {
+ referenceCount++;
+ }
+
+ public void removeReference() {
+ referenceCount--;
+ }
+
+ public int referenceCount() {
+ return referenceCount;
+ }
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyServlet.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyServlet.java
new file mode 100644
index 0000000000..a9c051c653
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyServlet.java
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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
+ * Angelo Zerr - give the capability to use resources (JSP, HTML, Servlet...)
+ * from the Bridge webapp with HTTP Filter.
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.NamespaceException;
+
+/**
+ * The ProxyServlet is the private side of a Servlet that when registered (and init() called) in a servlet container
+ * will in-turn register and provide an OSGi Http Service implementation.
+ * This class is not meant for extending or even using directly and is purely meant for registering
+ * in a servlet container.
+ */
+public class ProxyServlet extends HttpServlet implements Filter {
+
+ private static final long serialVersionUID = 4117456123807468871L;
+ private Map servletRegistrations = new HashMap(); //alias --> servlet registration
+ private Set registeredServlets = new HashSet(); //All the servlets objects that have been registered
+
+ private Map filterRegistrations = new HashMap(); //filter --> filter registration;
+ private ProxyContext proxyContext;
+
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ proxyContext = new ProxyContext(config.getServletContext());
+ Activator.addProxyServlet(this);
+ }
+
+ public void destroy() {
+ Activator.removeProxyServlet(this);
+ proxyContext.destroy();
+ proxyContext = null;
+ super.destroy();
+ }
+
+ /**
+ * @see HttpServlet#service(ServletRequest, ServletResponse)
+ */
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ process(req, resp, null);
+ }
+
+ protected void process(HttpServletRequest req, HttpServletResponse resp, FilterChain filterChain) throws ServletException, IOException {
+ proxyContext.initializeServletPath(req, filterChain);
+ String alias = HttpServletRequestAdaptor.getDispatchPathInfo(req, filterChain);
+ if (alias == null)
+ alias = "/"; //$NON-NLS-1$
+
+ // perfect match
+ if (processAlias(req, resp, alias, null, filterChain))
+ return;
+
+ String extensionAlias = findExtensionAlias(alias);
+ alias = alias.substring(0, alias.lastIndexOf('/'));
+
+ // longest path match
+ while (alias.length() != 0) {
+ if (processAlias(req, resp, alias, extensionAlias, filterChain))
+ return;
+ alias = alias.substring(0, alias.lastIndexOf('/'));
+ }
+
+ // default handler match
+ if (extensionAlias != null)
+ extensionAlias = extensionAlias.substring(1); // remove the leading '/'
+ if (processAlias(req, resp, "/", extensionAlias, filterChain)) //Handle '/' aliases //$NON-NLS-1$
+ return;
+
+ // Cannot find the Servlet or Filter from the OSGi registry services :
+ if (filterChain != null) {
+ // Delegate the HTTP request to the Web server container.
+ filterChain.doFilter(req, resp);
+
+ } else {
+ // Send HTTP 4040 Error.
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "ProxyServlet: " + req.getRequestURI()); //$NON-NLS-1$
+ }
+ }
+
+ private String findExtensionAlias(String alias) {
+ String lastSegment = alias.substring(alias.lastIndexOf('/') + 1);
+ int dot = lastSegment.lastIndexOf('.');
+ if (dot == -1)
+ return null;
+ String extension = lastSegment.substring(dot + 1);
+ if (extension.length() == 0)
+ return null;
+ return "/*." + extension; //$NON-NLS-1$
+ }
+
+ private boolean processAlias(HttpServletRequest req, HttpServletResponse resp, String alias, String extensionAlias, FilterChain filterChain) throws ServletException, IOException {
+ ServletRegistration registration = null;
+ List matchingFilterRegistrations = Collections.EMPTY_LIST;
+ String dispatchPathInfo = HttpServletRequestAdaptor.getDispatchPathInfo(req, filterChain);
+ synchronized (this) {
+ if (extensionAlias == null)
+ registration = (ServletRegistration) servletRegistrations.get(alias);
+ else {
+ registration = (ServletRegistration) servletRegistrations.get(alias + extensionAlias);
+ if (registration != null) {
+ // for regular ServletRegistrations extensions should be handled on the full alias
+ if (!(registration.getServlet() instanceof ResourceServlet))
+ alias = HttpServletRequestAdaptor.getDispatchPathInfo(req, filterChain);
+ } else
+ registration = (ServletRegistration) servletRegistrations.get(alias);
+ }
+ if (registration != null) {
+ registration.addReference();
+ if (!filterRegistrations.isEmpty()) {
+ matchingFilterRegistrations = new ArrayList();
+ for (Iterator it = filterRegistrations.values().iterator(); it.hasNext();) {
+ FilterRegistration filterRegistration = (FilterRegistration) it.next();
+ if (filterRegistration.matches(dispatchPathInfo)) {
+ matchingFilterRegistrations.add(filterRegistration);
+ filterRegistration.addReference();
+ }
+ }
+ }
+ }
+ }
+ if (registration != null) {
+ try {
+ HttpServletRequest wrappedRequest = new HttpServletRequestAdaptor(req, alias, registration.getServlet(), filterChain);
+ if (matchingFilterRegistrations.isEmpty()) {
+ registration.service(wrappedRequest, resp);
+ } else {
+ Collections.sort(matchingFilterRegistrations);
+ FilterChain chain = new FilterChainImpl(matchingFilterRegistrations, registration);
+ chain.doFilter(wrappedRequest, resp);
+ }
+ } finally {
+ registration.removeReference();
+ for (Iterator it = matchingFilterRegistrations.iterator(); it.hasNext();) {
+ FilterRegistration filterRegistration = (FilterRegistration) it.next();
+ filterRegistration.removeReference();
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ //Effective unregistration of servlet and resources as defined in HttpService#unregister()
+ synchronized void unregister(String alias, boolean destroy) {
+ ServletRegistration removedRegistration = (ServletRegistration) servletRegistrations.remove(alias);
+ if (removedRegistration != null) {
+ registeredServlets.remove(removedRegistration.getServlet());
+ try {
+ if (destroy)
+ removedRegistration.destroy();
+ } finally {
+ proxyContext.destroyContextAttributes(removedRegistration.getHttpContext());
+ }
+ }
+ }
+
+ //Effective registration of the servlet as defined HttpService#registerServlet()
+ synchronized void registerServlet(String alias, Servlet servlet, Dictionary initparams, HttpContext httpContext) throws ServletException, NamespaceException {
+ checkAlias(alias);
+ if (servletRegistrations.containsKey(alias))
+ throw new NamespaceException("The alias '" + alias + "' is already in use."); //$NON-NLS-1$//$NON-NLS-2$
+
+ if (servlet == null)
+ throw new IllegalArgumentException("Servlet cannot be null"); //$NON-NLS-1$
+
+ if (registeredServlets.contains(servlet))
+ throw new ServletException("This servlet has already been registered."); //$NON-NLS-1$
+
+ ServletRegistration registration = new ServletRegistration(servlet, httpContext);
+ ServletContext wrappedServletContext = new ServletContextAdaptor(proxyContext, getServletContext(), httpContext, AccessController.getContext());
+ ServletConfig servletConfig = new ServletConfigImpl(servlet, initparams, wrappedServletContext);
+
+ boolean initialized = false;
+ proxyContext.createContextAttributes(httpContext);
+ try {
+ registration.init(servletConfig);
+ initialized = true;
+ } finally {
+ if (!initialized)
+ proxyContext.destroyContextAttributes(httpContext);
+ }
+ registeredServlets.add(servlet);
+ servletRegistrations.put(alias, registration);
+ }
+
+ //Effective registration of the resources as defined HttpService#registerResources()
+ synchronized void registerResources(String alias, String name, HttpContext httpContext) throws NamespaceException {
+ checkName(name);
+ Servlet resourceServlet = new ResourceServlet(name, httpContext, AccessController.getContext());
+ try {
+ registerServlet(alias, resourceServlet, null, httpContext);
+ } catch (ServletException e) {
+ throw new IllegalStateException("Unexpected ServletException throw when registering resources at alias " + alias + "."); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
+
+ private void checkName(String name) {
+ if (name == null)
+ throw new IllegalArgumentException("Name cannot be null"); //$NON-NLS-1$
+
+ if (name.endsWith("/") && !name.equals("/")) //$NON-NLS-1$ //$NON-NLS-2$
+ throw new IllegalArgumentException("Invalid Name '" + name + "'"); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ private void checkAlias(String alias) {
+ if (alias == null)
+ throw new IllegalArgumentException("Alias cannot be null"); //$NON-NLS-1$
+
+ if (!alias.startsWith("/") || (alias.endsWith("/") && !alias.equals("/"))) //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
+ throw new IllegalArgumentException("Invalid alias '" + alias + "'"); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ public synchronized void unregisterFilter(Filter filter, boolean destroy) {
+ FilterRegistration removedRegistration = (FilterRegistration) filterRegistrations.remove(filter);
+ if (removedRegistration != null) {
+ try {
+ if (destroy)
+ removedRegistration.destroy();
+ } finally {
+ proxyContext.destroyContextAttributes(removedRegistration.getHttpContext());
+ }
+ }
+ }
+
+ public synchronized void registerFilter(String alias, Filter filter, Dictionary initparams, HttpContext httpContext) throws ServletException {
+ checkAlias(alias);
+ if (filter == null)
+ throw new IllegalArgumentException("Filter cannot be null"); //$NON-NLS-1$
+
+ if (filterRegistrations.containsKey(filter))
+ throw new ServletException("This filter has already been registered."); //$NON-NLS-1$
+
+ int filterPriority = findFilterPriority(initparams);
+ FilterRegistration registration = new FilterRegistration(filter, httpContext, alias, filterPriority);
+ ServletContext wrappedServletContext = new ServletContextAdaptor(proxyContext, getServletContext(), httpContext, AccessController.getContext());
+ FilterConfig filterConfig = new FilterConfigImpl(filter, initparams, wrappedServletContext);
+
+ boolean initialized = false;
+ proxyContext.createContextAttributes(httpContext);
+ try {
+ registration.init(filterConfig);
+ initialized = true;
+ } finally {
+ if (!initialized)
+ proxyContext.destroyContextAttributes(httpContext);
+ }
+ filterRegistrations.put(filter, registration);
+ }
+
+ private int findFilterPriority(Dictionary initparams) {
+ if (initparams == null)
+ return 0;
+ String filterPriority = (String) initparams.get("filter-priority"); //$NON-NLS-1$
+ if (filterPriority == null)
+ return 0;
+
+ try {
+ int result = Integer.parseInt(filterPriority);
+ if (result >= -1000 && result <= 1000)
+ return result;
+ } catch (NumberFormatException e) {
+ // fall through
+ }
+ throw new IllegalArgumentException("filter-priority must be an integer between -1000 and 1000 but was: " + filterPriority); //$NON-NLS-1$
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ ServletConfig servletConfig = new ServletConfigAdaptor(filterConfig);
+ this.init(servletConfig);
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse resp = (HttpServletResponse) response;
+ process(req, resp, chain);
+ }
+
+ private static class ServletConfigAdaptor implements ServletConfig {
+
+ private FilterConfig filterConfig;
+
+ public ServletConfigAdaptor(FilterConfig filterConfig) {
+ this.filterConfig = filterConfig;
+ }
+
+ public String getInitParameter(String arg0) {
+ return filterConfig.getInitParameter(arg0);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return filterConfig.getInitParameterNames();
+ }
+
+ public ServletContext getServletContext() {
+ return filterConfig.getServletContext();
+ }
+
+ public String getServletName() {
+ return filterConfig.getFilterName();
+ }
+
+ }
+
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Registration.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Registration.java
new file mode 100644
index 0000000000..155346aee6
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Registration.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+public abstract class Registration {
+
+ protected int referenceCount;
+
+ public synchronized void addReference() {
+ ++referenceCount;
+ }
+
+ public synchronized void removeReference() {
+ --referenceCount;
+ if (referenceCount == 0) {
+ notifyAll();
+ }
+ }
+
+ public synchronized void destroy() {
+ boolean interrupted = false;
+ try {
+ while (referenceCount != 0) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // wait until the servlet is inactive but save the interrupted status
+ interrupted = true;
+ }
+ }
+ } finally {
+ if (interrupted)
+ Thread.currentThread().interrupt(); //restore the interrupted state
+ }
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/RequestDispatcherAdaptor.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/RequestDispatcherAdaptor.java
new file mode 100644
index 0000000000..4e48e403a8
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/RequestDispatcherAdaptor.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import java.io.IOException;
+import javax.servlet.*;
+
+//This class unwraps the request so it can be processed by the underlying servlet container.
+public class RequestDispatcherAdaptor implements RequestDispatcher {
+
+ private RequestDispatcher requestDispatcher;
+
+ public RequestDispatcherAdaptor(RequestDispatcher requestDispatcher) {
+ this.requestDispatcher = requestDispatcher;
+ }
+
+ public void forward(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
+ if (req instanceof HttpServletRequestAdaptor)
+ req = ((HttpServletRequestAdaptor) req).getRequest();
+
+ requestDispatcher.forward(req, resp);
+ }
+
+ public void include(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
+ if (req instanceof HttpServletRequestAdaptor)
+ req = ((HttpServletRequestAdaptor) req).getRequest();
+
+ requestDispatcher.include(req, resp);
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ResourceServlet.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ResourceServlet.java
new file mode 100644
index 0000000000..62f4ed11b8
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ResourceServlet.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * Copyright (c) 2005-2007 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
+ * Angelo Zerr - give the capability to use resources (JSP, HTML, Servlet...)
+ * from the Bridge webapp with HTTP Filter.
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.*;
+import javax.servlet.http.*;
+import org.osgi.service.http.HttpContext;
+
+public class ResourceServlet extends HttpServlet {
+ private static final long serialVersionUID = 3586876493076122102L;
+ private static final String LAST_MODIFIED = "Last-Modified"; //$NON-NLS-1$
+ private static final String IF_MODIFIED_SINCE = "If-Modified-Since"; //$NON-NLS-1$
+ private static final String IF_NONE_MATCH = "If-None-Match"; //$NON-NLS-1$
+ private static final String ETAG = "ETag"; //$NON-NLS-1$
+
+ private String internalName;
+ HttpContext httpContext;
+ private AccessControlContext acc;
+
+ public ResourceServlet(String internalName, HttpContext context, AccessControlContext acc) {
+ this.internalName = internalName;
+ if (internalName.equals("/")) { //$NON-NLS-1$
+ this.internalName = ""; //$NON-NLS-1$
+ }
+ this.httpContext = context;
+ this.acc = acc;
+ }
+
+ public void service(HttpServletRequest req, final HttpServletResponse resp) throws IOException {
+ String method = req.getMethod();
+ if (method.equals("GET") || method.equals("POST") || method.equals("HEAD")) { //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ String pathInfo = HttpServletRequestAdaptor.getDispatchPathInfo(req, null);
+ if (pathInfo == null)
+ pathInfo = ""; //$NON-NLS-1$
+ String resourcePath = internalName + pathInfo;
+ URL resourceURL = httpContext.getResource(resourcePath);
+ if (resourceURL != null)
+ writeResource(req, resp, resourcePath, resourceURL);
+ else
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "ProxyServlet: " + req.getRequestURI()); //$NON-NLS-1$
+ } else {
+ resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ }
+ }
+
+ private void writeResource(final HttpServletRequest req, final HttpServletResponse resp, final String resourcePath, final URL resourceURL) throws IOException {
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction() {
+
+ public Object run() throws Exception {
+ URLConnection connection = resourceURL.openConnection();
+ long lastModified = connection.getLastModified();
+ int contentLength = connection.getContentLength();
+
+ String etag = null;
+ if (lastModified != -1 && contentLength != -1)
+ etag = "W/\"" + contentLength + "-" + lastModified + "\""; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+
+ // Check for cache revalidation.
+ // We should prefer ETag validation as the guarantees are stronger and all HTTP 1.1 clients should be using it
+ String ifNoneMatch = req.getHeader(IF_NONE_MATCH);
+ if (ifNoneMatch != null && etag != null && ifNoneMatch.indexOf(etag) != -1) {
+ resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ return Boolean.TRUE;
+ }
+
+ long ifModifiedSince = req.getDateHeader(IF_MODIFIED_SINCE);
+ // for purposes of comparison we add 999 to ifModifiedSince since the fidelity
+ // of the IMS header generally doesn't include milli-seconds
+ if (ifModifiedSince > -1 && lastModified > 0 && lastModified <= (ifModifiedSince + 999)) {
+ resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ return Boolean.TRUE;
+ }
+
+ // return the full contents regularly
+ if (contentLength != -1)
+ resp.setContentLength(contentLength);
+
+ String contentType = httpContext.getMimeType(resourcePath);
+ if (contentType == null)
+ contentType = getServletConfig().getServletContext().getMimeType(resourcePath);
+
+ if (contentType != null)
+ resp.setContentType(contentType);
+
+ if (lastModified > 0)
+ resp.setDateHeader(LAST_MODIFIED, lastModified);
+
+ if (etag != null)
+ resp.setHeader(ETAG, etag);
+
+ if (contentLength != 0) {
+ // open the input stream
+ InputStream is = null;
+ try {
+ is = connection.getInputStream();
+ // write the resource
+ try {
+ OutputStream os = resp.getOutputStream();
+ int writtenContentLength = writeResourceToOutputStream(is, os);
+ if (contentLength == -1 || contentLength != writtenContentLength)
+ resp.setContentLength(writtenContentLength);
+ } catch (IllegalStateException e) { // can occur if the response output is already open as a Writer
+ Writer writer = resp.getWriter();
+ writeResourceToWriter(is, writer);
+ // Since ContentLength is a measure of the number of bytes contained in the body
+ // of a message when we use a Writer we lose control of the exact byte count and
+ // defer the problem to the Servlet Engine's Writer implementation.
+ }
+ } catch (FileNotFoundException e) {
+ // FileNotFoundException may indicate the following scenarios
+ // - url is a directory
+ // - url is not accessible
+ sendError(resp, HttpServletResponse.SC_FORBIDDEN);
+ } catch (SecurityException e) {
+ // SecurityException may indicate the following scenarios
+ // - url is not accessible
+ sendError(resp, HttpServletResponse.SC_FORBIDDEN);
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ return Boolean.TRUE;
+ }
+ }, acc);
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ }
+
+ void sendError(final HttpServletResponse resp, int sc) throws IOException {
+
+ try {
+ // we need to reset headers for 302 and 403
+ resp.reset();
+ resp.sendError(sc);
+ } catch (IllegalStateException e) {
+ // this could happen if the response has already been committed
+ }
+ }
+
+ int writeResourceToOutputStream(InputStream is, OutputStream os) throws IOException {
+ byte[] buffer = new byte[8192];
+ int bytesRead = is.read(buffer);
+ int writtenContentLength = 0;
+ while (bytesRead != -1) {
+ os.write(buffer, 0, bytesRead);
+ writtenContentLength += bytesRead;
+ bytesRead = is.read(buffer);
+ }
+ return writtenContentLength;
+ }
+
+ void writeResourceToWriter(InputStream is, Writer writer) throws IOException {
+ Reader reader = new InputStreamReader(is);
+ try {
+ char[] buffer = new char[8192];
+ int charsRead = reader.read(buffer);
+ while (charsRead != -1) {
+ writer.write(buffer, 0, charsRead);
+ charsRead = reader.read(buffer);
+ }
+ } finally {
+ if (reader != null) {
+ reader.close(); // will also close input stream
+ }
+ }
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletConfigImpl.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletConfigImpl.java
new file mode 100644
index 0000000000..51951e1476
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletConfigImpl.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import java.util.*;
+import javax.servlet.*;
+
+public class ServletConfigImpl implements ServletConfig {
+
+ private static final Dictionary EMPTY_PARAMS = new Hashtable(0);
+ private static final String SERVLET_NAME = "servlet-name"; //$NON-NLS-1$
+ private Servlet servlet;
+ private Dictionary initparams;
+ private ServletContext servletContext;
+
+ public ServletConfigImpl(Servlet servlet, Dictionary initparams, ServletContext servletContext) {
+ this.servlet = servlet;
+ this.initparams = (initparams != null) ? initparams : EMPTY_PARAMS;
+ this.servletContext = servletContext;
+ }
+
+ /*
+ * @see javax.servlet.ServletConfig#getServletName()
+ *
+ * The OSGi Http Service does not specify a way to set a servlet name at the API level. This
+ * implementation will try to use the value of the "servlet-name" initial parameter if available.
+ */
+ public String getServletName() {
+ String servletName = (String) initparams.get(SERVLET_NAME);
+ return (servletName != null) ? servletName : servlet.getClass().getName();
+ }
+
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
+
+ public String getInitParameter(String name) {
+ return (String) initparams.get(name);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return initparams.keys();
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletContextAdaptor.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletContextAdaptor.java
new file mode 100644
index 0000000000..ffdfa838b3
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletContextAdaptor.java
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servlet.internal;
+
+import java.io.*;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.*;
+import java.util.*;
+import javax.servlet.*;
+import org.osgi.service.http.HttpContext;
+
+public class ServletContextAdaptor implements ServletContext {
+
+ private ServletContext servletContext;
+ HttpContext httpContext;
+ private AccessControlContext acc;
+ private ProxyContext proxyContext;
+
+ public ServletContextAdaptor(ProxyContext proxyContext, ServletContext servletContext, HttpContext httpContext, AccessControlContext acc) {
+ this.servletContext = servletContext;
+ this.httpContext = httpContext;
+ this.acc = acc;
+ this.proxyContext = proxyContext;
+ }
+
+ /**
+ * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
+ *
+ * This method was added in the Servlet 2.3 API however the OSGi HttpService currently does not provide
+ * support for this method in the HttpContext interface. To support "getResourcePaths(...) this
+ * implementation uses reflection to check for and then call the associated HttpContext.getResourcePaths(...)
+ * method opportunistically. Null is returned if the method is not present or fails.
+ */
+ public Set getResourcePaths(String name) {
+ if (name == null || !name.startsWith("/")) //$NON-NLS-1$
+ return null;
+ try {
+ Method getResourcePathsMethod = httpContext.getClass().getMethod("getResourcePaths", new Class[] {String.class}); //$NON-NLS-1$
+ if (!getResourcePathsMethod.isAccessible())
+ getResourcePathsMethod.setAccessible(true);
+ return (Set) getResourcePathsMethod.invoke(httpContext, new Object[] {name});
+ } catch (Exception e) {
+ // ignore
+ }
+ return null;
+ }
+
+ public Object getAttribute(String attributeName) {
+ Dictionary attributes = proxyContext.getContextAttributes(httpContext);
+ return attributes.get(attributeName);
+ }
+
+ public Enumeration getAttributeNames() {
+ Dictionary attributes = proxyContext.getContextAttributes(httpContext);
+ return attributes.keys();
+ }
+
+ public void setAttribute(String attributeName, Object attributeValue) {
+ Dictionary attributes = proxyContext.getContextAttributes(httpContext);
+ attributes.put(attributeName, attributeValue);
+ }
+
+ public void removeAttribute(String attributeName) {
+ Dictionary attributes = proxyContext.getContextAttributes(httpContext);
+ attributes.remove(attributeName);
+ }
+
+ public String getMimeType(String name) {
+ String mimeType = httpContext.getMimeType(name);
+ return (mimeType != null) ? mimeType : servletContext.getMimeType(name);
+ }
+
+ public URL getResource(final String name) {
+ try {
+ return (URL) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public Object run() throws Exception {
+ return httpContext.getResource(name);
+ }
+ }, acc);
+ } catch (PrivilegedActionException e) {
+ log(e.getException().getMessage(), e.getException());
+ }
+ return null;
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ URL url = getResource(name);
+ if (url != null) {
+ try {
+ return url.openStream();
+ } catch (IOException e) {
+ log("Error opening stream for resource '" + name + "'", e); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ return null;
+ }
+
+ public ServletContext getContext(String arg0) {
+ return servletContext.getContext(arg0);
+ }
+
+ public String getInitParameter(String arg0) {
+ return servletContext.getInitParameter(arg0);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return servletContext.getInitParameterNames();
+ }
+
+ public int getMajorVersion() {
+ return servletContext.getMajorVersion();
+ }
+
+ public int getMinorVersion() {
+ return servletContext.getMinorVersion();
+ }
+
+ public RequestDispatcher getNamedDispatcher(String arg0) {
+ return new RequestDispatcherAdaptor(servletContext.getNamedDispatcher(arg0));
+ }
+
+ public String getRealPath(String arg0) {
+ return servletContext.getRealPath(arg0);
+ }
+
+ public RequestDispatcher getRequestDispatcher(String arg0) {
+ return new RequestDispatcherAdaptor(servletContext.getRequestDispatcher(proxyContext.getServletPath() + arg0));
+ }
+
+ public String getServerInfo() {
+ return servletContext.getServerInfo();
+ }
+
+ /**@deprecated*/
+ public Servlet getServlet(String arg0) throws ServletException {
+ return servletContext.getServlet(arg0);
+ }
+
+ public String getServletContextName() {
+ return servletContext.getServletContextName();
+ }
+
+ /**@deprecated*/
+ public Enumeration getServletNames() {
+ return servletContext.getServletNames();
+ }
+
+ /**@deprecated*/
+ public Enumeration getServlets() {
+ return servletContext.getServlets();
+ }
+
+ /**@deprecated*/
+ public void log(Exception arg0, String arg1) {
+ servletContext.log(arg0, arg1);
+ }
+
+ public void log(String arg0, Throwable arg1) {
+ servletContext.log(arg0, arg1);
+ }
+
+ public void log(String arg0) {
+ servletContext.log(arg0);
+ }
+
+ // Added in Servlet 2.5
+ public String getContextPath() {
+ try {
+ Method getContextPathMethod = servletContext.getClass().getMethod("getContextPath", null); //$NON-NLS-1$
+ return (String) getContextPathMethod.invoke(servletContext, null) + proxyContext.getServletPath();
+ } catch (Exception e) {
+ // ignore
+ }
+ return null;
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletRegistration.java b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletRegistration.java
new file mode 100644
index 0000000000..84cf5da32c
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletRegistration.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2005-2007 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.http.servlet.internal;
+
+import java.io.IOException;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.osgi.service.http.HttpContext;
+
+//This class wraps the servlet object registered in the HttpService.registerServlet call, to manage the context classloader when handleRequests are being asked.
+public class ServletRegistration extends Registration {
+
+ private Servlet servlet; //The actual servlet object registered against the http service. All requests will eventually be delegated to it.
+ private HttpContext httpContext; //The context used during the registration of the servlet
+ private ClassLoader registeredContextClassLoader;
+
+ public ServletRegistration(Servlet servlet, HttpContext context) {
+ this.servlet = servlet;
+ this.httpContext = context;
+ registeredContextClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ public void destroy() {
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(registeredContextClassLoader);
+ super.destroy();
+ servlet.destroy();
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ //Delegate the init call to the actual servlet
+ public void init(ServletConfig servletConfig) throws ServletException {
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(registeredContextClassLoader);
+ servlet.init(servletConfig);
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ //Delegate the handling of the request to the actual servlet
+ public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(registeredContextClassLoader);
+ if (httpContext.handleSecurity(req, resp))
+ servlet.service(req, resp);
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ public Servlet getServlet() {
+ return servlet;
+ }
+
+ public HttpContext getHttpContext() {
+ return httpContext;
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/.classpath b/org.adempiere.eclipse.equinox.http.servletbridge/.classpath
new file mode 100644
index 0000000000..ad32c83a78
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/.project b/org.adempiere.eclipse.equinox.http.servletbridge/.project
new file mode 100644
index 0000000000..7298accd65
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/.project
@@ -0,0 +1,28 @@
+
+
+ org.adempiere.eclipse.equinox.http.servletbridge
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.jdt.core.prefs b/org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..67adc84601
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Fri Dec 24 14:43:16 MYT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.pde.core.prefs b/org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000000..7c19d8aff0
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,4 @@
+#Fri Dec 24 14:43:16 MYT 2010
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/META-INF/MANIFEST.MF b/org.adempiere.eclipse.equinox.http.servletbridge/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..2e991ffa29
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0,J2SE-1.3
+Bundle-Name: %bundleName
+Bundle-SymbolicName: org.adempiere.eclipse.equinox.http.servletbridge;singleton:=true
+Eclipse-SourceReferences: scm:cvs:pserver:dev.eclipse.org:/cvsroot/rt:
+ org.eclipse.equinox/server-side/bundles/org.eclipse.equinox.http.serv
+ letbridge;tag=v20100503
+Bundle-Activator: org.eclipse.equinox.http.servletbridge.internal.Acti
+ vator
+Bundle-Version: 1.0.200.v20100503
+Import-Package: javax.servlet;version="2.3",javax.servlet.http;version
+ ="2.3",org.eclipse.equinox.http.servlet;version="1.0.0",org.eclipse.e
+ quinox.servletbridge;version="1.0.0",org.osgi.framework;version="1.3.
+ 0",org.osgi.service.http;version="1.2.0"
+Bundle-ManifestVersion: 2
+
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/about.html b/org.adempiere.eclipse.equinox.http.servletbridge/about.html
new file mode 100644
index 0000000000..d7e1cdf1e2
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/about.html
@@ -0,0 +1,28 @@
+
+
+
+
+About
+
+
+About This Content
+
+January 30, 2007
+License
+
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at http://www.eclipse.org/legal/epl-v10.html.
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
+
\ No newline at end of file
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/build.properties b/org.adempiere.eclipse.equinox.http.servletbridge/build.properties
new file mode 100644
index 0000000000..34d2e4d2da
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/plugin.properties b/org.adempiere.eclipse.equinox.http.servletbridge/plugin.properties
new file mode 100644
index 0000000000..f2626ac746
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/plugin.properties
@@ -0,0 +1,4 @@
+##Source Bundle Localization
+#Thu Jun 24 08:29:00 EDT 2010
+bundleName=Servletbridge Http Service Source
+providerName=Eclipse.org - Equinox
diff --git a/org.adempiere.eclipse.equinox.http.servletbridge/src/org/eclipse/equinox/http/servletbridge/internal/Activator.java b/org.adempiere.eclipse.equinox.http.servletbridge/src/org/eclipse/equinox/http/servletbridge/internal/Activator.java
new file mode 100644
index 0000000000..97d35f34f8
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.http.servletbridge/src/org/eclipse/equinox/http/servletbridge/internal/Activator.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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.http.servletbridge.internal;
+
+import org.eclipse.equinox.http.servlet.HttpServiceServlet;
+import org.eclipse.equinox.servletbridge.BridgeServlet;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+
+ private HttpServiceServlet httpServiceServlet;
+
+ public void start(BundleContext context) throws Exception {
+ httpServiceServlet = new HttpServiceServlet();
+ BridgeServlet.registerServletDelegate(httpServiceServlet);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ BridgeServlet.unregisterServletDelegate(httpServiceServlet);
+ httpServiceServlet = null;
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.servletbridge/.classpath b/org.adempiere.eclipse.equinox.servletbridge/.classpath
new file mode 100644
index 0000000000..ad32c83a78
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/org.adempiere.eclipse.equinox.servletbridge/.project b/org.adempiere.eclipse.equinox.servletbridge/.project
new file mode 100644
index 0000000000..817e9a17bd
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/.project
@@ -0,0 +1,28 @@
+
+
+ org.adempiere.eclipse.equinox.servletbridge
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.jdt.core.prefs b/org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..7d53f64ded
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Fri Dec 24 14:44:01 MYT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.pde.core.prefs b/org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000000..26dd37f080
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,4 @@
+#Fri Dec 24 14:44:01 MYT 2010
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/org.adempiere.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF b/org.adempiere.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..df7becd3c6
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0,J2SE-1.3
+Bundle-Name: %bundleName
+Bundle-SymbolicName: org.adempiere.eclipse.equinox.servletbridge;singleton:=true
+Eclipse-SourceReferences: scm:cvs:pserver:dev.eclipse.org:/cvsroot/rt:
+ org.eclipse.equinox/server-side/bundles/org.eclipse.equinox.servletbr
+ idge;tag=v20100503
+Bundle-Version: 1.2.0.v20100503
+Export-Package: org.eclipse.equinox.servletbridge;version="1.1.0"
+Import-Package: javax.servlet;version="2.3.0",javax.servlet.http;versi
+ on="2.3.0"
+Bundle-ManifestVersion: 2
diff --git a/org.adempiere.eclipse.equinox.servletbridge/about.html b/org.adempiere.eclipse.equinox.servletbridge/about.html
new file mode 100644
index 0000000000..d7e1cdf1e2
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/about.html
@@ -0,0 +1,28 @@
+
+
+
+
+About
+
+
+About This Content
+
+January 30, 2007
+License
+
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at http://www.eclipse.org/legal/epl-v10.html.
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
+
\ No newline at end of file
diff --git a/org.adempiere.eclipse.equinox.servletbridge/build.properties b/org.adempiere.eclipse.equinox.servletbridge/build.properties
new file mode 100644
index 0000000000..34d2e4d2da
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/org.adempiere.eclipse.equinox.servletbridge/plugin.properties b/org.adempiere.eclipse.equinox.servletbridge/plugin.properties
new file mode 100644
index 0000000000..8390460842
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/plugin.properties
@@ -0,0 +1,4 @@
+##Source Bundle Localization
+#Thu Jun 24 08:29:00 EDT 2010
+bundleName=Servletbridge Source
+providerName=Eclipse.org - Equinox
diff --git a/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeFilter.java b/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeFilter.java
new file mode 100644
index 0000000000..4e19787ea6
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeFilter.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Angelo Zerr 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:
+ * Angelo Zerr - give the capability to use resources (JSP, HTML, Servlet...)
+ * from the Bridge webapp with HTTP Filter.
+ *******************************************************************************/
+package org.eclipse.equinox.servletbridge;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * {@link BridgeServlet} which implements HTTP {@link Filter} to declare "BridgeServlet" as an HTTP Filter (see >=2.3 Servlet spec):
+ *
+ *
+ *
+ * equinoxbridgeservlet
+ * org.eclipse.equinox.servletbridge.BridgeFilter
+ *
+ * ...
+ *
+ equinoxbridgeservlet
+ /*
+
+ *
+ *
+ */
+public class BridgeFilter extends BridgeServlet implements Filter {
+
+ private static final long serialVersionUID = 1309373924501049438L;
+
+ private ServletConfigAdaptor servletConfig;
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ this.servletConfig = new ServletConfigAdaptor(filterConfig);
+ super.init();
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse resp = (HttpServletResponse) response;
+ // Call process class with FilterChain.
+ super.process(req, resp, chain);
+ }
+
+ public ServletConfig getServletConfig() {
+ return servletConfig;
+ }
+
+ /**
+ *
+ * Class which adapt {@link FilterConfig} to a {@link ServletConfig}.
+ *
+ */
+ private static class ServletConfigAdaptor implements ServletConfig {
+
+ private FilterConfig filterConfig;
+
+ public ServletConfigAdaptor(FilterConfig filterConfig) {
+ this.filterConfig = filterConfig;
+ }
+
+ public String getInitParameter(String arg0) {
+ return filterConfig.getInitParameter(arg0);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return filterConfig.getInitParameterNames();
+ }
+
+ public ServletContext getServletContext() {
+ return filterConfig.getServletContext();
+ }
+
+ public String getServletName() {
+ return filterConfig.getFilterName();
+ }
+
+ }
+}
diff --git a/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeServlet.java b/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeServlet.java
new file mode 100644
index 0000000000..177b467a4c
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeServlet.java
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * 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
+ * Angelo Zerr - give the capability to use resources (JSP, HTML, Servlet...)
+ * from the Bridge webapp with HTTP Filter.
+ *******************************************************************************/
+package org.eclipse.equinox.servletbridge;
+
+import java.io.IOException;
+import javax.servlet.*;
+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 static HttpServlet servletDelegateInstance;
+ private HttpServlet delegate;
+ // true if current HttpServlet is an HTTP Filter and false otherwise.
+ private boolean delegateIsFilter;
+ private int delegateReferenceCount;
+
+ /**
+ * 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();
+ setInstance(this);
+ }
+
+ /**
+ * destroy() is called by the Servlet Container and used to first stop and then destroy the framework.
+ */
+ public void 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 {
+ process(req, resp, null);
+ }
+
+ /**
+ * Search from OSGi registry the servlet, ressources... to executed according the path of the HTTP request. If no service are founded :
+ *
+ * - if {@link FilterChain} is not null : {@link FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} is called.
+ * - otherwise throw 404 error.
+ *
+ *
+ * @param req
+ * @param resp
+ * @param chain
+ * @throws ServletException
+ * @throws IOException
+ */
+ protected void process(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws ServletException, IOException {
+ String pathInfo = getDispatchPathInfo(req, chain);
+ // 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) {
+ } 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);
+ }
+ }
+
+ HttpServlet servletReference = acquireDelegateReference();
+ if (servletReference == null) {
+ // Cannot find the HttpServletService from OSGi registry services :
+ if (chain != null) {
+ // Delegate the HTTP request to the Web server container.
+ chain.doFilter(req, resp);
+
+ } else {
+ // Send HTTP 4040 Error.
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "BridgeServlet: " + req.getRequestURI()); //$NON-NLS-1$
+ }
+ } else {
+ try {
+ if (delegateIsFilter && chain != null) {
+ ((Filter) servletReference).doFilter(req, resp, chain);
+ } else {
+ servletReference.service(req, resp);
+ }
+ } finally {
+ releaseDelegateReference();
+ }
+ }
+ }
+
+ 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;
+ }
+
+ private static synchronized void setInstance(BridgeServlet servlet) {
+ if ((instance != null) && (servlet != null))
+ throw new IllegalStateException("instance already set"); //$NON-NLS-1$
+ instance = servlet;
+ if (instance == null)
+ servletDelegateInstance = null;
+ else if (servletDelegateInstance != null)
+ registerServletDelegate(servletDelegateInstance);
+ }
+
+ 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) {
+ servletDelegateInstance = servletDelegate;
+ return;
+ }
+
+ servletDelegateInstance = null;
+
+ 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 {
+ // cache the flag if HttpServlet servlet delegate is an HTTP Filter.
+ instance.delegateIsFilter = (servletDelegate instanceof Filter);
+ // initialize the servlet delegate.
+ 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;
+ instance.delegateIsFilter = false;
+ 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);
+ }
+ }
+
+ /**
+ * Returns the path info of the HTTP request.
+ *
+ * @param req
+ * @param chain
+ * @return the path info of the HTTP request.
+ */
+ private static String getDispatchPathInfo(HttpServletRequest req, FilterChain chain) {
+ // if (req.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null)
+ // return (String) req.getAttribute(INCLUDE_PATH_INFO_ATTRIBUTE);
+
+ String pathInfo = req.getPathInfo();
+ if (pathInfo != null) {
+ return pathInfo;
+ }
+ if (chain == null) {
+ return null;
+ }
+
+ // HTTP Filter case, pathInfo is null, compute it by using contextPath and requestURI
+ String contextPath = req.getContextPath();
+ String requestURI = req.getRequestURI();
+ return requestURI.substring(contextPath.length(), requestURI.length());
+ }
+
+}
\ No newline at end of file
diff --git a/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java b/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java
new file mode 100644
index 0000000000..f29efbf4ba
--- /dev/null
+++ b/org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java
@@ -0,0 +1,391 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+/**
+ * The java.net.URLClassLoader class allows one to load resources from arbitrary URLs and in particular is optimized to handle
+ * "jar" URLs. Unfortunately for jar files this optimization ends up holding the file open which ultimately prevents the file from
+ * being deleted or update until the VM is shutdown.
+ *
+ * The CloseableURLClassLoader is meant to replace the URLClassLoader and provides an additional method to allow one to "close" any
+ * resources left open. In the current version the CloseableURLClassLoader will only ensure the closing of jar file resources. The
+ * jar handling behavior in this class will also provides a construct to allow one to turn off jar file verification in performance
+ * sensitive situations where the verification us not necessary.
+ *
+ * also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=190279
+ */
+
+package org.eclipse.equinox.servletbridge;
+
+import java.io.*;
+import java.net.*;
+import java.security.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.jar.Attributes.Name;
+
+public class CloseableURLClassLoader extends URLClassLoader {
+ static final String DOT_CLASS = ".class"; //$NON-NLS-1$
+ static final String BANG_SLASH = "!/"; //$NON-NLS-1$
+ static final String JAR = "jar"; //$NON-NLS-1$
+
+ // @GuardedBy("loaders")
+ final ArrayList loaders = new ArrayList(); // package private to avoid synthetic access.
+ // @GuardedBy("loaders")
+ private final ArrayList loaderURLs = new ArrayList(); // note: protected by loaders
+ // @GuardedBy("loaders")
+ boolean closed = false; // note: protected by loaders, package private to avoid synthetic access.
+
+ private final AccessControlContext context;
+ private final boolean verifyJars;
+
+ private static class CloseableJarURLConnection extends JarURLConnection {
+ private final JarFile jarFile;
+ // @GuardedBy("this")
+ private JarEntry entry;
+
+ public CloseableJarURLConnection(URL url, JarFile jarFile) throws MalformedURLException {
+ super(url);
+ this.jarFile = jarFile;
+ }
+
+ public void connect() throws IOException {
+ internalGetEntry();
+ }
+
+ private synchronized JarEntry internalGetEntry() throws IOException {
+ if (entry != null)
+ return entry;
+ entry = jarFile.getJarEntry(getEntryName());
+ if (entry == null)
+ throw new FileNotFoundException(getEntryName());
+ return entry;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return jarFile.getInputStream(internalGetEntry());
+ }
+
+ /**
+ * @throws IOException
+ * Documented to avoid warning
+ */
+ public JarFile getJarFile() throws IOException {
+ return jarFile;
+ }
+
+ public JarEntry getJarEntry() throws IOException {
+ return internalGetEntry();
+ }
+ }
+
+ private static class CloseableJarURLStreamHandler extends URLStreamHandler {
+ private final JarFile jarFile;
+
+ public CloseableJarURLStreamHandler(JarFile jarFile) {
+ this.jarFile = jarFile;
+ }
+
+ protected URLConnection openConnection(URL u) throws IOException {
+ return new CloseableJarURLConnection(u, jarFile);
+ }
+
+ protected void parseURL(URL u, String spec, int start, int limit) {
+ setURL(u, JAR, null, 0, null, null, spec.substring(start, limit), null, null);
+ }
+ }
+
+ private static class CloseableJarFileLoader {
+ private final JarFile jarFile;
+ private final Manifest manifest;
+ private final CloseableJarURLStreamHandler jarURLStreamHandler;
+ private final String jarFileURLPrefixString;
+
+ public CloseableJarFileLoader(File file, boolean verify) throws IOException {
+ this.jarFile = new JarFile(file, verify);
+ this.manifest = jarFile.getManifest();
+ this.jarURLStreamHandler = new CloseableJarURLStreamHandler(jarFile);
+ this.jarFileURLPrefixString = file.toURL().toString() + BANG_SLASH;
+ }
+
+ public URL getURL(String name) {
+ if (jarFile.getEntry(name) != null)
+ try {
+ return new URL(JAR, null, -1, jarFileURLPrefixString + name, jarURLStreamHandler);
+ } catch (MalformedURLException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ public Manifest getManifest() {
+ return manifest;
+ }
+
+ public void close() {
+ try {
+ jarFile.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * @param urls the array of URLs to use for loading resources
+ * @see URLClassLoader
+ */
+ public CloseableURLClassLoader(URL[] urls) {
+ this(urls, ClassLoader.getSystemClassLoader(), true);
+ }
+
+ /**
+ * @param urls the URLs from which to load classes and resources
+ * @param parent the parent class loader used for delegation
+ * @see URLClassLoader
+ */
+ public CloseableURLClassLoader(URL[] urls, ClassLoader parent) {
+ this(excludeFileJarURLS(urls), parent, true);
+ }
+
+ /**
+ * @param urls the URLs from which to load classes and resources
+ * @param parent the parent class loader used for delegation
+ * @param verifyJars flag to determine if jar file verification should be performed
+ * @see URLClassLoader
+ */
+ public CloseableURLClassLoader(URL[] urls, ClassLoader parent, boolean verifyJars) {
+ super(excludeFileJarURLS(urls), parent);
+ this.context = AccessController.getContext();
+ this.verifyJars = verifyJars;
+ for (int i = 0; i < urls.length; i++) {
+ if (isFileJarURL(urls[i])) {
+ loaderURLs.add(urls[i]);
+ safeAddLoader(urls[i]);
+ }
+ }
+ }
+
+ // @GuardedBy("loaders")
+ private void safeAddLoader(URL url) {
+ String path = url.getPath();
+ File file = new File(path);
+ if (file.exists()) {
+ try {
+ loaders.add(new CloseableJarFileLoader(file, verifyJars));
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ private static URL[] excludeFileJarURLS(URL[] urls) {
+ ArrayList urlList = new ArrayList();
+ for (int i = 0; i < urls.length; i++) {
+ if (!isFileJarURL(urls[i]))
+ urlList.add(urls[i]);
+ }
+ return (URL[]) urlList.toArray(new URL[urlList.size()]);
+ }
+
+ private static boolean isFileJarURL(URL url) {
+ if (!url.getProtocol().equals("file")) //$NON-NLS-1$
+ return false;
+
+ String path = url.getPath();
+ if (path != null && path.endsWith("/")) //$NON-NLS-1$
+ return false;
+
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see java.net.URLClassLoader#findClass(java.lang.String)
+ */
+ protected Class findClass(final String name) throws ClassNotFoundException {
+ try {
+ Class clazz = (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public Object run() throws ClassNotFoundException {
+ String resourcePath = name.replace('.', '/') + DOT_CLASS;
+ CloseableJarFileLoader loader = null;
+ URL resourceURL = null;
+ synchronized (loaders) {
+ if (closed)
+ return null;
+ for (Iterator iterator = loaders.iterator(); iterator.hasNext();) {
+ loader = (CloseableJarFileLoader) iterator.next();
+ resourceURL = loader.getURL(resourcePath);
+ if (resourceURL != null)
+ break;
+ }
+ }
+ if (resourceURL != null) {
+ try {
+ return defineClass(name, resourceURL, loader.getManifest());
+ } catch (IOException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ }
+ return null;
+ }
+ }, context);
+ if (clazz != null)
+ return clazz;
+ } catch (PrivilegedActionException e) {
+ throw (ClassNotFoundException) e.getException();
+ }
+ return super.findClass(name);
+ }
+
+ // package private to avoid synthetic access.
+ Class defineClass(String name, URL resourceURL, Manifest manifest) throws IOException {
+ JarURLConnection connection = (JarURLConnection) resourceURL.openConnection();
+ int lastDot = name.lastIndexOf('.');
+ if (lastDot != -1) {
+ String packageName = name.substring(0, lastDot + 1);
+ Package pkg = getPackage(packageName);
+ if (pkg != null) {
+ checkForSealedPackage(pkg, packageName, manifest, connection.getJarFileURL());
+ } else {
+ definePackage(packageName, manifest, connection.getJarFileURL());
+ }
+ }
+ JarEntry entry = connection.getJarEntry();
+ byte[] bytes = new byte[(int) entry.getSize()];
+ DataInputStream is = null;
+ try {
+ is = new DataInputStream(connection.getInputStream());
+ is.readFully(bytes, 0, bytes.length);
+ CodeSource cs = new CodeSource(connection.getJarFileURL(), entry.getCertificates());
+ return defineClass(name, bytes, 0, bytes.length, cs);
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ private void checkForSealedPackage(Package pkg, String packageName, Manifest manifest, URL jarFileURL) {
+ if (pkg.isSealed() && !pkg.isSealed(jarFileURL))
+ throw new SecurityException("The package '" + packageName + "' was previously loaded and is already sealed."); //$NON-NLS-1$ //$NON-NLS-2$
+
+ String entryPath = packageName.replace('.', '/') + "/"; //$NON-NLS-1$
+ Attributes entryAttributes = manifest.getAttributes(entryPath);
+ String sealed = null;
+ if (entryAttributes != null)
+ sealed = entryAttributes.getValue(Name.SEALED);
+
+ if (sealed == null) {
+ Attributes mainAttributes = manifest.getMainAttributes();
+ if (mainAttributes != null)
+ sealed = mainAttributes.getValue(Name.SEALED);
+ }
+ if (Boolean.valueOf(sealed).booleanValue())
+ throw new SecurityException("The package '" + packageName + "' was previously loaded unsealed. Cannot seal package."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /* (non-Javadoc)
+ * @see java.net.URLClassLoader#findResource(java.lang.String)
+ */
+ public URL findResource(final String name) {
+ URL url = (URL) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ synchronized (loaders) {
+ if (closed)
+ return null;
+ for (Iterator iterator = loaders.iterator(); iterator.hasNext();) {
+ CloseableJarFileLoader loader = (CloseableJarFileLoader) iterator.next();
+ URL resourceURL = loader.getURL(name);
+ if (resourceURL != null)
+ return resourceURL;
+ }
+ }
+ return null;
+ }
+ }, context);
+ if (url != null)
+ return url;
+ return super.findResource(name);
+ }
+
+ /* (non-Javadoc)
+ * @see java.net.URLClassLoader#findResources(java.lang.String)
+ */
+ public Enumeration findResources(final String name) throws IOException {
+ final List resources = new ArrayList();
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ synchronized (loaders) {
+ if (closed)
+ return null;
+ for (Iterator iterator = loaders.iterator(); iterator.hasNext();) {
+ CloseableJarFileLoader loader = (CloseableJarFileLoader) iterator.next();
+ URL resourceURL = loader.getURL(name);
+ if (resourceURL != null)
+ resources.add(resourceURL);
+ }
+ }
+ return null;
+ }
+ }, context);
+ Enumeration e = super.findResources(name);
+ while (e.hasMoreElements())
+ resources.add(e.nextElement());
+
+ return Collections.enumeration(resources);
+ }
+
+ /**
+ * The "close" method is called when the class loader is no longer needed and we should close any open resources.
+ * In particular this method will close the jar files associated with this class loader.
+ */
+ public void close() {
+ synchronized (loaders) {
+ if (closed)
+ return;
+ for (Iterator iterator = loaders.iterator(); iterator.hasNext();) {
+ CloseableJarFileLoader loader = (CloseableJarFileLoader) iterator.next();
+ loader.close();
+ }
+ closed = true;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.net.URLClassLoader#addURL(java.net.URL)
+ */
+ protected void addURL(URL url) {
+ synchronized (loaders) {
+ if (isFileJarURL(url)) {
+ if (closed)
+ throw new IllegalStateException("Cannot add url. CloseableURLClassLoader is closed."); //$NON-NLS-1$
+ loaderURLs.add(url);
+ safeAddLoader(url);
+ return;
+ }
+ }
+ super.addURL(url);
+ }
+
+ /* (non-Javadoc)
+ * @see java.net.URLClassLoader#getURLs()
+ */
+ public URL[] getURLs() {
+ List result = new ArrayList();
+ synchronized (loaders) {
+ result.addAll(loaderURLs);
+ }
+ result.addAll(Arrays.asList(super.getURLs()));
+ return (URL[]) result.toArray(new URL[result.size()]);
+ }
+}