diff options
author | Jan Bartel | 2013-09-26 08:26:13 +0000 |
---|---|---|
committer | Jan Bartel | 2013-09-30 03:27:05 +0000 |
commit | 04002898263b3d408f7f4851906dfcb7185902ee (patch) | |
tree | 47886b39b850fb96f7c1a5872eb964556590001b | |
parent | 0fd656dcbe0102c7e9deecaac9ac4de1a5016c62 (diff) | |
download | org.eclipse.jetty.project-04002898263b3d408f7f4851906dfcb7185902ee.tar.gz org.eclipse.jetty.project-04002898263b3d408f7f4851906dfcb7185902ee.tar.xz org.eclipse.jetty.project-04002898263b3d408f7f4851906dfcb7185902ee.zip |
417561 Refactor annotation related code - make annotation phase able to be multithreaded
24 files changed, 633 insertions, 401 deletions
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java index b46a053a6e..f81110252c 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.annotations; -import java.util.List; - import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler; import org.eclipse.jetty.webapp.DiscoveredAnnotation; import org.eclipse.jetty.webapp.WebAppContext; @@ -37,15 +35,9 @@ public abstract class AbstractDiscoverableAnnotationHandler extends AbstractHand public AbstractDiscoverableAnnotationHandler(WebAppContext context) { - this(context, null); - } - - public AbstractDiscoverableAnnotationHandler(WebAppContext context, List<DiscoveredAnnotation> list) - { _context = context; - } - + public void addAnnotation (DiscoveredAnnotation a) { diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java index f7f17b25ab..d38673c65e 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java @@ -20,18 +20,28 @@ package org.eclipse.jetty.annotations; import java.net.URI; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import javax.servlet.ServletContainerInitializer; import javax.servlet.annotation.HandlesTypes; +import org.eclipse.jetty.annotations.AnnotationParser.Handler; import org.eclipse.jetty.plus.annotation.ContainerInitializer; -import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.ConcurrentHashSet; +import org.eclipse.jetty.util.MultiException; 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.util.thread.QueuedThreadPool; import org.eclipse.jetty.webapp.AbstractConfiguration; import org.eclipse.jetty.webapp.FragmentDescriptor; import org.eclipse.jetty.webapp.MetaDataComplete; @@ -46,16 +56,50 @@ import org.eclipse.jetty.webapp.WebDescriptor; public class AnnotationConfiguration extends AbstractConfiguration { private static final Logger LOG = Log.getLogger(AnnotationConfiguration.class); + public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap"; public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers"; public static final String CONTAINER_INITIALIZER_STARTER = "org.eclipse.jetty.containerInitializerStarter"; - + public static final String MULTI_THREADED = "org.eclipse.jetty.annotations.multiThreaded"; + public static final String MAX_SCAN_WAIT = "org.eclipse.jetty.annotations.maxWait"; + + public static final int DEFAULT_MAX_SCAN_WAIT = 60; /* time in sec */ protected List<AbstractDiscoverableAnnotationHandler> _discoverableAnnotationHandlers = new ArrayList<AbstractDiscoverableAnnotationHandler>(); protected ClassInheritanceHandler _classInheritanceHandler; protected List<ContainerInitializerAnnotationHandler> _containerInitializerAnnotationHandlers = new ArrayList<ContainerInitializerAnnotationHandler>(); + protected List<ParserTask> _parserTasks; + protected WebAppClassNameResolver _webAppClassNameResolver; + protected ContainerClassNameResolver _containerClassNameResolver; + + + public class ParserTask implements Callable<Void> + { + protected Exception _exception; + protected final AnnotationParser _parser; + protected final Set<? extends Handler> _handlers; + protected final ClassNameResolver _resolver; + protected final Resource _resource; + + + public ParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource, ClassNameResolver resolver) + { + _parser = parser; + _handlers = handlers; + _resolver = resolver; + _resource = resource; + } + + public Void call() throws Exception + { + if (_parser != null) + _parser.parse(_handlers, _resource, _resolver); + return null; + } + } + /** * WebAppClassNameResolver * @@ -134,6 +178,8 @@ public class AnnotationConfiguration extends AbstractConfiguration @Override public void preConfigure(final WebAppContext context) throws Exception { + _webAppClassNameResolver = new WebAppClassNameResolver(context); + _containerClassNameResolver = new ContainerClassNameResolver(context); } @@ -166,7 +212,7 @@ public class AnnotationConfiguration extends AbstractConfiguration //Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any - AnnotationParser parser = null; + if (!metadataComplete) { //If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations @@ -187,18 +233,8 @@ public class AnnotationConfiguration extends AbstractConfiguration createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context)); if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) - { - parser = createAnnotationParser(); - if (LOG.isDebugEnabled()) LOG.debug("Scanning all classses for annotations: webxmlVersion="+context.getServletContext().getEffectiveMajorVersion()+" configurationDiscovered="+context.isConfigurationDiscovered()); - parseContainerPath(context, parser); - //email from Rajiv Mordani jsrs 315 7 April 2010 - // If there is a <others/> then the ordering should be - // WEB-INF/classes the order of the declared elements + others. - // In case there is no others then it is - // WEB-INF/classes + order of the elements. - parseWebInfClasses(context, parser); - parseWebInfLib (context, parser); - } + scanForAnnotations(context); + } @@ -209,7 +245,7 @@ public class AnnotationConfiguration extends AbstractConfiguration @Override public void postConfigure(WebAppContext context) throws Exception { - MultiMap<String> classMap = (MultiMap<String>)context.getAttribute(CLASS_INHERITANCE_MAP); + ConcurrentHashMap<String, ConcurrentHashSet<String>> classMap = (ConcurrentHashMap<String, ConcurrentHashSet<String>>)context.getAttribute(CLASS_INHERITANCE_MAP); List<ContainerInitializer> initializers = (List<ContainerInitializer>)context.getAttribute(CONTAINER_INITIALIZERS); context.removeAttribute(CLASS_INHERITANCE_MAP); @@ -222,14 +258,118 @@ public class AnnotationConfiguration extends AbstractConfiguration if (_discoverableAnnotationHandlers != null) _discoverableAnnotationHandlers.clear(); - + _classInheritanceHandler = null; if (_containerInitializerAnnotationHandlers != null) _containerInitializerAnnotationHandlers.clear(); - + + if (_parserTasks != null) + { + _parserTasks.clear(); + _parserTasks = null; + } + super.postConfigure(context); } + + + + /** + * Perform scanning of classes for annotations + * + * @param context + * @throws Exception + */ + protected void scanForAnnotations (WebAppContext context) + throws Exception + { + AnnotationParser parser = createAnnotationParser(); + boolean multiThreadedScan = isUseMultiThreading(context); + int maxThreads = 0; + int maxScanWait = 0; + if (multiThreadedScan) + { + _parserTasks = new ArrayList<ParserTask>(); + maxScanWait = getMaxScanWait(context); + } + + long start = 0; + + if (LOG.isDebugEnabled()) + { + start = System.nanoTime(); + LOG.debug("Scanning for annotations: webxml={}, configurationDiscovered={}, multiThreaded={}, threads={}", + context.getServletContext().getEffectiveMajorVersion(), + context.isConfigurationDiscovered(), + multiThreadedScan, maxThreads); + } + + parseContainerPath(context, parser); + //email from Rajiv Mordani jsrs 315 7 April 2010 + // If there is a <others/> then the ordering should be + // WEB-INF/classes the order of the declared elements + others. + // In case there is no others then it is + // WEB-INF/classes + order of the elements. + parseWebInfClasses(context, parser); + parseWebInfLib (context, parser); + + if (!multiThreadedScan) + { + if (LOG.isDebugEnabled()) + { + long end = System.nanoTime(); + LOG.debug("Annotation parsing millisec={}",(TimeUnit.MILLISECONDS.convert(end-start, TimeUnit.NANOSECONDS))); + } + return; + } + + if (LOG.isDebugEnabled()) + start = System.nanoTime(); + + //execute scan asynchronously using jetty's thread pool + final CountDownLatch latch = new CountDownLatch(_parserTasks.size()); + final MultiException me = new MultiException(); + final Semaphore task_limit=new Semaphore(Runtime.getRuntime().availableProcessors()); + for (final ParserTask p:_parserTasks) + { + task_limit.acquire(); + context.getServer().getThreadPool().execute(new Runnable() + { + @Override + public void run() + { + try + { + p.call(); + } + catch (Exception e) + { + me.add(e); + } + finally + { + task_limit.release(); + latch.countDown(); + } + } + }); + } + + boolean timeout = !latch.await(maxScanWait, TimeUnit.SECONDS); + + if (LOG.isDebugEnabled()) + { + long end = System.nanoTime(); + LOG.debug("Annotation parsing millisec={}",(TimeUnit.MILLISECONDS.convert(end-start, TimeUnit.NANOSECONDS))); + } + + if (timeout) + me.add(new Exception("Timeout scanning annotations")); + me.ifExceptionThrow(); + } + + /** * @return a new AnnotationParser. This method can be overridden to use a different implementation of * the AnnotationParser. Note that this is considered internal API. @@ -238,7 +378,60 @@ public class AnnotationConfiguration extends AbstractConfiguration { return new AnnotationParser(); } + + /** + * Check if we should use multiple threads to scan for annotations or not + * @param context + * @return + */ + protected boolean isUseMultiThreading(WebAppContext context) + { + //try context attribute to see if we should use multithreading + Object o = context.getAttribute(MULTI_THREADED); + if (o instanceof Boolean) + { + if (((Boolean)o).booleanValue()) + return true; + } + //try server attribute to see if we should use multithreading + o = context.getServer().getAttribute(MULTI_THREADED); + if (o instanceof Boolean) + { + if (((Boolean)o).booleanValue()) + return true; + } + //try system property to see if we should use multithreading + return Boolean.getBoolean(MULTI_THREADED); + } + + + /** + * Work out how long we should wait for the async scanning to occur. + * + * @param context + * @return + */ + protected int getMaxScanWait (WebAppContext context) + { + //try context attribute to get max time in sec to wait for scan completion + Object o = context.getAttribute(MAX_SCAN_WAIT); + if (o != null && o instanceof Number) + { + return ((Number)o).intValue(); + } + //try server attribute to get max time in sec to wait for scan completion + o = context.getServer().getAttribute(MAX_SCAN_WAIT); + if (o != null && o instanceof Number) + { + return ((Number)o).intValue(); + } + //try system property to get max time in sec to wait for scan completion + return Integer.getInteger(MAX_SCAN_WAIT, DEFAULT_MAX_SCAN_WAIT).intValue(); + } + + + /** * @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext) */ @@ -267,23 +460,21 @@ public class AnnotationConfiguration extends AbstractConfiguration for (ServletContainerInitializer service : scis) { HandlesTypes annotation = service.getClass().getAnnotation(HandlesTypes.class); - ContainerInitializer initializer = new ContainerInitializer(); - initializer.setTarget(service); - initializers.add(initializer); + ContainerInitializer initializer = null; if (annotation != null) - { + { //There is a HandlesTypes annotation on the on the ServletContainerInitializer Class[] classes = annotation.value(); if (classes != null) { - initializer.setInterestedTypes(classes); - + initializer = new ContainerInitializer(service, classes); //If we haven't already done so, we need to register a handler that will //process the whole class hierarchy to satisfy the ServletContainerInitializer if (context.getAttribute(CLASS_INHERITANCE_MAP) == null) { - MultiMap<String> map = new MultiMap<>(); + //MultiMap<String> map = new MultiMap<>(); + ConcurrentHashMap<String, ConcurrentHashSet<String>> map = new ConcurrentHashMap<String, ConcurrentHashSet<String>>(); context.setAttribute(CLASS_INHERITANCE_MAP, map); _classInheritanceHandler = new ClassInheritanceHandler(map); } @@ -300,12 +491,21 @@ public class AnnotationConfiguration extends AbstractConfiguration } } else + { + initializer = new ContainerInitializer(service, null); if (LOG.isDebugEnabled()) LOG.debug("No classes in HandlesTypes on initializer "+service.getClass()); + } } else + { + initializer = new ContainerInitializer(service, null); if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer "+service.getClass()); + } + + initializers.add(initializer); } + //add a bean to the context which will call the servletcontainerinitializers when appropriate ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(CONTAINER_INITIALIZER_STARTER); if (starter != null) @@ -370,8 +570,13 @@ public class AnnotationConfiguration extends AbstractConfiguration List<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>(); //We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect + long start = 0; + if (LOG.isDebugEnabled()) + start = System.nanoTime(); ServiceLoader<ServletContainerInitializer> loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class, context.getClassLoader()); - + if (LOG.isDebugEnabled()) + LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime()-start), TimeUnit.NANOSECONDS))); + if (loadedInitializers != null) { for (ServletContainerInitializer service : loadedInitializers) @@ -393,28 +598,27 @@ public class AnnotationConfiguration extends AbstractConfiguration * @param parser * @throws Exception */ - public void parseContainerPath (final WebAppContext context, final AnnotationParser parser) - throws Exception + public void parseContainerPath (final WebAppContext context, final AnnotationParser parser) throws Exception { //if no pattern for the container path is defined, then by default scan NOTHING - LOG.debug("Scanning container jars"); - + LOG.debug("Scanning container jars"); + //always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations - parser.clearHandlers(); - parser.registerHandlers(_discoverableAnnotationHandlers); - parser.registerHandler(_classInheritanceHandler); - parser.registerHandlers(_containerInitializerAnnotationHandlers); + final Set<Handler> handlers = new HashSet<Handler>(); + handlers.addAll(_discoverableAnnotationHandlers); + handlers.addAll(_containerInitializerAnnotationHandlers); + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); - //Convert from Resource to URI - ArrayList<URI> containerUris = new ArrayList<URI>(); for (Resource r : context.getMetaData().getContainerResources()) { - URI uri = r.getURI(); - containerUris.add(uri); - } - - parser.parse (containerUris.toArray(new URI[containerUris.size()]), - new ContainerClassNameResolver (context)); + //queue it up for scanning if using multithreaded mode + if (_parserTasks != null) + _parserTasks.add(new ParserTask(parser, handlers, r, _containerClassNameResolver)); + else + //just scan it now + parser.parse(handlers, r, _containerClassNameResolver); + } } @@ -425,9 +629,10 @@ public class AnnotationConfiguration extends AbstractConfiguration * @param parser * @throws Exception */ - public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser) - throws Exception - { + public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser) throws Exception + { + LOG.debug("Scanning WEB-INF/lib jars"); + List<FragmentDescriptor> frags = context.getMetaData().getFragments(); //email from Rajiv Mordani jsrs 315 7 April 2010 @@ -444,9 +649,8 @@ public class AnnotationConfiguration extends AbstractConfiguration for (Resource r : jars) { //for each jar, we decide which set of annotations we need to parse for - parser.clearHandlers(); + final Set<Handler> handlers = new HashSet<Handler>(); - URI uri = r.getURI(); FragmentDescriptor f = getFragmentFromJar(r, frags); //if its from a fragment jar that is metadata complete, we should skip scanning for @webservlet etc @@ -456,20 +660,26 @@ public class AnnotationConfiguration extends AbstractConfiguration if (f == null || !isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) { //register the classinheritance handler if there is one - parser.registerHandler(_classInheritanceHandler); - + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + //register the handlers for the @HandlesTypes values that are themselves annotations if there are any - parser.registerHandlers(_containerInitializerAnnotationHandlers); - + handlers.addAll(_containerInitializerAnnotationHandlers); + //only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor if (f == null || !isMetaDataComplete(f)) - parser.registerHandlers(_discoverableAnnotationHandlers); + handlers.addAll(_discoverableAnnotationHandlers); - parser.parse(uri, new WebAppClassNameResolver(context)); + if (_parserTasks != null) + _parserTasks.add (new ParserTask(parser, handlers,r, _webAppClassNameResolver)); + else + parser.parse(handlers, r, _webAppClassNameResolver); } } + } + /** * Scan classes in WEB-INF/classes * @@ -480,22 +690,26 @@ public class AnnotationConfiguration extends AbstractConfiguration public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser) throws Exception { - LOG.debug("Scanning classes in WEB-INF/classes"); - - parser.clearHandlers(); - parser.registerHandlers(_discoverableAnnotationHandlers); - parser.registerHandler(_classInheritanceHandler); - parser.registerHandlers(_containerInitializerAnnotationHandlers); + LOG.debug("Scanning WEB-INF/classes"); + + Set<Handler> handlers = new HashSet<Handler>(); + handlers.addAll(_discoverableAnnotationHandlers); + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + handlers.addAll(_containerInitializerAnnotationHandlers); for (Resource dir : context.getMetaData().getWebInfClassesDirs()) { - parser.parseDir(dir, new WebAppClassNameResolver(context)); + if (_parserTasks != null) + _parserTasks.add(new ParserTask(parser, handlers, dir, _webAppClassNameResolver)); + else + parser.parse(handlers, dir, _webAppClassNameResolver); } } - /** + /** * Get the web-fragment.xml from a jar * * @param jar diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java index 4a182694e2..201533fdb6 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java @@ -26,10 +26,14 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.jar.JarInputStream; import org.eclipse.jetty.util.ConcurrentHashSet; @@ -69,7 +73,7 @@ public class AnnotationParser private static final Logger LOG = Log.getLogger(AnnotationParser.class); protected Set<String> _parsedClassNames = new ConcurrentHashSet<String>(); - protected Set<Handler> _handlers = new ConcurrentHashSet<Handler>(); + /** * Convert internal name to simple name @@ -363,6 +367,7 @@ public class AnnotationParser public class MyMethodVisitor extends MethodVisitor { final MethodInfo _mi; + final Set<? extends Handler> _handlers; /** @@ -373,7 +378,7 @@ public class AnnotationParser * @param signature * @param exceptions */ - public MyMethodVisitor( + public MyMethodVisitor(final Set<? extends Handler> handlers, final ClassInfo classInfo, final int access, final String name, @@ -382,6 +387,7 @@ public class AnnotationParser final String[] exceptions) { super(Opcodes.ASM4); + _handlers = handlers; _mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions); } @@ -413,12 +419,14 @@ public class AnnotationParser public class MyFieldVisitor extends FieldVisitor { final FieldInfo _fieldInfo; + final Set<? extends Handler> _handlers; /** * @param classname */ - public MyFieldVisitor(final ClassInfo classInfo, + public MyFieldVisitor(final Set<? extends Handler> handlers, + final ClassInfo classInfo, final int access, final String fieldName, final String fieldType, @@ -426,6 +434,7 @@ public class AnnotationParser final Object value) { super(Opcodes.ASM4); + _handlers = handlers; _fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value); } @@ -458,11 +467,13 @@ public class AnnotationParser { final Resource _containingResource; + final Set<? extends Handler> _handlers; ClassInfo _ci; - public MyClassVisitor(Resource containingResource) + public MyClassVisitor(Set<? extends Handler> handlers, Resource containingResource) { super(Opcodes.ASM4); + _handlers = handlers; _containingResource = containingResource; } @@ -513,7 +524,7 @@ public class AnnotationParser final String[] exceptions) { - return new MyMethodVisitor(_ci, access, name, methodDesc, signature, exceptions); + return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions); } /** @@ -528,57 +539,11 @@ public class AnnotationParser final String signature, final Object value) { - return new MyFieldVisitor(_ci, access, fieldName, fieldType, signature, value); + return new MyFieldVisitor(_handlers, _ci, access, fieldName, fieldType, signature, value); } } - /** - * Add a particular handler - * - * @param h - */ - public void registerHandler(Handler h) - { - if (h == null) - return; - - _handlers.add(h); - } - - - /** - * Add a list of handlers - * - * @param handlers - */ - public void registerHandlers(List<? extends Handler> handlers) - { - if (handlers == null) - return; - _handlers.addAll(handlers); - } - - - /** - * Remove a particular handler - * - * @param h - */ - public boolean deregisterHandler(Handler h) - { - return _handlers.remove(h); - } - - - /** - * Remove all registered handlers - */ - public void clearHandlers() - { - _handlers.clear(); - } - /** * True if the class has already been processed, false otherwise @@ -598,7 +563,7 @@ public class AnnotationParser * @param resolver * @throws Exception */ - public void parse (String className, ClassNameResolver resolver) + public void parse (Set<? extends Handler> handlers, String className, ClassNameResolver resolver) throws Exception { if (className == null) @@ -613,7 +578,7 @@ public class AnnotationParser if (resource!= null) { Resource r = Resource.newResource(resource); - scanClass(null, r.getInputStream()); + scanClass(handlers, null, r.getInputStream()); } } } @@ -629,7 +594,7 @@ public class AnnotationParser * @param visitSuperClasses * @throws Exception */ - public void parse (Class<?> clazz, ClassNameResolver resolver, boolean visitSuperClasses) + public void parse (Set<? extends Handler> handlers, Class<?> clazz, ClassNameResolver resolver, boolean visitSuperClasses) throws Exception { Class<?> cz = clazz; @@ -644,7 +609,7 @@ public class AnnotationParser if (resource!= null) { Resource r = Resource.newResource(resource); - scanClass(null, r.getInputStream()); + scanClass(handlers, null, r.getInputStream()); } } } @@ -664,13 +629,13 @@ public class AnnotationParser * @param resolver * @throws Exception */ - public void parse (String[] classNames, ClassNameResolver resolver) + public void parse (Set<? extends Handler> handlers, String[] classNames, ClassNameResolver resolver) throws Exception { if (classNames == null) return; - parse(Arrays.asList(classNames), resolver); + parse(handlers, Arrays.asList(classNames), resolver); } @@ -681,7 +646,7 @@ public class AnnotationParser * @param resolver * @throws Exception */ - public void parse (List<String> classNames, ClassNameResolver resolver) + public void parse (Set<? extends Handler> handlers, List<String> classNames, ClassNameResolver resolver) throws Exception { for (String s:classNames) @@ -693,7 +658,7 @@ public class AnnotationParser if (resource!= null) { Resource r = Resource.newResource(resource); - scanClass(null, r.getInputStream()); + scanClass(handlers, null, r.getInputStream()); } } } @@ -707,7 +672,7 @@ public class AnnotationParser * @param resolver * @throws Exception */ - protected void parseDir (Resource dir, ClassNameResolver resolver) + protected void parseDir (Set<? extends Handler> handlers, Resource dir, ClassNameResolver resolver) throws Exception { //skip dirs whose name start with . (ie hidden) @@ -723,7 +688,7 @@ public class AnnotationParser { Resource res = dir.addPath(files[f]); if (res.isDirectory()) - parseDir(res, resolver); + parseDir(handlers, res, resolver); else { //we've already verified the directories, so just verify the class file name @@ -741,7 +706,7 @@ public class AnnotationParser { Resource r = Resource.newResource(res.getURL()); if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);}; - scanClass(dir, r.getInputStream()); + scanClass(handlers, dir, r.getInputStream()); } } @@ -765,7 +730,7 @@ public class AnnotationParser * @param resolver * @throws Exception */ - public void parse (ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver) + public void parse (final Set<? extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver) throws Exception { if (loader==null) @@ -781,7 +746,7 @@ public class AnnotationParser { try { - parseJarEntry(Resource.newResource(jarUri), entry, resolver); + parseJarEntry(handlers, Resource.newResource(jarUri), entry, resolver); } catch (Exception e) { @@ -802,7 +767,7 @@ public class AnnotationParser * @param resolver * @throws Exception */ - public void parse (URI[] uris, final ClassNameResolver resolver) + public void parse (final Set<? extends Handler> handlers, final URI[] uris, final ClassNameResolver resolver) throws Exception { if (uris==null) @@ -812,7 +777,7 @@ public class AnnotationParser { try { - parse(uri, resolver); + parse(handlers, uri, resolver); } catch (Exception e) { @@ -828,13 +793,13 @@ public class AnnotationParser * @param resolver * @throws Exception */ - public void parse (URI uri, final ClassNameResolver resolver) + public void parse (final Set<? extends Handler> handlers, URI uri, final ClassNameResolver resolver) throws Exception { if (uri == null) return; - parse (Resource.newResource(uri), resolver); + parse (handlers, Resource.newResource(uri), resolver); } @@ -846,7 +811,7 @@ public class AnnotationParser * @param resolver * @throws Exception */ - public void parse (Resource r, final ClassNameResolver resolver) + public void parse (final Set<? extends Handler> handlers, Resource r, final ClassNameResolver resolver) throws Exception { if (r == null) @@ -854,20 +819,20 @@ public class AnnotationParser if (r.exists() && r.isDirectory()) { - parseDir(r, resolver); + parseDir(handlers, r, resolver); return; } String fullname = r.toString(); if (fullname.endsWith(".jar")) { - parseJar(r, resolver); + parseJar(handlers, r, resolver); return; } if (fullname.endsWith(".class")) { - scanClass(null, r.getInputStream()); + scanClass(handlers, null, r.getInputStream()); return; } @@ -884,29 +849,56 @@ public class AnnotationParser * @param resolver * @throws Exception */ - protected void parseJar (Resource jarResource, final ClassNameResolver resolver) + protected void parseJar (Set<? extends Handler> handlers, Resource jarResource, final ClassNameResolver resolver) throws Exception { if (jarResource == null) return; - if (jarResource.toString().endsWith(".jar")) { if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);}; - - //treat it as a jar that we need to open and scan all entries from + + //treat it as a jar that we need to open and scan all entries from + //TODO alternative impl + /* + Collection<Resource> resources = Resource.newResource("jar:"+jarResource+"!/").getAllResources(); + for (Resource r:resources) + { + //skip directories + if (r.isDirectory()) + continue; + + String name = r.getName(); + name = name.substring(name.indexOf("!/")+2); + + //check file is a valid class file name + if (isValidClassFileName(name) && isValidClassFilePath(name)) + { + + String shortName = name.replace('/', '.').substring(0,name.length()-6); + + if ((resolver == null) + || + (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName)))) + { + if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", r);}; + scanClass(handlers, jarResource, r.getInputStream()); + } + } + }*/ + InputStream in = jarResource.getInputStream(); if (in==null) return; - + JarInputStream jar_in = new JarInputStream(in); try { JarEntry entry = jar_in.getNextJarEntry(); while (entry!=null) { - parseJarEntry(jarResource, entry, resolver); + parseJarEntry(handlers, jarResource, entry, resolver); entry = jar_in.getNextJarEntry(); } } @@ -924,7 +916,7 @@ public class AnnotationParser * @param resolver * @throws Exception */ - protected void parseJarEntry (Resource jar, JarEntry entry, final ClassNameResolver resolver) + protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, JarEntry entry, final ClassNameResolver resolver) throws Exception { if (jar == null || entry == null) @@ -947,7 +939,7 @@ public class AnnotationParser { Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name); if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);}; - scanClass(jar, clazz.getInputStream()); + scanClass(handlers, jar, clazz.getInputStream()); } } } @@ -961,11 +953,11 @@ public class AnnotationParser * @param is * @throws IOException */ - protected void scanClass (Resource containingResource, InputStream is) + protected void scanClass (Set<? extends Handler> handlers, Resource containingResource, InputStream is) throws IOException { ClassReader reader = new ClassReader(is); - reader.accept(new MyClassVisitor(containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); + reader.accept(new MyClassVisitor(handlers, containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } /** diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java index a5cfa20e37..14f1198af8 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java @@ -19,9 +19,13 @@ package org.eclipse.jetty.annotations; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; +import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -35,10 +39,10 @@ public class ClassInheritanceHandler extends AbstractHandler { private static final Logger LOG = Log.getLogger(ClassInheritanceHandler.class); - MultiMap<String> _inheritanceMap; + ConcurrentHashMap<String, ConcurrentHashSet<String>> _inheritanceMap; - public ClassInheritanceHandler(MultiMap<String> map) + public ClassInheritanceHandler(ConcurrentHashMap<String, ConcurrentHashSet<String>> map) { _inheritanceMap = map; } @@ -49,15 +53,37 @@ public class ClassInheritanceHandler extends AbstractHandler { for (int i=0; classInfo.getInterfaces() != null && i < classInfo.getInterfaces().length;i++) { - _inheritanceMap.add (classInfo.getInterfaces()[i], classInfo.getClassName()); + addToInheritanceMap(classInfo.getInterfaces()[i], classInfo.getClassName()); + //_inheritanceMap.add (classInfo.getInterfaces()[i], classInfo.getClassName()); } //To save memory, we don't record classes that only extend Object, as that can be assumed if (!"java.lang.Object".equals(classInfo.getSuperName())) - _inheritanceMap.add(classInfo.getSuperName(), classInfo.getClassName()); + { + addToInheritanceMap(classInfo.getSuperName(), classInfo.getClassName()); + //_inheritanceMap.add(classInfo.getSuperName(), classInfo.getClassName()); + } } catch (Exception e) { LOG.warn(e); } } + + private void addToInheritanceMap (String interfaceOrSuperClassName, String implementingOrExtendingClassName) + { + + //As it is likely that the interfaceOrSuperClassName is already in the map, try getting it first + ConcurrentHashSet<String> implementingClasses = _inheritanceMap.get(interfaceOrSuperClassName); + //If it isn't in the map, then add it in, but test to make sure that someone else didn't get in + //first and add it + if (implementingClasses == null) + { + implementingClasses = new ConcurrentHashSet<String>(); + ConcurrentHashSet<String> tmp = _inheritanceMap.putIfAbsent(interfaceOrSuperClassName, implementingClasses); + if (tmp != null) + implementingClasses = tmp; + } + + implementingClasses.add(implementingOrExtendingClassName); + } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java index d912cdc34e..6eff325a92 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java @@ -39,8 +39,8 @@ import org.eclipse.jetty.plus.annotation.ContainerInitializer; */ public class ContainerInitializerAnnotationHandler extends AbstractHandler { - ContainerInitializer _initializer; - Class _annotation; + final ContainerInitializer _initializer; + final Class _annotation; public ContainerInitializerAnnotationHandler (ContainerInitializer initializer, Class annotation) { diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java index a7e5304adb..1fde73510c 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializersStarter.java @@ -21,8 +21,10 @@ package org.eclipse.jetty.annotations; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.plus.annotation.ContainerInitializer; +import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; @@ -60,11 +62,11 @@ public class ServletContainerInitializersStarter extends AbstractLifeCycle if (initializers == null) return; - MultiMap classMap = (MultiMap)_context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); + ConcurrentHashMap<String, ConcurrentHashSet<String>> map = ( ConcurrentHashMap<String, ConcurrentHashSet<String>>)_context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); for (ContainerInitializer i : initializers) { - configureHandlesTypes(_context, i, classMap); + configureHandlesTypes(_context, i, map); //instantiate ServletContainerInitializers, call doStart try @@ -80,13 +82,13 @@ public class ServletContainerInitializersStarter extends AbstractLifeCycle } - private void configureHandlesTypes (WebAppContext context, ContainerInitializer initializer, MultiMap<String> classMap) + private void configureHandlesTypes (WebAppContext context, ContainerInitializer initializer, ConcurrentHashMap<String, ConcurrentHashSet<String>> classMap) { doHandlesTypesAnnotations(context, initializer, classMap); doHandlesTypesClasses(context, initializer, classMap); } - private void doHandlesTypesAnnotations(WebAppContext context, ContainerInitializer initializer, MultiMap<String> classMap) + private void doHandlesTypesAnnotations(WebAppContext context, ContainerInitializer initializer, ConcurrentHashMap<String, ConcurrentHashSet<String>> classMap) { if (initializer == null) return; @@ -96,26 +98,26 @@ public class ServletContainerInitializersStarter extends AbstractLifeCycle //We have already found the classes that directly have an annotation that was in the HandlesTypes //annotation of the ServletContainerInitializer. For each of those classes, walk the inheritance //hierarchy to find classes that extend or implement them. - if (initializer.getAnnotatedTypeNames() != null) + Set<String> annotatedClassNames = initializer.getAnnotatedTypeNames(); + if (annotatedClassNames != null && !annotatedClassNames.isEmpty()) { if (classMap == null) throw new IllegalStateException ("No class hierarchy"); - Set<String> annotatedClassNames = new HashSet<String>(initializer.getAnnotatedTypeNames()); for (String name : annotatedClassNames) { //add the class that has the annotation initializer.addApplicableTypeName(name); //find and add the classes that inherit the annotation - addInheritedTypes(classMap, initializer, (List<String>)classMap.getValues(name)); + addInheritedTypes(classMap, initializer, (ConcurrentHashSet<String>)classMap.get(name)); } } } - private void doHandlesTypesClasses (WebAppContext context, ContainerInitializer initializer, MultiMap<String> classMap) + private void doHandlesTypesClasses (WebAppContext context, ContainerInitializer initializer, ConcurrentHashMap<String, ConcurrentHashSet<String>> classMap) { if (initializer == null) return; @@ -135,14 +137,14 @@ public class ServletContainerInitializersStarter extends AbstractLifeCycle { //find and add the classes that implement or extend the class. //but not including the class itself - addInheritedTypes(classMap, initializer, (List<String>)classMap.getValues(c.getName())); + addInheritedTypes(classMap, initializer, (ConcurrentHashSet<String>)classMap.get(c.getName())); } } } } - private void addInheritedTypes (MultiMap classMap, ContainerInitializer initializer, List<String> names) + private void addInheritedTypes (ConcurrentHashMap<String, ConcurrentHashSet<String>> classMap, ContainerInitializer initializer, ConcurrentHashSet<String> names) { if (names == null || names.isEmpty()) return; @@ -153,7 +155,7 @@ public class ServletContainerInitializersStarter extends AbstractLifeCycle initializer.addApplicableTypeName(s); //walk the hierarchy and find all types that extend or implement the class - addInheritedTypes(classMap, initializer, (List<String>)classMap.getValues(s)); + addInheritedTypes(classMap, initializer, (ConcurrentHashSet<String>)classMap.get(s)); } } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java index ffe5597c27..fe5cc277e3 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java @@ -18,14 +18,11 @@ package org.eclipse.jetty.annotations; -import java.util.List; - import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.webapp.DiscoveredAnnotation; import org.eclipse.jetty.webapp.WebAppContext; /** @@ -41,11 +38,7 @@ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHa { super(context); } - - public WebFilterAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list) - { - super(context, list); - } + @Override public void handle(ClassInfo info, String annotationName) diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java index 396eaea1d8..5ba94dd723 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java @@ -18,14 +18,11 @@ package org.eclipse.jetty.annotations; -import java.util.List; - import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.webapp.DiscoveredAnnotation; import org.eclipse.jetty.webapp.WebAppContext; public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler @@ -37,11 +34,7 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation super(context); } - public WebListenerAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list) - { - super(context, list); - } - + /** * @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handle(ClassAnnotationInfo) */ diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java index ea300925ae..6a675a33c7 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java @@ -43,11 +43,6 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH super(context); } - public WebServletAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list) - { - super(context, list); - } - /** * Handle discovering a WebServlet annotation. diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java index 7d5d902444..fddde8a956 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java @@ -24,8 +24,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.naming.Context; import javax.naming.InitialContext; @@ -34,6 +36,7 @@ import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; +import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.MultiMap; import org.junit.After; import org.junit.Test; @@ -92,8 +95,7 @@ public class TestAnnotationInheritance SampleHandler handler = new SampleHandler(); AnnotationParser parser = new AnnotationParser(); - parser.registerHandler(handler); - parser.parse(classNames, new ClassNameResolver () + parser.parse(Collections.singleton(handler), classNames, new ClassNameResolver () { public boolean isExcluded(String name) { @@ -129,8 +131,7 @@ public class TestAnnotationInheritance { SampleHandler handler = new SampleHandler(); AnnotationParser parser = new AnnotationParser(); - parser.registerHandler(handler); - parser.parse(ClassB.class, new ClassNameResolver () + parser.parse(Collections.singleton(handler), ClassB.class, new ClassNameResolver () { public boolean isExcluded(String name) { @@ -166,8 +167,7 @@ public class TestAnnotationInheritance { AnnotationParser parser = new AnnotationParser(); SampleHandler handler = new SampleHandler(); - parser.registerHandler(handler); - parser.parse(ClassA.class.getName(), new ClassNameResolver() + parser.parse(Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver() { public boolean isExcluded(String name) { @@ -187,7 +187,7 @@ public class TestAnnotationInheritance handler.annotatedFields.clear(); handler.annotatedMethods.clear(); - parser.parse (ClassA.class.getName(), new ClassNameResolver() + parser.parse (Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver() { public boolean isExcluded(String name) { @@ -205,11 +205,10 @@ public class TestAnnotationInheritance @Test public void testTypeInheritanceHandling() throws Exception { - MultiMap map = new MultiMap(); + ConcurrentHashMap<String, ConcurrentHashSet<String>> map = new ConcurrentHashMap<String, ConcurrentHashSet<String>>(); AnnotationParser parser = new AnnotationParser(); ClassInheritanceHandler handler = new ClassInheritanceHandler(map); - parser.registerHandler(handler); class Foo implements InterfaceD { @@ -221,21 +220,22 @@ public class TestAnnotationInheritance classNames.add(InterfaceD.class.getName()); classNames.add(Foo.class.getName()); - parser.parse(classNames, null); + parser.parse(Collections.singleton(handler), classNames, null); assertNotNull(map); assertFalse(map.isEmpty()); assertEquals(2, map.size()); - Map stringArrayMap = map.toStringArrayMap(); - assertTrue (stringArrayMap.keySet().contains("org.eclipse.jetty.annotations.ClassA")); - assertTrue (stringArrayMap.keySet().contains("org.eclipse.jetty.annotations.InterfaceD")); - String[] classes = (String[])stringArrayMap.get("org.eclipse.jetty.annotations.ClassA"); - assertEquals(1, classes.length); - assertEquals ("org.eclipse.jetty.annotations.ClassB", classes[0]); - - classes = (String[])stringArrayMap.get("org.eclipse.jetty.annotations.InterfaceD"); - assertEquals(2, classes.length); - assertEquals ("org.eclipse.jetty.annotations.ClassB", classes[0]); - assertEquals(Foo.class.getName(), classes[1]); + + + assertTrue (map.keySet().contains("org.eclipse.jetty.annotations.ClassA")); + assertTrue (map.keySet().contains("org.eclipse.jetty.annotations.InterfaceD")); + ConcurrentHashSet<String> classes = map.get("org.eclipse.jetty.annotations.ClassA"); + assertEquals(1, classes.size()); + assertEquals ("org.eclipse.jetty.annotations.ClassB", classes.iterator().next()); + + classes = map.get("org.eclipse.jetty.annotations.InterfaceD"); + assertEquals(2, classes.size()); + assertTrue(classes.contains("org.eclipse.jetty.annotations.ClassB")); + assertTrue(classes.contains(Foo.class.getName())); } } diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java index 0b46345cb8..1f0eafcd6f 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java @@ -31,12 +31,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; +import org.eclipse.jetty.annotations.AnnotationParser.Handler; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.IO; @@ -108,10 +110,8 @@ public class TestAnnotationParser } } - parser.registerHandler(new SampleAnnotationHandler()); - //long start = System.currentTimeMillis(); - parser.parse(classNames,new ClassNameResolver() + parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames,new ClassNameResolver() { public boolean isExcluded(String name) { @@ -162,8 +162,7 @@ public class TestAnnotationParser } } - parser.registerHandler(new MultiAnnotationHandler()); - parser.parse(classNames,null); + parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames,null); } @Test @@ -171,7 +170,8 @@ public class TestAnnotationParser { File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar"); AnnotationParser parser = new AnnotationParser(); - parser.parse(badClassesJar.toURI(),null); + Set<Handler> emptySet = Collections.emptySet(); + parser.parse(emptySet, badClassesJar.toURI(),null); // only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here } @@ -194,10 +194,9 @@ public class TestAnnotationParser // Setup annotation scanning AnnotationParser parser = new AnnotationParser(); - parser.registerHandler(tracker); // Parse - parser.parse(basedir.toURI(),null); + parser.parse(Collections.singleton(tracker), basedir.toURI(),null); // Validate Assert.assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName())); diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java index d6614ed239..e1f5a989fd 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -74,9 +75,8 @@ public class TestServletAnnotations List<DiscoveredAnnotation> results = new ArrayList<DiscoveredAnnotation>(); TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results); - parser.registerHandler(handler); - - parser.parse(classes, new ClassNameResolver () + + parser.parse(Collections.singleton(handler), classes, new ClassNameResolver () { public boolean isExcluded(String name) { diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenAnnotationConfiguration.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenAnnotationConfiguration.java index a16c4dbcc3..3f15a59b32 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenAnnotationConfiguration.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenAnnotationConfiguration.java @@ -19,10 +19,12 @@ package org.eclipse.jetty.maven.plugin; import java.io.File; +import java.util.HashSet; +import java.util.Set; -import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.annotations.AnnotationParser; +import org.eclipse.jetty.annotations.AnnotationParser.Handler; import org.eclipse.jetty.annotations.ClassNameResolver; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -50,10 +52,11 @@ public class MavenAnnotationConfiguration extends AnnotationConfiguration if (metaData == null) throw new IllegalStateException ("No metadata"); - parser.clearHandlers(); - parser.registerHandlers(_discoverableAnnotationHandlers); - parser.registerHandler(_classInheritanceHandler); - parser.registerHandlers(_containerInitializerAnnotationHandlers); + Set<Handler> handlers = new HashSet<Handler>(); + handlers.addAll(_discoverableAnnotationHandlers); + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + handlers.addAll(_containerInitializerAnnotationHandlers); for (File f:jwac.getClassPathFiles()) @@ -61,7 +64,7 @@ public class MavenAnnotationConfiguration extends AnnotationConfiguration //scan the equivalent of the WEB-INF/classes directory that has been synthesised by the plugin if (f.isDirectory() && f.exists()) { - doParse(context, parser, Resource.newResource(f.toURI())); + doParse(handlers, context, parser, Resource.newResource(f.toURI())); } } @@ -72,32 +75,19 @@ public class MavenAnnotationConfiguration extends AnnotationConfiguration Resource classesDir = context.getWebInf().addPath("classes/"); if (classesDir.exists()) { - doParse(context, parser, classesDir); + doParse(handlers, context, parser, classesDir); } } } } - public void doParse (final WebAppContext context, final AnnotationParser parser, Resource resource) + public void doParse (final Set<? extends Handler> handlers, final WebAppContext context, final AnnotationParser parser, Resource resource) throws Exception { - parser.parse(resource, new ClassNameResolver() - { - public boolean isExcluded (String name) - { - if (context.isSystemClass(name)) return true; - if (context.isServerClass(name)) return false; - return false; - } - - public boolean shouldOverride (String name) - { - //looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp? - if (context.isParentLoaderPriority()) - return false; - return true; - } - }); + if (_parserTasks != null) + _parserTasks.add(new ParserTask(parser, handlers, resource, _webAppClassNameResolver)); + else + parser.parse(handlers, resource, _webAppClassNameResolver); } } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java index e5d2d2cf13..d70d1ea797 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java @@ -18,7 +18,10 @@ package org.eclipse.jetty.osgi.annotations; -import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jetty.annotations.AnnotationParser.Handler; import org.eclipse.jetty.annotations.ClassNameResolver; import org.eclipse.jetty.osgi.boot.OSGiWebappConstants; import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker; @@ -34,7 +37,27 @@ import org.osgi.framework.Constants; */ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration { + public class BundleParserTask extends ParserTask + { + + public BundleParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource, ClassNameResolver resolver) + { + super(parser, handlers, resource, resolver); + } + public Void call() throws Exception + { + if (_parser != null) + { + org.eclipse.jetty.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.osgi.annotations.AnnotationParser)_parser; + Bundle bundle = osgiAnnotationParser.getBundle(_resource); + osgiAnnotationParser.parse(_handlers, bundle, _resolver); + } + return null; + } + } + + /** * This parser scans the bundles using the OSGi APIs instead of assuming a jar. */ @@ -64,7 +87,7 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot Bundle[] fragAndRequiredBundles = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(webbundle); if (fragAndRequiredBundles != null) { - //index: + //index and scan fragments for (Bundle bundle : fragAndRequiredBundles) { Resource bundleRes = oparser.indexBundle(bundle); @@ -72,19 +95,16 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot { context.getMetaData().addWebInfJar(bundleRes); } - } - - //scan the fragments - for (Bundle fragmentBundle : fragAndRequiredBundles) - { - if (fragmentBundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) + + if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) { //a fragment indeed: - parseFragmentBundle(context,oparser,webbundle,fragmentBundle); + parseFragmentBundle(context,oparser,webbundle,bundle); } } } //scan ourselves + oparser.indexBundle(webbundle); parseWebBundle(context,oparser,webbundle); //scan the WEB-INF/lib @@ -146,17 +166,21 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot } protected void parseBundle(WebAppContext context, AnnotationParser parser, - Bundle webbundle, Bundle bundle) throws Exception - { - - Resource bundleRes = parser.getResource(bundle); - - parser.clearHandlers(); - parser.registerHandlers(_discoverableAnnotationHandlers); - parser.registerHandler(_classInheritanceHandler); - parser.registerHandlers(_containerInitializerAnnotationHandlers); - - parser.parse(bundle,createClassNameResolver(context)); + Bundle webbundle, Bundle bundle) throws Exception + { + + Resource bundleRes = parser.getResource(bundle); + Set<Handler> handlers = new HashSet<Handler>(); + handlers.addAll(_discoverableAnnotationHandlers); + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + handlers.addAll(_containerInitializerAnnotationHandlers); + + ClassNameResolver classNameResolver = createClassNameResolver(context); + if (_parserTasks != null) + _parserTasks.add(new BundleParserTask(parser, handlers, bundleRes, classNameResolver)); + else + parser.parse(handlers, bundle, classNameResolver); } /** diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java index c2c14e3ed7..e502435dac 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java @@ -29,9 +29,11 @@ import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.annotations.ClassNameResolver; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; +import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.resource.Resource; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; @@ -41,11 +43,12 @@ import org.osgi.framework.Constants; */ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationParser { - private Set<URI> _alreadyParsed = new HashSet<URI>(); + private Set<URI> _alreadyParsed = new ConcurrentHashSet<URI>(); - private Map<URI,Bundle> _uriToBundle = new HashMap<URI, Bundle>(); - private Map<Bundle,Resource> _resourceToBundle = new HashMap<Bundle,Resource>(); - private Map<Bundle,URI> _bundleToUri = new HashMap<Bundle, URI>(); + private ConcurrentHashMap<URI,Bundle> _uriToBundle = new ConcurrentHashMap<URI, Bundle>(); + private ConcurrentHashMap<Bundle,Resource> _bundleToResource = new ConcurrentHashMap<Bundle,Resource>(); + private ConcurrentHashMap<Resource, Bundle> _resourceToBundle = new ConcurrentHashMap<Resource, Bundle>(); + private ConcurrentHashMap<Bundle,URI> _bundleToUri = new ConcurrentHashMap<Bundle, URI>(); /** * Keep track of a jetty URI Resource and its associated OSGi bundle. @@ -58,9 +61,10 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa File bundleFile = BundleFileLocatorHelper.DEFAULT.getBundleInstallLocation(bundle); Resource resource = Resource.newResource(bundleFile.toURI()); URI uri = resource.getURI(); - _uriToBundle.put(uri,bundle); - _bundleToUri.put(bundle,uri); - _resourceToBundle.put(bundle,resource); + _uriToBundle.putIfAbsent(uri,bundle); + _bundleToUri.putIfAbsent(bundle,uri); + _bundleToResource.putIfAbsent(bundle,resource); + _resourceToBundle.putIfAbsent(resource,bundle); return resource; } protected URI getURI(Bundle bundle) @@ -69,13 +73,19 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa } protected Resource getResource(Bundle bundle) { - return _resourceToBundle.get(bundle); + return _bundleToResource.get(bundle); } + protected Bundle getBundle (Resource resource) + { + return _resourceToBundle.get(resource); + } + + /** * */ @Override - public void parse (URI[] uris, ClassNameResolver resolver) + public void parse (Set<? extends Handler> handlers, URI[] uris, ClassNameResolver resolver) throws Exception { for (URI uri : uris) @@ -89,16 +99,16 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa } //a jar in WEB-INF/lib or the WEB-INF/classes //use the behavior of the super class for a standard jar. - super.parse(new URI[] {uri},resolver); + super.parse(handlers, new URI[] {uri},resolver); } else { - parse(associatedBundle,resolver); + parse(handlers, associatedBundle,resolver); } } } - protected void parse(Bundle bundle, ClassNameResolver resolver) + protected void parse(Set<? extends Handler> handlers, Bundle bundle, ClassNameResolver resolver) throws Exception { URI uri = _bundleToUri.get(bundle); @@ -190,7 +200,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa //transform into a classname to pass to the resolver String shortName = name.replace('/', '.').substring(0,name.length()-6); if ((resolver == null)|| (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName)))) - scanClass(getResource(bundle), classUrl.openStream()); + scanClass(handlers, getResource(bundle), classUrl.openStream()); } } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java index 197ee2cb2c..b47b8ac908 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java @@ -21,7 +21,11 @@ package org.eclipse.jetty.osgi.boot; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory; import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker; @@ -53,8 +57,8 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration @Override public void preConfigure(final WebAppContext context) throws Exception { - List<Resource> frags = (List<Resource>) context.getAttribute(METAINF_FRAGMENTS); - List<Resource> resfrags = (List<Resource>) context.getAttribute(METAINF_RESOURCES); + Map<Resource, Resource> frags = (Map<Resource, Resource>) context.getAttribute(METAINF_FRAGMENTS); + Set<Resource> resfrags = (Set<Resource>) context.getAttribute(METAINF_RESOURCES); List<Resource> tldfrags = (List<Resource>) context.getAttribute(METAINF_TLDS); Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE)); @@ -73,10 +77,11 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration { if (frags == null) { - frags = new ArrayList<Resource>(); + frags = new HashMap<Resource,Resource>(); context.setAttribute(METAINF_FRAGMENTS, frags); } - frags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag).toURI())); + frags.put(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag).toURI()), + Resource.newResource(webFrag)); } if (resEnum != null && resEnum.hasMoreElements()) { @@ -92,7 +97,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration { if (resfrags == null) { - resfrags = new ArrayList<Resource>(); + resfrags = new HashSet<Resource>(); context.setAttribute(METAINF_RESOURCES, resfrags); } resfrags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(resourcesEntry))); diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java index b606db40fa..75d79a8fad 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java @@ -212,7 +212,7 @@ public class OSGiWebInfConfiguration extends WebInfConfiguration } } if (!appendedResourcesPath.isEmpty()) - context.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values())); + context.setAttribute(WebInfConfiguration.RESOURCE_DIRS, new HashSet<Resource>(appendedResourcesPath.values())); } } diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java index 6ac2102db4..dcdda325b0 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java @@ -18,26 +18,28 @@ package org.eclipse.jetty.plus.annotation; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.servlet.ServletContainerInitializer; -import javax.servlet.ServletContextListener; +import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.webapp.WebAppContext; public class ContainerInitializer { - protected ServletContainerInitializer _target; - protected Class[] _interestedTypes; - protected Set<String> _applicableTypeNames; - protected Set<String> _annotatedTypeNames; + final protected ServletContainerInitializer _target; + final protected Class[] _interestedTypes; + protected Set<String> _applicableTypeNames = new ConcurrentHashSet<String>(); + protected Set<String> _annotatedTypeNames = new ConcurrentHashSet<String>(); - public void setTarget (ServletContainerInitializer target) + public ContainerInitializer (ServletContainerInitializer target, Class[] classes) { _target = target; + _interestedTypes = classes; } public ServletContainerInitializer getTarget () @@ -50,10 +52,6 @@ public class ContainerInitializer return _interestedTypes; } - public void setInterestedTypes (Class[] interestedTypes) - { - _interestedTypes = interestedTypes; - } /** * A class has been found that has an annotation of interest @@ -62,26 +60,22 @@ public class ContainerInitializer */ public void addAnnotatedTypeName (String className) { - if (_annotatedTypeNames == null) - _annotatedTypeNames = new HashSet<String>(); _annotatedTypeNames.add(className); } public Set<String> getAnnotatedTypeNames () { - return _annotatedTypeNames; + return Collections.unmodifiableSet(_annotatedTypeNames); } public void addApplicableTypeName (String className) { - if (_applicableTypeNames == null) - _applicableTypeNames = new HashSet<String>(); _applicableTypeNames.add(className); } public Set<String> getApplicableTypeNames () { - return _applicableTypeNames; + return Collections.unmodifiableSet(_applicableTypeNames); } @@ -97,11 +91,9 @@ public class ContainerInitializer try { - if (_applicableTypeNames != null) - { - for (String s : _applicableTypeNames) - classes.add(Loader.loadClass(context.getClass(), s)); - } + for (String s : _applicableTypeNames) + classes.add(Loader.loadClass(context.getClass(), s)); + context.getServletContext().setExtendedListenerTypes(true); _target.onStartup(classes, context.getServletContext()); } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index 7b9b9bbaae..536b780ab4 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -29,7 +29,9 @@ import java.net.URI; import java.net.URL; import java.nio.channels.ReadableByteChannel; import java.text.DateFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import org.eclipse.jetty.util.B64Code; @@ -663,6 +665,34 @@ public abstract class Resource implements ResourceFactory, Closeable } /* ------------------------------------------------------------ */ + public Collection<Resource> getAllResources() + { + try + { + ArrayList<Resource> deep=new ArrayList<>(); + { + String[] list=list(); + if (list!=null) + { + for (String i:list) + { + Resource r=addPath(i); + if (r.isDirectory()) + deep.addAll(r.getAllResources()); + else + deep.add(r); + } + } + } + return deep; + } + catch(Exception e) + { + throw new IllegalStateException(e); + } + } + + /* ------------------------------------------------------------ */ /** Generate a properly encoded URL from a {@link File} instance. * @param file Target file. * @return URL of the target file. diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java index c811daa764..6fd85423b3 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java @@ -30,9 +30,9 @@ import java.io.InputStream; import java.net.URI; import java.net.URL; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; - import java.util.zip.ZipFile; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; @@ -297,10 +297,20 @@ public class ResourceTest assertTrue(entries.contains("numbers")); IO.delete(extract); - - } + /* ------------------------------------------------------------ */ + @Test + public void testJarFileGetAllResoures() + throws Exception + { + String s = "jar:"+__userURL+"TestData/test.zip!/subdir/"; + Resource r = Resource.newResource(s); + Collection<Resource> deep=r.getAllResources(); + + assertEquals(4, deep.size()); + } + @Test public void testJarFileIsContainedIn () throws Exception diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java index eb8129228f..ff28d0562a 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java @@ -19,7 +19,7 @@ package org.eclipse.jetty.webapp; -import java.util.List; +import java.util.Map; import org.eclipse.jetty.util.resource.Resource; @@ -70,18 +70,18 @@ public class FragmentConfiguration extends AbstractConfiguration public void findWebFragments (final WebAppContext context, final MetaData metaData) throws Exception { @SuppressWarnings("unchecked") - List<Resource> frags = (List<Resource>)context.getAttribute(FRAGMENT_RESOURCES); + Map<Resource, Resource> frags = (Map<Resource,Resource>)context.getAttribute(FRAGMENT_RESOURCES); if (frags!=null) { - for (Resource frag : frags) + for (Resource key : frags.keySet()) { - if (frag.isDirectory()) //tolerate the case where the library is a directory, not a jar. useful for OSGi for example + if (key.isDirectory()) //tolerate the case where the library is a directory, not a jar. useful for OSGi for example { - metaData.addFragment(frag, Resource.newResource(frag.getURL()+"/META-INF/web-fragment.xml")); + metaData.addFragment(key, frags.get(key)); } else //the standard case: a jar most likely inside WEB-INF/lib { - metaData.addFragment(frag, Resource.newResource("jar:"+frag.getURL()+"!/META-INF/web-fragment.xml")); + metaData.addFragment(key, frags.get(key)); } } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java index 7c476586a9..1bd781e873 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java @@ -288,7 +288,17 @@ public class MetaData } } - public void addDiscoveredAnnotation (DiscoveredAnnotation annotation) + + /** + * Add an annotation that has been discovered on a class, method or field within a resource + * eg a jar or dir. + * + * This method is synchronized as it is anticipated that it may be called by many threads + * during the annotation scanning phase. + * + * @param annotation + */ + public synchronized void addDiscoveredAnnotation (DiscoveredAnnotation annotation) { if (annotation == null) return; diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java index df913b508d..b7c4e0f0b2 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java @@ -21,8 +21,13 @@ package org.eclipse.jetty.webapp; import java.net.URI; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.jar.JarEntry; import org.eclipse.jetty.util.log.Log; @@ -45,57 +50,51 @@ public class MetaInfConfiguration extends AbstractConfiguration public static final String METAINF_TLDS = TagLibConfiguration.TLD_RESOURCES; public static final String METAINF_FRAGMENTS = FragmentConfiguration.FRAGMENT_RESOURCES; - public static final String METAINF_RESOURCES = WebInfConfiguration.RESOURCE_URLS; + public static final String METAINF_RESOURCES = WebInfConfiguration.RESOURCE_DIRS; @Override public void preConfigure(final WebAppContext context) throws Exception { - //Merge all container and webinf lib jars to look for META-INF resources - + //Merge all container and webinf lib jars to look for META-INF resources ArrayList<Resource> jars = new ArrayList<Resource>(); jars.addAll(context.getMetaData().getContainerResources()); jars.addAll(context.getMetaData().getWebInfJars()); - JarScanner scanner = new JarScanner() + //Scan jars for META-INF information + if (jars != null) { - public void processEntry(URI jarUri, JarEntry entry) + for (Resource r : jars) { - try + URI uri = r.getURI(); + Resource fragXml = Resource.newResource("jar:"+uri+"!/META-INF/web-fragment.xml"); + if (fragXml.exists()) { - MetaInfConfiguration.this.processEntry(context,jarUri,entry); + //add mapping for resource->fragment + Map<Resource, Resource> fragments = (Map<Resource,Resource>)context.getAttribute(METAINF_FRAGMENTS); + if (fragments == null) + { + fragments = new HashMap<Resource, Resource>(); + context.setAttribute(METAINF_FRAGMENTS, fragments); + } + fragments.put(r, fragXml); } - catch (Exception e) + + Resource resourcesDir = Resource.newResource("jar:"+uri+"!/META-INF/resources"); + if (resourcesDir.exists()) { - LOG.warn("Problem processing jar entry " + entry, e); + //add resources dir + Set<Resource> dirs = (Set<Resource>)context.getAttribute(METAINF_RESOURCES); + if (dirs == null) + { + dirs = new HashSet<Resource>(); + context.setAttribute(METAINF_RESOURCES, dirs); + } + dirs.add(resourcesDir); } } - }; - - - //Scan jars for META-INF information - if (jars != null) - { - URI[] uris = new URI[jars.size()]; - int i=0; - for (Resource r : jars) - { - uris[i++] = r.getURI(); - } - scanner.scan(null, uris, true); } } - @Override - public void configure(WebAppContext context) throws Exception - { - - } - - @Override - public void deconfigure(WebAppContext context) throws Exception - { - - } - + @Override public void postConfigure(WebAppContext context) throws Exception { @@ -103,50 +102,4 @@ public class MetaInfConfiguration extends AbstractConfiguration context.setAttribute(METAINF_RESOURCES, null); context.setAttribute(METAINF_TLDS, null); } - - public void addResource (WebAppContext context, String attribute, Resource jar) - { - @SuppressWarnings("unchecked") - List<Resource> list = (List<Resource>)context.getAttribute(attribute); - if (list==null) - { - list=new ArrayList<Resource>(); - context.setAttribute(attribute,list); - } - if (!list.contains(jar)) - list.add(jar); - } - - - protected void processEntry(WebAppContext context, URI jarUri, JarEntry entry) - { - String name = entry.getName(); - - if (!name.startsWith("META-INF/")) - return; - - try - { - if (name.equals("META-INF/web-fragment.xml") && context.isConfigurationDiscovered()) - { - addResource(context,METAINF_FRAGMENTS,Resource.newResource(jarUri)); - } - else if (name.equals("META-INF/resources/") && context.isConfigurationDiscovered()) - { - addResource(context,METAINF_RESOURCES,Resource.newResource("jar:"+jarUri+"!/META-INF/resources")); - } - else - { - String lcname = name.toLowerCase(Locale.ENGLISH); - if (lcname.endsWith(".tld")) - { - addResource(context,METAINF_TLDS,Resource.newResource("jar:"+jarUri+"!/"+name)); - } - } - } - catch(Exception e) - { - context.getServletContext().log(jarUri+"!/"+name,e); - } - } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index d0d4085161..d9ebc2fb9e 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -27,7 +27,9 @@ import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.eclipse.jetty.server.Connector; @@ -54,7 +56,7 @@ public class WebInfConfiguration extends AbstractConfiguration * If set, to a list of URLs, these resources are added to the context * resource base as a resource collection. */ - public static final String RESOURCE_URLS = "org.eclipse.jetty.resources"; + public static final String RESOURCE_DIRS = "org.eclipse.jetty.resources"; protected Resource _preUnpackBaseResource; @@ -140,7 +142,7 @@ public class WebInfConfiguration extends AbstractConfiguration } } webInfJarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match - + //No pattern to appy to classes, just add to metadata context.getMetaData().setWebInfClassesDirs(findClassDirs(context)); } @@ -175,7 +177,7 @@ public class WebInfConfiguration extends AbstractConfiguration // Look for extra resource @SuppressWarnings("unchecked") - List<Resource> resources = (List<Resource>)context.getAttribute(RESOURCE_URLS); + Set<Resource> resources = (Set<Resource>)context.getAttribute(RESOURCE_DIRS); if (resources!=null) { Resource[] collection=new Resource[resources.size()+1]; |