/** * Check to see if the ServletContainerIntializer loaded via the ServiceLoader came from a jar * that is excluded by the fragment ordering. See ServletSpec 3.0 p.85. * * @param context * @param sci * @return true if excluded */ public boolean isFromExcludedJar( WebAppContext context, ServletContainerInitializer sci, Resource sciResource) throws Exception { if (sci == null) throw new IllegalArgumentException("ServletContainerInitializer null"); if (context == null) throw new IllegalArgumentException("WebAppContext null"); if (LOG.isDebugEnabled()) LOG.debug("Checking {} for jar exclusion", sci); // A ServletContainerInitializer that came from the container's classpath cannot be excluded by // an ordering // of WEB-INF/lib jars if (isFromContainerClassPath(context, sci)) return false; List<Resource> orderedJars = context.getMetaData().getOrderedWebInfJars(); // If no ordering, nothing is excluded if (context.getMetaData().getOrdering() == null) return false; // there is an ordering, but there are no jars resulting from the ordering, everything excluded if (orderedJars.isEmpty()) return true; if (sciResource == null) return false; // not from a jar therefore not from WEB-INF so not excludable URI loadingJarURI = sciResource.getURI(); boolean found = false; Iterator<Resource> itor = orderedJars.iterator(); while (!found && itor.hasNext()) { Resource r = itor.next(); found = r.getURI().equals(loadingJarURI); } return !found; }
/** * Scan jars in WEB-INF/lib * * @param context * @param parser * @throws Exception */ public void parseWebInfLib(final WebAppContext context, final AnnotationParser parser) throws Exception { List<FragmentDescriptor> frags = context.getMetaData().getFragments(); // email from Rajiv Mordani jsrs 315 7 April 2010 // jars that do not have a web-fragment.xml are still considered fragments // they have to participate in the ordering ArrayList<URI> webInfUris = new ArrayList<URI>(); List<Resource> jars = context.getMetaData().getOrderedWebInfJars(); // No ordering just use the jars in any order if (jars == null || jars.isEmpty()) jars = context.getMetaData().getWebInfJars(); _webInfLibStats = new CounterStatistic(); for (Resource r : jars) { // for each jar, we decide which set of annotations we need to parse for final Set<Handler> handlers = new HashSet<Handler>(); FragmentDescriptor f = getFragmentFromJar(r, frags); // if its from a fragment jar that is metadata complete, we should skip scanning for // @webservlet etc // but yet we still need to do the scanning for the classes on behalf of the // servletcontainerinitializers // if a jar has no web-fragment.xml we scan it (because it is not excluded by the ordering) // or if it has a fragment we scan it if it is not metadata complete if (f == null || !isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) { // register the classinheritance handler if there is one if (_classInheritanceHandler != null) handlers.add(_classInheritanceHandler); // register the handlers for the @HandlesTypes values that are themselves annotations if // there are any 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)) handlers.addAll(_discoverableAnnotationHandlers); if (_parserTasks != null) { ParserTask task = new ParserTask(parser, handlers, r, _webAppClassNameResolver); _parserTasks.add(task); _webInfLibStats.increment(); if (LOG.isDebugEnabled()) task.setStatistic(new TimeStatistic()); } } } }
/** * Here is the order in which jars and osgi artifacts are scanned for discoverable annotations. * * <ol> * <li>The container jars are scanned. * <li>The WEB-INF/classes are scanned * <li>The osgi fragment to the web bundle are parsed. * <li>The WEB-INF/lib are scanned * <li>The required bundles are parsed * </ol> */ @Override public void parseWebInfLib( WebAppContext context, org.eclipse.jetty.annotations.AnnotationParser parser) throws Exception { AnnotationParser oparser = (AnnotationParser) parser; Bundle webbundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); Set<Bundle> fragAndRequiredBundles = (Set<Bundle>) context.getAttribute(OSGiWebInfConfiguration.FRAGMENT_AND_REQUIRED_BUNDLES); if (fragAndRequiredBundles != null) { // index and scan fragments for (Bundle bundle : fragAndRequiredBundles) { // skip bundles that have been uninstalled since we discovered them if (bundle.getState() == Bundle.UNINSTALLED) continue; Resource bundleRes = oparser.indexBundle(bundle); if (!context.getMetaData().getWebInfJars().contains(bundleRes)) { context.getMetaData().addWebInfJar(bundleRes); } if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) { // a fragment indeed: parseFragmentBundle(context, oparser, webbundle, bundle); } } } // scan ourselves oparser.indexBundle(webbundle); parseWebBundle(context, oparser, webbundle); // scan the WEB-INF/lib super.parseWebInfLib(context, parser); if (fragAndRequiredBundles != null) { // scan the required bundles for (Bundle requiredBundle : fragAndRequiredBundles) { // skip bundles that have been uninstalled since we discovered them if (requiredBundle.getState() == Bundle.UNINSTALLED) continue; if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null) { // a bundle indeed: parseRequiredBundle(context, oparser, webbundle, requiredBundle); } } } }
/** * Scan classes in WEB-INF/classes * * @param context * @param parser * @throws Exception */ public void parseWebInfClasses(final WebAppContext context, final AnnotationParser parser) throws Exception { Set<Handler> handlers = new HashSet<Handler>(); handlers.addAll(_discoverableAnnotationHandlers); if (_classInheritanceHandler != null) handlers.add(_classInheritanceHandler); handlers.addAll(_containerInitializerAnnotationHandlers); _webInfClassesStats = new CounterStatistic(); for (Resource dir : context.getMetaData().getWebInfClassesDirs()) { if (_parserTasks != null) { ParserTask task = new ParserTask(parser, handlers, dir, _webAppClassNameResolver); _parserTasks.add(task); _webInfClassesStats.increment(); if (LOG.isDebugEnabled()) task.setStatistic(new TimeStatistic()); } } }
public void doHandle(Class clazz) { // Check that the PreDestroy is on a class that we're interested in if (Util.supportsPostConstructPreDestroy(clazz)) { Method[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method m = (Method) methods[i]; if (m.isAnnotationPresent(PreDestroy.class)) { if (m.getParameterTypes().length != 0) throw new IllegalStateException(m + " has parameters"); if (m.getReturnType() != Void.TYPE) throw new IllegalStateException(m + " is not void"); if (m.getExceptionTypes().length != 0) throw new IllegalStateException(m + " throws checked exceptions"); if (Modifier.isStatic(m.getModifiers())) throw new IllegalStateException(m + " is static"); // ServletSpec 3.0 p80 If web.xml declares even one predestroy then all predestroys // in fragments must be ignored. Otherwise, they are additive. MetaData metaData = _context.getMetaData(); Origin origin = metaData.getOrigin("pre-destroy"); if (origin != null && (origin == Origin.WebXml || origin == Origin.WebDefaults || origin == Origin.WebOverride)) return; PreDestroyCallback callback = new PreDestroyCallback(); callback.setTarget(clazz.getName(), m.getName()); LifeCycleCallbackCollection lifecycles = (LifeCycleCallbackCollection) _context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); if (lifecycles == null) { lifecycles = new LifeCycleCallbackCollection(); _context.setAttribute( LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, lifecycles); } lifecycles.add(callback); } } } }
/** * Scan jars on container path. * * @param context * @param parser * @throws Exception */ public void parseContainerPath(final WebAppContext context, final AnnotationParser parser) throws Exception { // always parse for discoverable annotations as well as class hierarchy and // servletcontainerinitializer related annotations final Set<Handler> handlers = new HashSet<Handler>(); handlers.addAll(_discoverableAnnotationHandlers); handlers.addAll(_containerInitializerAnnotationHandlers); if (_classInheritanceHandler != null) handlers.add(_classInheritanceHandler); _containerPathStats = new CounterStatistic(); for (Resource r : context.getMetaData().getContainerResources()) { // queue it up for scanning if using multithreaded mode if (_parserTasks != null) { ParserTask task = new ParserTask(parser, handlers, r, _containerClassNameResolver); _parserTasks.add(task); _containerPathStats.increment(); if (LOG.isDebugEnabled()) task.setStatistic(new TimeStatistic()); } } }
/** * @see * org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext) */ @Override public void configure(WebAppContext context) throws Exception { context.addDecorator(new AnnotationDecorator(context)); // Even if metadata is complete, we still need to scan for ServletContainerInitializers - if // there are any if (!context.getMetaData().isMetaDataComplete()) { // If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we // need to search for annotations if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered()) { _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context)); _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context)); _discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context)); } } // Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, // then we need to scan all the // classes so we can call their onStartup() methods correctly createServletContainerInitializerAnnotationHandlers( context, getNonExcludedInitializers(context)); if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) scanForAnnotations(context); // Resolve container initializers List<ContainerInitializer> initializers = (List<ContainerInitializer>) context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS); if (initializers != null && initializers.size() > 0) { Map<String, Set<String>> map = (Map<String, Set<String>>) context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); if (map == null) LOG.warn("ServletContainerInitializers: detected. Class hierarchy: empty"); for (ContainerInitializer i : initializers) i.resolveClasses(context, map); } }
/** * @param context * @return list of non-excluded {@link ServletContainerInitializer}s * @throws Exception */ public List<ServletContainerInitializer> getNonExcludedInitializers(WebAppContext context) throws Exception { ArrayList<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>(); // We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect long start = 0; ClassLoader old = Thread.currentThread().getContextClassLoader(); ServiceLoader<ServletContainerInitializer> loadedInitializers = null; try { if (LOG.isDebugEnabled()) start = System.nanoTime(); Thread.currentThread().setContextClassLoader(context.getClassLoader()); loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class); } finally { Thread.currentThread().setContextClassLoader(old); } if (LOG.isDebugEnabled()) LOG.debug( "Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime() - start), TimeUnit.NANOSECONDS))); Map<ServletContainerInitializer, Resource> sciResourceMap = new HashMap<ServletContainerInitializer, Resource>(); ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context); // Get initial set of SCIs that aren't from excluded jars or excluded by the // containerExclusionPattern, or excluded // because containerInitializerOrdering omits it for (ServletContainerInitializer sci : loadedInitializers) { if (matchesExclusionPattern(sci)) { if (LOG.isDebugEnabled()) LOG.debug("{} excluded by pattern", sci); continue; } Resource sciResource = getJarFor(sci); if (isFromExcludedJar(context, sci, sciResource)) { if (LOG.isDebugEnabled()) LOG.debug("{} is from excluded jar", sci); continue; } // check containerInitializerOrdering doesn't exclude it String name = sci.getClass().getName(); if (initializerOrdering != null && (!initializerOrdering.hasWildcard() && initializerOrdering.getIndexOf(name) < 0)) { if (LOG.isDebugEnabled()) LOG.debug("{} is excluded by ordering", sci); continue; } sciResourceMap.put(sci, sciResource); } // Order the SCIs that are included if (initializerOrdering != null && !initializerOrdering.isDefaultOrder()) { if (LOG.isDebugEnabled()) LOG.debug("Ordering ServletContainerInitializers with " + initializerOrdering); // There is an ordering that is not just "*". // Arrange ServletContainerInitializers according to the ordering of classnames given, // irrespective of coming from container or webapp classpaths nonExcludedInitializers.addAll(sciResourceMap.keySet()); Collections.sort( nonExcludedInitializers, new ServletContainerInitializerComparator(initializerOrdering)); } else { // No jetty-specific ordering specified, or just the wildcard value "*" specified. // Fallback to ordering the ServletContainerInitializers according to: // container classpath first, WEB-INF/classes then WEB-INF/lib (obeying any web.xml jar // ordering) // no web.xml ordering defined, add SCIs in any order if (context.getMetaData().getOrdering() == null) { if (LOG.isDebugEnabled()) LOG.debug("No web.xml ordering, ServletContainerInitializers in random order"); nonExcludedInitializers.addAll(sciResourceMap.keySet()); } else { if (LOG.isDebugEnabled()) LOG.debug( "Ordering ServletContainerInitializers with ordering {}", context.getMetaData().getOrdering()); for (Map.Entry<ServletContainerInitializer, Resource> entry : sciResourceMap.entrySet()) { // add in SCIs from the container classpath if (entry.getKey().getClass().getClassLoader() == context.getClassLoader().getParent()) nonExcludedInitializers.add(entry.getKey()); else if (entry.getValue() == null) // add in SCIs not in a jar, as they must be from WEB-INF/classes and can't // be ordered nonExcludedInitializers.add(entry.getKey()); } // add SCIs according to the ordering of its containing jar for (Resource webInfJar : context.getMetaData().getOrderedWebInfJars()) { for (Map.Entry<ServletContainerInitializer, Resource> entry : sciResourceMap.entrySet()) { if (webInfJar.equals(entry.getValue())) nonExcludedInitializers.add(entry.getKey()); } } } } if (LOG.isDebugEnabled()) { int i = 0; for (ServletContainerInitializer sci : nonExcludedInitializers) LOG.debug("ServletContainerInitializer: {} {}", (++i), sci.getClass().getName()); } return nonExcludedInitializers; }
/** * Perform scanning of classes for annotations * * @param context * @throws Exception */ protected void scanForAnnotations(WebAppContext context) throws Exception { AnnotationParser parser = createAnnotationParser(); _parserTasks = new ArrayList<ParserTask>(); long start = 0; if (LOG.isDebugEnabled()) LOG.debug( "Annotation scanning commencing: webxml={}, metadatacomplete={}, configurationDiscovered={}, multiThreaded={}, maxScanWait={}", context.getServletContext().getEffectiveMajorVersion(), context.getMetaData().isMetaDataComplete(), context.isConfigurationDiscovered(), isUseMultiThreading(context), getMaxScanWait(context)); 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); start = System.nanoTime(); // execute scan, either effectively synchronously (1 thread only), or asynchronously (limited by // number of processors available) final Semaphore task_limit = (isUseMultiThreading(context) ? new Semaphore(Runtime.getRuntime().availableProcessors()) : new Semaphore(1)); final CountDownLatch latch = new CountDownLatch(_parserTasks.size()); final MultiException me = new MultiException(); 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(getMaxScanWait(context), TimeUnit.SECONDS); if (LOG.isDebugEnabled()) { for (ParserTask p : _parserTasks) LOG.debug( "Scanned {} in {}ms", p.getResource(), TimeUnit.MILLISECONDS.convert(p.getStatistic().getElapsed(), TimeUnit.NANOSECONDS)); LOG.debug( "Scanned {} container path jars, {} WEB-INF/lib jars, {} WEB-INF/classes dirs in {}ms for context {}", _containerPathStats.getTotal(), _webInfLibStats.getTotal(), _webInfClassesStats.getTotal(), (TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS)), context); } if (timeout) me.add(new Exception("Timeout scanning annotations")); me.ifExceptionThrow(); }