| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.osgi.boot.internal.webapp; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.Field; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.jar.JarFile; |
| |
| import javax.servlet.http.HttpServlet; |
| |
| import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelperFactory; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| import org.eclipse.jetty.util.resource.Resource; |
| import org.eclipse.jetty.webapp.WebAppClassLoader; |
| import org.eclipse.jetty.webapp.WebAppContext; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleReference; |
| |
| /** |
| * OSGiWebappClassLoader |
| * |
| * |
| * Extends the webapp classloader to also use the classloader of the Bundle defining the webapp. |
| */ |
| public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleReference |
| { |
| |
| private static final Logger __logger = Log.getLogger(OSGiWebappClassLoader.class.getName()); |
| |
| /** |
| * when a logging framework is setup in the osgi classloaders, it can access |
| * this and register the classes that must not be found in the jar. |
| */ |
| public static final Set<String> JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED = new HashSet<String>(); |
| |
| public static void addClassThatIdentifiesAJarThatMustBeRejected(Class<?> zclass) |
| { |
| JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclass.getName().replace('.', '/') + ".class"); |
| } |
| |
| public static void addClassThatIdentifiesAJarThatMustBeRejected(String zclassName) |
| { |
| JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclassName.replace('.', '/') + ".class"); |
| } |
| |
| static |
| { |
| addClassThatIdentifiesAJarThatMustBeRejected(HttpServlet.class); |
| } |
| |
| private ClassLoader _osgiBundleClassLoader; |
| |
| private Bundle _contributor; |
| |
| private boolean _lookInOsgiFirst = true; |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param parent The parent classloader. |
| * @param context The WebAppContext |
| * @param contributor The bundle that defines this web-application. |
| * @throws IOException if unable to cerate the OSGiWebappClassLoader |
| */ |
| public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor) |
| throws IOException |
| { |
| super(parent, context); |
| _contributor = contributor; |
| _osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(contributor); |
| } |
| |
| |
| |
| @Override |
| public Class<?> loadClass(String name) throws ClassNotFoundException |
| { |
| return super.loadClass(name); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Returns the <code>Bundle</code> that defined this web-application. |
| * |
| * @return The <code>Bundle</code> object associated with this |
| * <code>BundleReference</code>. |
| */ |
| public Bundle getBundle() |
| { |
| return _contributor; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public Enumeration<URL> getResources(String name) throws IOException |
| { |
| Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name); |
| Enumeration<URL> urls = super.getResources(name); |
| if (_lookInOsgiFirst) |
| { |
| return Collections.enumeration(toList(osgiUrls, urls)); |
| } |
| else |
| { |
| return Collections.enumeration(toList(urls, osgiUrls)); |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public URL getResource(String name) |
| { |
| if (_lookInOsgiFirst) |
| { |
| URL url = _osgiBundleClassLoader.getResource(name); |
| return url != null ? url : super.getResource(name); |
| } |
| else |
| { |
| URL url = super.getResource(name); |
| return url != null ? url : _osgiBundleClassLoader.getResource(name); |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------ */ |
| private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2) |
| { |
| List<URL> list = new ArrayList<URL>(); |
| while (e != null && e.hasMoreElements()) |
| list.add(e.nextElement()); |
| while (e2 != null && e2.hasMoreElements()) |
| list.add(e2.nextElement()); |
| return list; |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| protected Class<?> findClass(String name) throws ClassNotFoundException |
| { |
| try |
| { |
| return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name); |
| } |
| catch (ClassNotFoundException cne) |
| { |
| try |
| { |
| return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name); |
| } |
| catch (ClassNotFoundException cne2) |
| { |
| throw cne; |
| } |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Parse the classpath ourselves to be able to filter things. This is a |
| * derivative work of the super class |
| */ |
| @Override |
| public void addClassPath(String classPath) throws IOException |
| { |
| |
| StringTokenizer tokenizer = new StringTokenizer(classPath, ",;"); |
| while (tokenizer.hasMoreTokens()) |
| { |
| String path = tokenizer.nextToken(); |
| Resource resource = getContext().newResource(path); |
| |
| // Resolve file path if possible |
| File file = resource.getFile(); |
| if (file != null && isAcceptableLibrary(file, JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED)) |
| { |
| super.addClassPath(path); |
| } |
| else |
| { |
| __logger.info("Did not add " + path + " to the classloader of the webapp " + getContext()); |
| } |
| } |
| |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param lib |
| * @return true if the lib should be included in the webapp classloader. |
| */ |
| private boolean isAcceptableLibrary(File file, Set<String> pathToClassFiles) |
| { |
| try |
| { |
| if (file.isDirectory()) |
| { |
| for (String criteria : pathToClassFiles) |
| { |
| if (new File(file, criteria).exists()) { return false; } |
| } |
| } |
| else |
| { |
| JarFile jar = null; |
| try |
| { |
| jar = new JarFile(file); |
| for (String criteria : pathToClassFiles) |
| { |
| if (jar.getEntry(criteria) != null) { return false; } |
| } |
| } |
| finally |
| { |
| if (jar != null) try |
| { |
| jar.close(); |
| } |
| catch (IOException ioe) |
| { |
| } |
| } |
| } |
| } |
| catch (IOException e) |
| { |
| // nevermind. just trying our best |
| __logger.ignore(e); |
| } |
| return true; |
| } |
| |
| private static Field _contextField; |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * In the case of the generation of a webapp via a jetty context file we |
| * need a proper classloader to setup the app before we have the |
| * WebappContext So we place a fake one there to start with. We replace it |
| * with the actual webapp context with this method. We also apply the |
| * extraclasspath there at the same time. |
| * @param webappContext the web app context |
| */ |
| public void setWebappContext(WebAppContext webappContext) |
| { |
| try |
| { |
| if (_contextField == null) |
| { |
| _contextField = WebAppClassLoader.class.getDeclaredField("_context"); |
| _contextField.setAccessible(true); |
| } |
| _contextField.set(this, webappContext); |
| if (webappContext.getExtraClasspath() != null) |
| { |
| addClassPath(webappContext.getExtraClasspath()); |
| } |
| } |
| catch (Throwable t) |
| { |
| // humf that will hurt if it does not work. |
| __logger.warn("Unable to set webappcontext", t); |
| } |
| } |
| } |