From cc5708c8d004d5e93625361bed83e8218c7e52ed Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Tue, 11 Jan 2011 01:00:26 +0800 Subject: [PATCH] Added patch equinox servlet bridge: 1) Filter support from http://angelozerr.wordpress.com/2010/09/08/osgi-equinox-in-a-servlet-container-step4/ 2) Modify to work with OSGi web container ( geminiweb ) instead of normal servlet container --- .../.classpath | 7 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 8 + .../.settings/org.eclipse.pde.core.prefs | 4 + .../META-INF/MANIFEST.MF | 20 + .../about.html | 28 ++ .../build.properties | 4 + .../plugin.properties | 4 + .../http/servlet/ExtendedHttpService.java | 55 +++ .../http/servlet/HttpServiceServlet.java | 25 ++ .../http/servlet/internal/Activator.java | 88 ++++ .../servlet/internal/DefaultHttpContext.java | 42 ++ .../servlet/internal/FilterChainImpl.java | 30 ++ .../servlet/internal/FilterConfigImpl.java | 52 +++ .../servlet/internal/FilterRegistration.java | 112 +++++ .../servlet/internal/HttpServiceFactory.java | 35 ++ .../servlet/internal/HttpServiceImpl.java | 119 ++++++ .../internal/HttpServletRequestAdaptor.java | 194 +++++++++ .../servlet/internal/HttpSessionAdaptor.java | 103 +++++ .../http/servlet/internal/ProxyContext.java | 135 ++++++ .../http/servlet/internal/ProxyServlet.java | 320 ++++++++++++++ .../http/servlet/internal/Registration.java | 45 ++ .../internal/RequestDispatcherAdaptor.java | 39 ++ .../servlet/internal/ResourceServlet.java | 188 +++++++++ .../servlet/internal/ServletConfigImpl.java | 53 +++ .../internal/ServletContextAdaptor.java | 186 +++++++++ .../servlet/internal/ServletRegistration.java | 74 ++++ .../.classpath | 7 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 8 + .../.settings/org.eclipse.pde.core.prefs | 4 + .../META-INF/MANIFEST.MF | 18 + .../about.html | 28 ++ .../build.properties | 4 + .../plugin.properties | 4 + .../servletbridge/internal/Activator.java | 32 ++ .../.classpath | 7 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 8 + .../.settings/org.eclipse.pde.core.prefs | 4 + .../META-INF/MANIFEST.MF | 14 + .../about.html | 28 ++ .../build.properties | 4 + .../plugin.properties | 4 + .../equinox/servletbridge/BridgeFilter.java | 89 ++++ .../equinox/servletbridge/BridgeServlet.java | 279 +++++++++++++ .../CloseableURLClassLoader.java | 391 ++++++++++++++++++ 47 files changed, 2987 insertions(+) create mode 100644 org.adempiere.eclipse.equinox.http.servlet/.classpath create mode 100644 org.adempiere.eclipse.equinox.http.servlet/.project create mode 100644 org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.jdt.core.prefs create mode 100644 org.adempiere.eclipse.equinox.http.servlet/.settings/org.eclipse.pde.core.prefs create mode 100644 org.adempiere.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF create mode 100644 org.adempiere.eclipse.equinox.http.servlet/about.html create mode 100644 org.adempiere.eclipse.equinox.http.servlet/build.properties create mode 100644 org.adempiere.eclipse.equinox.http.servlet/plugin.properties create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/ExtendedHttpService.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceServlet.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Activator.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultHttpContext.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterChainImpl.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterConfigImpl.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/FilterRegistration.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceFactory.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServletRequestAdaptor.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpSessionAdaptor.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyContext.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ProxyServlet.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/Registration.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/RequestDispatcherAdaptor.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ResourceServlet.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletConfigImpl.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletContextAdaptor.java create mode 100644 org.adempiere.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/ServletRegistration.java create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/.classpath create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/.project create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.jdt.core.prefs create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/.settings/org.eclipse.pde.core.prefs create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/META-INF/MANIFEST.MF create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/about.html create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/build.properties create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/plugin.properties create mode 100644 org.adempiere.eclipse.equinox.http.servletbridge/src/org/eclipse/equinox/http/servletbridge/internal/Activator.java create mode 100644 org.adempiere.eclipse.equinox.servletbridge/.classpath create mode 100644 org.adempiere.eclipse.equinox.servletbridge/.project create mode 100644 org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.jdt.core.prefs create mode 100644 org.adempiere.eclipse.equinox.servletbridge/.settings/org.eclipse.pde.core.prefs create mode 100644 org.adempiere.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF create mode 100644 org.adempiere.eclipse.equinox.servletbridge/about.html create mode 100644 org.adempiere.eclipse.equinox.servletbridge/build.properties create mode 100644 org.adempiere.eclipse.equinox.servletbridge/plugin.properties create mode 100644 org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeFilter.java create mode 100644 org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/BridgeServlet.java create mode 100644 org.adempiere.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java 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()]); + } +}