diff --git a/VERSION.txt b/VERSION.txt index a90d1d183a6..140808f494d 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -225,6 +225,20 @@ jetty-9.4.0.M0 - 03 June 2016 + 609 ignore failing test + 610 Ignore failing test +jetty-9.3.13.M0 - 30 September 2016 + + 277 Proxy servlet does not handle HTTP status 100 correctly + + 870 TLS protocol exclusion broken for SslContextFactory(String) + + 915 The jetty-maven-plugin:stop goal doesn't stop everything completely + + 918 Support certificates hot reload + + 930 Add module instructions to SSL section + + 943 Docs: Error in 'Embedding Jetty' page - example 'FileServer' + + 948 9.4.0.RC0 jetty-distribution invalid config etc/jetty-http2c.xml + + 955 Response listeners not invoked when using Connection.send() + + 959 CompleteListener invoked twice for HTTP/2 transport and response content + + 960 Async I/O spin when reading early EOF + + 965 Link from High Load docs to Garbage Collection Tuning is broken + + 966 Remove usages of ConcurrentArrayQueue + jetty-9.3.12.v20160915 - 15 September 2016 + 56 Fix authn issues in LdapLoginModule + 131 Improve Connector Statistic names and values 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 fcab2ac596a..048d2844270 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 @@ -21,7 +21,6 @@ package org.eclipse.jetty.annotations; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; -import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -48,6 +47,7 @@ import org.eclipse.jetty.plus.annotation.ContainerInitializer; import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -81,8 +81,6 @@ public class AnnotationConfiguration extends AbstractConfiguration protected List _containerInitializerAnnotationHandlers = new ArrayList(); protected List _parserTasks; - protected WebAppClassNameResolver _webAppClassNameResolver; - protected ContainerClassNameResolver _containerClassNameResolver; protected CounterStatistic _containerPathStats; protected CounterStatistic _webInfLibStats; @@ -138,15 +136,13 @@ public class AnnotationConfiguration extends AbstractConfiguration protected Exception _exception; protected final AnnotationParser _parser; protected final Set _handlers; - protected final ClassNameResolver _resolver; protected final Resource _resource; protected TimeStatistic _stat; - public ParserTask (AnnotationParser parser, Sethandlers, Resource resource, ClassNameResolver resolver) + public ParserTask (AnnotationParser parser, Sethandlers, Resource resource) { _parser = parser; _handlers = handlers; - _resolver = resolver; _resource = resource; } @@ -160,7 +156,7 @@ public class AnnotationConfiguration extends AbstractConfiguration if (_stat != null) _stat.start(); if (_parser != null) - _parser.parse(_handlers, _resource, _resolver); + _parser.parse(_handlers, _resource); if (_stat != null) _stat.end(); return null; @@ -174,83 +170,7 @@ public class AnnotationConfiguration extends AbstractConfiguration public Resource getResource() { return _resource; - } - - } - - /** - * WebAppClassNameResolver - * - * Checks to see if a classname belongs to hidden or visible packages when scanning, - * and whether a classname that is a duplicate should override a previously - * scanned classname. - * - * This is analogous to the management of classes that the WebAppClassLoader is doing, - * however we don't want to load the classes at this point so we are doing it on - * the name only. - * - */ - public class WebAppClassNameResolver implements ClassNameResolver - { - private WebAppContext _context; - - public WebAppClassNameResolver (WebAppContext context) - { - _context = context; - } - - 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; - } - } - - - /** - * ContainerClassNameResolver - * - * Checks to see if a classname belongs to a hidden or visible package - * when scanning for annotations and thus whether it should be excluded from - * consideration or not. - * - * This is analogous to the management of classes that the WebAppClassLoader is doing, - * however we don't want to load the classes at this point so we are doing it on - * the name only. - * - */ - public class ContainerClassNameResolver implements ClassNameResolver - { - private WebAppContext _context; - - public ContainerClassNameResolver (WebAppContext context) - { - _context = context; - } - public boolean isExcluded (String name) - { - if (_context.isSystemClass(name)) return false; - if (_context.isServerClass(name)) return true; - return false; - } - - public boolean shouldOverride (String name) - { - //visiting the container classpath, - if (_context.isParentLoaderPriority()) - return true; - return false; - } + } } @@ -390,8 +310,6 @@ public class AnnotationConfiguration extends AbstractConfiguration @Override public void preConfigure(final WebAppContext context) throws Exception { - _webAppClassNameResolver = new WebAppClassNameResolver(context); - _containerClassNameResolver = new ContainerClassNameResolver(context); String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN); _sciExcludePattern = (tmp==null?null:Pattern.compile(tmp)); } @@ -445,15 +363,15 @@ public class AnnotationConfiguration extends AbstractConfiguration if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) scanForAnnotations(context); - + // Resolve container initializers List initializers = - (List)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS); + (List)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS); if (initializers != null && initializers.size()>0) { Map> map = ( Map>) context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); for (ContainerInitializer i : initializers) - i.resolveClasses(context,map); + i.resolveClasses(context,map); } } @@ -723,23 +641,7 @@ public class AnnotationConfiguration extends AbstractConfiguration public Resource getJarFor (ServletContainerInitializer service) throws MalformedURLException, IOException { - //try the thread context classloader to get the jar that loaded the class - URL jarURL = Thread.currentThread().getContextClassLoader().getResource(service.getClass().getName().replace('.','/')+".class"); - - //if for some reason that failed (eg we're in osgi and the TCCL does not know about the service) try the classloader that - //loaded the class - if (jarURL == null) - jarURL = service.getClass().getClassLoader().getResource(service.getClass().getName().replace('.','/')+".class"); - - String loadingJarName = jarURL.toString(); - - int i = loadingJarName.indexOf(".jar"); - if (i < 0) - return null; //not from a jar - - loadingJarName = loadingJarName.substring(0,i+4); - loadingJarName = (loadingJarName.startsWith("jar:")?loadingJarName.substring(4):loadingJarName); - return Resource.newResource(loadingJarName); + return TypeUtil.getLoadedFrom(service.getClass()); } /** @@ -760,26 +662,41 @@ public class AnnotationConfiguration extends AbstractConfiguration 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)) + { + if (LOG.isDebugEnabled()) + LOG.debug("!Excluded {} from container classpath", sci); return false; + } //If no ordering, nothing is excluded if (context.getMetaData().getOrdering() == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("!Excluded {} no ordering", sci); return false; - + } List orderedJars = context.getMetaData().getOrderedWebInfJars(); //there is an ordering, but there are no jars resulting from the ordering, everything excluded if (orderedJars.isEmpty()) + { + if (LOG.isDebugEnabled()) + LOG.debug("Excluded {} empty ordering", sci); return true; + } if (sciResource == null) - return false; //not from a jar therefore not from WEB-INF so not excludable + { + //not from a jar therefore not from WEB-INF so not excludable + if (LOG.isDebugEnabled()) + LOG.debug("!Excluded {} not from jar", sci); + return false; + } URI loadingJarURI = sciResource.getURI(); boolean found = false; @@ -790,10 +707,11 @@ public class AnnotationConfiguration extends AbstractConfiguration found = r.getURI().equals(loadingJarURI); } + if (LOG.isDebugEnabled()) + LOG.debug("{}Excluded {} found={}",found?"!":"",sci,found); return !found; } - /** * Test if the ServletContainerIntializer is excluded by the * o.e.j.containerInitializerExclusionPattern @@ -808,7 +726,8 @@ public class AnnotationConfiguration extends AbstractConfiguration return false; //test if name of class matches the regex - if (LOG.isDebugEnabled()) LOG.debug("Checking {} against containerInitializerExclusionPattern",sci.getClass().getName()); + if (LOG.isDebugEnabled()) + LOG.debug("Checking {} against containerInitializerExclusionPattern",sci.getClass().getName()); return _sciExcludePattern.matcher(sci.getClass().getName()).matches(); } @@ -866,17 +785,18 @@ public class AnnotationConfiguration extends AbstractConfiguration //because containerInitializerOrdering omits it for (ServletContainerInitializer sci:_loadedInitializers) { - if (matchesExclusionPattern(sci)) { - if (LOG.isDebugEnabled()) LOG.debug("{} excluded by pattern", 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); + if (LOG.isDebugEnabled()) + LOG.debug("{} is from excluded jar", sci); continue; } @@ -885,7 +805,8 @@ public class AnnotationConfiguration extends AbstractConfiguration if (initializerOrdering != null && (!initializerOrdering.hasWildcard() && initializerOrdering.getIndexOf(name) < 0)) { - if (LOG.isDebugEnabled()) LOG.debug("{} is excluded by ordering", sci); + if (LOG.isDebugEnabled()) + LOG.debug("{} is excluded by ordering", sci); continue; } @@ -912,12 +833,14 @@ public class AnnotationConfiguration extends AbstractConfiguration //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"); + 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()); + if (LOG.isDebugEnabled()) + LOG.debug("Ordering ServletContainerInitializers with ordering {}",context.getMetaData().getOrdering()); for (Map.Entry entry:sciResourceMap.entrySet()) { //add in SCIs from the container classpath @@ -992,7 +915,7 @@ public class AnnotationConfiguration extends AbstractConfiguration //queue it up for scanning if using multithreaded mode if (_parserTasks != null) { - ParserTask task = new ParserTask(parser, handlers, r, _containerClassNameResolver); + ParserTask task = new ParserTask(parser, handlers, r); _parserTasks.add(task); _containerPathStats.increment(); if (LOG.isDebugEnabled()) @@ -1054,7 +977,7 @@ public class AnnotationConfiguration extends AbstractConfiguration if (_parserTasks != null) { - ParserTask task = new ParserTask(parser, handlers,r, _webAppClassNameResolver); + ParserTask task = new ParserTask(parser, handlers,r); _parserTasks.add (task); _webInfLibStats.increment(); if (LOG.isDebugEnabled()) @@ -1087,7 +1010,7 @@ public class AnnotationConfiguration extends AbstractConfiguration { if (_parserTasks != null) { - ParserTask task = new ParserTask(parser, handlers, dir, _webAppClassNameResolver); + ParserTask task = new ParserTask(parser, handlers, dir); _parserTasks.add(task); _webInfClassesStats.increment(); if (LOG.isDebugEnabled()) 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 01a1c475d72..5ee998e9e67 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 @@ -544,28 +544,24 @@ public class AnnotationParser * * @param handlers the set of handlers to find class * @param className the class name to parse - * @param resolver the class name resolver to use * @throws Exception if unable to parse */ - public void parse (Set handlers, String className, ClassNameResolver resolver) + public void parse (Set handlers, String className) throws Exception { if (className == null) return; - if (!resolver.isExcluded(className)) + if (!isParsed(className)) { - if (!isParsed(className) || resolver.shouldOverride(className)) + className = className.replace('.', '/')+".class"; + URL resource = Loader.getResource(className); + if (resource!= null) { - className = className.replace('.', '/')+".class"; - URL resource = Loader.getResource(className); - if (resource!= null) + Resource r = Resource.newResource(resource); + try (InputStream is = r.getInputStream()) { - Resource r = Resource.newResource(resource); - try (InputStream is = r.getInputStream()) - { - scanClass(handlers, null, is); - } + scanClass(handlers, null, is); } } } @@ -578,33 +574,30 @@ public class AnnotationParser * * @param handlers the handlers to look for class in * @param clazz the class to look for - * @param resolver the resolver to look up class with * @param visitSuperClasses if true, also visit super classes for parse * @throws Exception if unable to parse class */ - public void parse (Set handlers, Class clazz, ClassNameResolver resolver, boolean visitSuperClasses) + public void parse (Set handlers, Class clazz, boolean visitSuperClasses) throws Exception { Class cz = clazz; while (cz != null) { - if (!resolver.isExcluded(cz.getName())) + if (!isParsed(cz.getName())) { - if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName())) + String nameAsResource = cz.getName().replace('.', '/')+".class"; + URL resource = Loader.getResource(nameAsResource); + if (resource!= null) { - String nameAsResource = cz.getName().replace('.', '/')+".class"; - URL resource = Loader.getResource(nameAsResource); - if (resource!= null) + Resource r = Resource.newResource(resource); + try (InputStream is = r.getInputStream()) { - Resource r = Resource.newResource(resource); - try (InputStream is = r.getInputStream()) - { - scanClass(handlers, null, is); - } + scanClass(handlers, null, is); } } } + if (visitSuperClasses) cz = cz.getSuperclass(); else @@ -619,16 +612,15 @@ public class AnnotationParser * * @param handlers the set of handlers to look for class in * @param classNames the class name - * @param resolver the class name resolver * @throws Exception if unable to parse */ - public void parse (Set handlers, String[] classNames, ClassNameResolver resolver) + public void parse (Set handlers, String[] classNames) throws Exception { if (classNames == null) return; - parse(handlers, Arrays.asList(classNames), resolver); + parse(handlers, Arrays.asList(classNames)); } @@ -637,10 +629,9 @@ public class AnnotationParser * * @param handlers the set of handlers to look for class in * @param classNames the class names - * @param resolver the class name resolver * @throws Exception if unable to parse */ - public void parse (Set handlers, List classNames, ClassNameResolver resolver) + public void parse (Set handlers, List classNames) throws Exception { MultiException me = new MultiException(); @@ -649,7 +640,7 @@ public class AnnotationParser { try { - if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s)))) + if (!isParsed(s)) { s = s.replace('.', '/')+".class"; URL resource = Loader.getResource(s); @@ -677,10 +668,9 @@ public class AnnotationParser * * @param handlers the set of handlers to look for classes in * @param dir the resource directory to look for classes - * @param resolver the class name resolver * @throws Exception if unable to parse */ - protected void parseDir (Set handlers, Resource dir, ClassNameResolver resolver) + protected void parseDir (Set handlers, Resource dir) throws Exception { // skip dirs whose name start with . (ie hidden) @@ -696,7 +686,7 @@ public class AnnotationParser { Resource res = dir.addPath(files[f]); if (res.isDirectory()) - parseDir(handlers, res, resolver); + parseDir(handlers, res); else { //we've already verified the directories, so just verify the class file name @@ -706,7 +696,7 @@ public class AnnotationParser try { String name = res.getName(); - if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name)))) + if (!isParsed(name)) { Resource r = Resource.newResource(res.getURL()); if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);}; @@ -741,10 +731,9 @@ public class AnnotationParser * @param loader the classloader for the classes * @param visitParents if true, visit parent classloaders too * @param nullInclusive if true, an empty pattern means all names match, if false, none match - * @param resolver the class name resolver * @throws Exception if unable to parse */ - public void parse (final Set handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver) + public void parse (final Set handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive) throws Exception { if (loader==null) @@ -762,7 +751,7 @@ public class AnnotationParser { try { - parseJarEntry(handlers, Resource.newResource(jarUri), entry, resolver); + parseJarEntry(handlers, Resource.newResource(jarUri), entry); } catch (Exception e) { @@ -782,10 +771,9 @@ public class AnnotationParser * * @param handlers the handlers to look for classes in * @param uris the uris for the jars - * @param resolver the class name resolver * @throws Exception if unable to parse */ - public void parse (final Set handlers, final URI[] uris, final ClassNameResolver resolver) + public void parse (final Set handlers, final URI[] uris) throws Exception { if (uris==null) @@ -797,7 +785,7 @@ public class AnnotationParser { try { - parse(handlers, uri, resolver); + parse(handlers, uri); } catch (Exception e) { @@ -812,16 +800,15 @@ public class AnnotationParser * * @param handlers the handlers to look for classes in * @param uri the uri for the jar - * @param resolver the class name resolver * @throws Exception if unable to parse */ - public void parse (final Set handlers, URI uri, final ClassNameResolver resolver) + public void parse (final Set handlers, URI uri) throws Exception { if (uri == null) return; - parse (handlers, Resource.newResource(uri), resolver); + parse (handlers, Resource.newResource(uri)); } @@ -830,10 +817,9 @@ public class AnnotationParser * * @param handlers the handlers to look for classes in * @param r the resource to parse - * @param resolver the class name resolver * @throws Exception if unable to parse */ - public void parse (final Set handlers, Resource r, final ClassNameResolver resolver) + public void parse (final Set handlers, Resource r) throws Exception { if (r == null) @@ -841,14 +827,14 @@ public class AnnotationParser if (r.exists() && r.isDirectory()) { - parseDir(handlers, r, resolver); + parseDir(handlers, r); return; } String fullname = r.toString(); if (fullname.endsWith(".jar")) { - parseJar(handlers, r, resolver); + parseJar(handlers, r); return; } @@ -872,10 +858,9 @@ public class AnnotationParser * * @param handlers the handlers to look for classes in * @param jarResource the jar resource to parse - * @param resolver the class name resolver * @throws Exception if unable to parse */ - protected void parseJar (Set handlers, Resource jarResource, final ClassNameResolver resolver) + protected void parseJar (Set handlers, Resource jarResource) throws Exception { if (jarResource == null) @@ -899,7 +884,7 @@ public class AnnotationParser { try { - parseJarEntry(handlers, jarResource, entry, resolver); + parseJarEntry(handlers, jarResource, entry); } catch (Exception e) { @@ -927,10 +912,9 @@ public class AnnotationParser * @param handlers the handlers to look for classes in * @param jar the jar resource to parse * @param entry the entry in the jar resource to parse - * @param resolver the class name resolver * @throws Exception if unable to parse */ - protected void parseJarEntry (Set handlers, Resource jar, JarEntry entry, final ClassNameResolver resolver) + protected void parseJarEntry (Set handlers, Resource jar, JarEntry entry) throws Exception { if (jar == null || entry == null) @@ -947,9 +931,7 @@ public class AnnotationParser { String shortName = name.replace('/', '.').substring(0,name.length()-6); - if ((resolver == null) - || - (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName)))) + if (!isParsed(shortName)) { Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name); if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);}; diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassNameResolver.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassNameResolver.java deleted file mode 100644 index 84905cb5d28..00000000000 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassNameResolver.java +++ /dev/null @@ -1,42 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.annotations; - - - -public interface ClassNameResolver -{ - /** - * Based on the execution context, should the class represented - * by "name" be excluded from consideration? - * @param name the name to test - * @return true if classname is excluded - */ - public boolean isExcluded (String name); - - - /** - * Based on the execution context, if a duplicate class - * represented by "name" is detected, should the existing - * one be overridden or not? - * @param name the name to test - * @return true if name should be overridden - */ - public boolean shouldOverride (String name); -} 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 643c3f84077..5a7ed8b56ac 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 @@ -74,6 +74,12 @@ public class TestAnnotationInheritance return; annotatedMethods.add(info.getClassInfo().getClassName()+"."+info.getMethodName()); } + + @Override + public String toString() + { + return annotatedClassNames.toString()+annotatedMethods+annotatedFields; + } } @After @@ -93,18 +99,7 @@ public class TestAnnotationInheritance SampleHandler handler = new SampleHandler(); AnnotationParser parser = new AnnotationParser(); - parser.parse(Collections.singleton(handler), classNames, new ClassNameResolver () - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - }); + parser.parse(Collections.singleton(handler), classNames); //check we got 2 class annotations assertEquals(2, handler.annotatedClassNames.size()); @@ -129,18 +124,7 @@ public class TestAnnotationInheritance { SampleHandler handler = new SampleHandler(); AnnotationParser parser = new AnnotationParser(); - parser.parse(Collections.singleton(handler), ClassB.class, new ClassNameResolver () - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - }, true); + parser.parse(Collections.singleton(handler), ClassB.class, true); //check we got 2 class annotations assertEquals(2, handler.annotatedClassNames.size()); @@ -160,46 +144,6 @@ public class TestAnnotationInheritance assertEquals("org.eclipse.jetty.annotations.ClassA.m", handler.annotatedFields.get(0)); } - @Test - public void testExclusions() throws Exception - { - AnnotationParser parser = new AnnotationParser(); - SampleHandler handler = new SampleHandler(); - parser.parse(Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver() - { - public boolean isExcluded(String name) - { - return true; - } - - public boolean shouldOverride(String name) - { - return false; - } - }); - assertEquals (0, handler.annotatedClassNames.size()); - assertEquals (0, handler.annotatedFields.size()); - assertEquals (0, handler.annotatedMethods.size()); - - handler.annotatedClassNames.clear(); - handler.annotatedFields.clear(); - handler.annotatedMethods.clear(); - - parser.parse (Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver() - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - }); - assertEquals (1, handler.annotatedClassNames.size()); - } - @Test public void testTypeInheritanceHandling() throws Exception { @@ -218,7 +162,7 @@ public class TestAnnotationInheritance classNames.add(InterfaceD.class.getName()); classNames.add(Foo.class.getName()); - parser.parse(Collections.singleton(handler), classNames, null); + parser.parse(Collections.singleton(handler), classNames); assertNotNull(map); assertFalse(map.isEmpty()); 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 d86283b32a5..002e41bfa5c 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 @@ -111,19 +111,7 @@ public class TestAnnotationParser } //long start = System.currentTimeMillis(); - parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames,new ClassNameResolver() - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - - }); + parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames); //long end = System.currentTimeMillis(); //System.err.println("Time to parse class: " + ((end - start))); @@ -162,7 +150,7 @@ public class TestAnnotationParser } } - parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames,null); + parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames); } @Test @@ -171,7 +159,7 @@ public class TestAnnotationParser File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar"); AnnotationParser parser = new AnnotationParser(); Set emptySet = Collections.emptySet(); - parser.parse(emptySet, badClassesJar.toURI(),null); + parser.parse(emptySet, badClassesJar.toURI()); // only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here } @@ -196,7 +184,7 @@ public class TestAnnotationParser AnnotationParser parser = new AnnotationParser(); // Parse - parser.parse(Collections.singleton(tracker), basedir.toURI(),null); + parser.parse(Collections.singleton(tracker), basedir.toURI()); // 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 e26f856035c..01ed2f35d3a 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 @@ -73,18 +73,7 @@ public class TestServletAnnotations TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results); - parser.parse(Collections.singleton(handler), classes, new ClassNameResolver () - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - }); + parser.parse(Collections.singleton(handler), classes); assertEquals(1, results.size()); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java b/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java index 8bae3b8a993..c469d2e4719 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java @@ -18,26 +18,13 @@ package org.eclipse.jetty.client; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; -import java.util.zip.ZipException; - -import org.eclipse.jetty.util.BufferUtil; /** * {@link ContentDecoder} for the "gzip" encoding. + * */ -public class GZIPContentDecoder implements ContentDecoder +public class GZIPContentDecoder extends org.eclipse.jetty.http.GZIPContentDecoder implements ContentDecoder { - private final Inflater inflater = new Inflater(true); - private final byte[] bytes; - private byte[] output; - private State state; - private int size; - private int value; - private byte flags; public GZIPContentDecoder() { @@ -46,285 +33,7 @@ public class GZIPContentDecoder implements ContentDecoder public GZIPContentDecoder(int bufferSize) { - this.bytes = new byte[bufferSize]; - reset(); - } - - /** - * {@inheritDoc} - *

If the decoding did not produce any output, for example because it consumed gzip header - * or trailer bytes, it returns a buffer with zero capacity.

- *

This method never returns null.

- *

The given {@code buffer}'s position will be modified to reflect the bytes consumed during - * the decoding.

- *

The decoding may be finished without consuming the buffer completely if the buffer contains - * gzip bytes plus other bytes (either plain or gzipped).

- */ - @Override - public ByteBuffer decode(ByteBuffer buffer) - { - try - { - while (buffer.hasRemaining()) - { - byte currByte = buffer.get(); - switch (state) - { - case INITIAL: - { - buffer.position(buffer.position() - 1); - state = State.ID; - break; - } - case ID: - { - value += (currByte & 0xFF) << 8 * size; - ++size; - if (size == 2) - { - if (value != 0x8B1F) - throw new ZipException("Invalid gzip bytes"); - state = State.CM; - } - break; - } - case CM: - { - if ((currByte & 0xFF) != 0x08) - throw new ZipException("Invalid gzip compression method"); - state = State.FLG; - break; - } - case FLG: - { - flags = currByte; - state = State.MTIME; - size = 0; - value = 0; - break; - } - case MTIME: - { - // Skip the 4 MTIME bytes - ++size; - if (size == 4) - state = State.XFL; - break; - } - case XFL: - { - // Skip XFL - state = State.OS; - break; - } - case OS: - { - // Skip OS - state = State.FLAGS; - break; - } - case FLAGS: - { - buffer.position(buffer.position() - 1); - if ((flags & 0x04) == 0x04) - { - state = State.EXTRA_LENGTH; - size = 0; - value = 0; - } - else if ((flags & 0x08) == 0x08) - state = State.NAME; - else if ((flags & 0x10) == 0x10) - state = State.COMMENT; - else if ((flags & 0x2) == 0x2) - { - state = State.HCRC; - size = 0; - value = 0; - } - else - state = State.DATA; - break; - } - case EXTRA_LENGTH: - { - value += (currByte & 0xFF) << 8 * size; - ++size; - if (size == 2) - state = State.EXTRA; - break; - } - case EXTRA: - { - // Skip EXTRA bytes - --value; - if (value == 0) - { - // Clear the EXTRA flag and loop on the flags - flags &= ~0x04; - state = State.FLAGS; - } - break; - } - case NAME: - { - // Skip NAME bytes - if (currByte == 0) - { - // Clear the NAME flag and loop on the flags - flags &= ~0x08; - state = State.FLAGS; - } - break; - } - case COMMENT: - { - // Skip COMMENT bytes - if (currByte == 0) - { - // Clear the COMMENT flag and loop on the flags - flags &= ~0x10; - state = State.FLAGS; - } - break; - } - case HCRC: - { - // Skip HCRC - ++size; - if (size == 2) - { - // Clear the HCRC flag and loop on the flags - flags &= ~0x02; - state = State.FLAGS; - } - break; - } - case DATA: - { - buffer.position(buffer.position() - 1); - while (true) - { - int decoded = inflate(bytes); - if (decoded == 0) - { - if (inflater.needsInput()) - { - if (buffer.hasRemaining()) - { - byte[] input = new byte[buffer.remaining()]; - buffer.get(input); - inflater.setInput(input); - } - else - { - if (output != null) - { - ByteBuffer result = ByteBuffer.wrap(output); - output = null; - return result; - } - break; - } - } - else if (inflater.finished()) - { - int remaining = inflater.getRemaining(); - buffer.position(buffer.limit() - remaining); - state = State.CRC; - size = 0; - value = 0; - break; - } - else - { - throw new ZipException("Invalid inflater state"); - } - } - else - { - if (output == null) - { - // Save the inflated bytes and loop to see if we have finished - output = Arrays.copyOf(bytes, decoded); - } - else - { - // Accumulate inflated bytes and loop to see if we have finished - byte[] newOutput = Arrays.copyOf(output, output.length + decoded); - System.arraycopy(bytes, 0, newOutput, output.length, decoded); - output = newOutput; - } - } - } - break; - } - case CRC: - { - value += (currByte & 0xFF) << 8 * size; - ++size; - if (size == 4) - { - // From RFC 1952, compliant decoders need not to verify the CRC - state = State.ISIZE; - size = 0; - value = 0; - } - break; - } - case ISIZE: - { - value += (currByte & 0xFF) << 8 * size; - ++size; - if (size == 4) - { - if (value != inflater.getBytesWritten()) - throw new ZipException("Invalid input size"); - - ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output); - reset(); - return result; - } - break; - } - default: - throw new ZipException(); - } - } - return BufferUtil.EMPTY_BUFFER; - } - catch (ZipException x) - { - throw new RuntimeException(x); - } - } - - private int inflate(byte[] bytes) throws ZipException - { - try - { - return inflater.inflate(bytes); - } - catch (DataFormatException x) - { - throw new ZipException(x.getMessage()); - } - } - - private void reset() - { - inflater.reset(); - Arrays.fill(bytes, (byte)0); - output = null; - state = State.INITIAL; - size = 0; - value = 0; - flags = 0; - } - - protected boolean isFinished() - { - return state == State.INITIAL; + super(null,bufferSize); } /** @@ -351,9 +60,4 @@ public class GZIPContentDecoder implements ContentDecoder return new GZIPContentDecoder(bufferSize); } } - - private enum State - { - INITIAL, ID, CM, FLG, MTIME, XFL, OS, FLAGS, EXTRA_LENGTH, EXTRA, NAME, COMMENT, HCRC, DATA, CRC, ISIZE - } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java index 484b7811065..ab65d91a16d 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java @@ -37,6 +37,7 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.CountingCallback; +import org.eclipse.jetty.util.component.Destroyable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -470,6 +471,7 @@ public abstract class HttpReceiver */ protected void reset() { + destroyDecoder(decoder); decoder = null; } @@ -482,9 +484,18 @@ public abstract class HttpReceiver */ protected void dispose() { + destroyDecoder(decoder); decoder = null; } + private static void destroyDecoder(ContentDecoder decoder) + { + if (decoder instanceof Destroyable) + { + ((Destroyable)decoder).destroy(); + } + } + public boolean abort(HttpExchange exchange, Throwable failure) { // Update the state to avoid more response processing. diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/GZIPContentDecoderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/GZIPContentDecoderTest.java index 9c1b460d02a..207f6d3e1fe 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/GZIPContentDecoderTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/GZIPContentDecoderTest.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.toolchain.test.TestTracker; import org.junit.Rule; import org.junit.Test; +@Deprecated public class GZIPContentDecoderTest { @Rule diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 2b1b1318c22..463922fbe8d 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -329,7 +329,7 @@ jetty.home=${assembly-directory} jetty.base=${assembly-directory} - --add-to-start=deploy,websocket,ext,resources,jsp,jstl,http + --add-to-start=server,deploy,websocket,ext,resources,jsp,jstl,http diff --git a/jetty-documentation/src/main/asciidoc/configuring/deploying/quickstart-webapp.adoc b/jetty-documentation/src/main/asciidoc/configuring/deploying/quickstart-webapp.adoc index b3651c24f4b..c7cacca4760 100644 --- a/jetty-documentation/src/main/asciidoc/configuring/deploying/quickstart-webapp.adoc +++ b/jetty-documentation/src/main/asciidoc/configuring/deploying/quickstart-webapp.adoc @@ -31,18 +31,24 @@ Not only does the `quickstart-web.xml` contain all the discovered Servlets, Filt With the quickstart mechanism, Jetty is able to entirely bypass all scanning and discovery modes and start a webapp in a predictable and fast way. Tests have shown that webapps that took many seconds to scan and deploy can now be deployed in a few hundred milliseconds. +Additionally, if debug logging is enabled, the generated quickstart information is tagged with the origin of every element, which can be useful for debugging purposes. ==== Setting up Quickstart -To use quickstart the module has to be available to the Jetty instance. -In a standard Jetty distribution it can be configured with the following command: +===== Prerequisites + +====== Jetty Distribution + +In a standard Jetty distribution the quickstart module can be configured with the following command: [source, screen, subs="{sub-order}"] ---- $ java -jar $JETTY_HOME/start.jar --add-to-start=quickstart ---- -In a Maven project this is done by adding a dependency on the artifact ID `jetty-quickstart`. +====== Embedded + +In a Maven project you add a dependency on the artifact `jetty-quickstart`. [source, xml, subs="{sub-order}"] ---- @@ -53,26 +59,62 @@ In a Maven project this is done by adding a dependency on the artifact ID `jetty ---- -Additionally, for those using Maven, the link:#get-up-and-running[Jetty Maven Plugin] has a goal, link:#jetty-effective-web-xml[`jetty:effective-web-xml`], which performs quickstart operations. -It should be noted, however, that the Jetty Maven Plugin also includes additional items on it's classpath which may not be needed by the webapp. -Deployed webapps need to be instances of link:{JDURL}/org/eclipse/jetty/quickstart/QuickStartWebApp.html[`org.eclipse.jetty.quickstart.QuickStartWebApp`] rather than the normal `org.eclipse.jetty.webapp.WebAppContext`. -If a web application already has a `webapps/myapp.xml` file, simply change the class in the `Configure` element. -Otherwise, create a `webapps/myapp.xml` file as follows: + +===== Configuration + +Webapps need to be instances of link:{JDURL}/org/eclipse/jetty/quickstart/QuickStartWebApp.html[`org.eclipse.jetty.quickstart.QuickStartWebApp`] rather than the normal `org.eclipse.jetty.webapp.WebAppContext`. + +`org.eclipse.jetty.quickstart.QuickStartWebApp` instances offer the same setters as the familiar `org.eclipse.jetty.webapp.WebAppContext`, with the addition of: + +autoPreconfigure:: + (true/false). + If true, the first time the webapp is run, the WEB-INF/quickstart-web.xml is generated BEFORE the webapp is deployed. + Subsequent runs use the previously generated quickstart file. + +====== In XML +If a web application already has a context xml file, eg `webapps/myapp.xml` file, simply change the class in the `Configure` element. +Otherwise, create a context xml file with the following information (in addition to the usual setting of contextPath, war etc): [source, xml, subs="{sub-order}"] ---- - + - /benchmark.war - /benchmark true ---- -For embedded implementations of Jetty, invoking the link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[`org.eclipse.jetty.quickstart.PreconfigureQuickStartWar`] class can be used to configure war files for quickstart deployment. -This will create the `quickstart-web.xml` before the first deployment. +====== In Code + +Create an instance of link:{JDURL}/org/eclipse/jetty/quickstart/QuickStartWebApp.html[`org.eclipse.jetty.quickstart.QuickStartWebApp`] rather than the normal `org.eclipse.jetty.webapp.WebAppContext`. You then use the QuickStartWebApp instance in exactly the same way that you would a WebAppContext. + +Here's a snippet: + +[source, java] +---- + QuickStartWebApp webapp = new QuickStartWebApp(); + webapp.setAutoPreconfigure(true); +---- + + +====== Pre-generating the quickstart-web.xml file + +Rather than use the `autoPreconfigure` feature of the QuickStartWebApp - which lazily generates the `quickstart-web.xml` file - you can eagerly pre-generate it for an existing war by invoking as a main class link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[`org.eclipse.jetty.quickstart.PreconfigureQuickStartWar`]. +Note that you will need to provide all necessary jetty jars on the command line classpath. +This will unpack the war if necessary, and create the `quickstart-web.xml` before the first deployment: + + +[source, screen, subs="{sub-order}"] +---- +$ java -cp [jetty classpath] org.eclipse.jetty.quickstart.PreconfigureQuickStartWar myapp.war +---- + +Run the class with no arguments to see other runtime options. + +Alternatively, you could use the link:#get-up-and-running[Jetty Maven Plugin] goal link:#jetty-effective-web-xml[`jetty:effective-web-xml`]: this will generate quickstart information, but print it to stderr. +The goal provides a configuration option to save the output to a file, which you can then copy into your webapp's WEB-INF dir. +Note that as the Jetty Maven Plugin is a general tool for running webapps, it may have more jars on its classpath than are needed by your application, and thus may generate extra quickstart information: we recommend that you use this goal only as a quick guide to the type of information that quickstart generates. // ==== Preconfiguring the web application // @@ -81,14 +123,8 @@ This will create the `quickstart-web.xml` before the first deployment. // // It is also possible to preconfigure a war file manually by running the class link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[org.eclipse.jetty.quickstart.PreconfigureQuickStartWar] with the jetty-all-uber (aggregate) jar: // -// [source, screen, subs="{sub-order}"] -// ---- -// $ java -cp jetty-all-{VERSION}-uber.jar org.eclipse.jetty.quickstart.PreconfigureQuickStartWar myapp.war -// ---- // // This will create the `quickstart-web.xml` file before the first deployment. -// Note that this can also be a good debugging tool for discovered configuration and if run with debug turned on the origin of every element is included in the `quickstart-web.xml` file. -// Run the class with no arguments to see other runtime options. ==== Avoiding TLD Scans with precompiled JSPs diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-datastore.mod b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-datastore.mod new file mode 100644 index 00000000000..43d1ea6e7b2 --- /dev/null +++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud-datastore.mod @@ -0,0 +1,72 @@ +[description] +Enables GCloud Datastore API and implementation + +[Tags] +3rdparty +gcloud + +[depends] +gcloud +jcl-api +jcl-impl + +[files] +maven://com.google.cloud/google-cloud-datastore/0.3.0|lib/gcloud/google-cloud-datastore-0.3.0.jar +maven://com.google.cloud/google-cloud-core/0.3.0|lib/gcloud/google-cloud-core-0.3.0.jar +maven://com.google.auth/google-auth-library-credentials/0.3.1|lib/gcloud/google-auth-library-credentials-0.3.1.jar +maven://com.google.auth/google-auth-library-oauth2-http/0.3.1|lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar +maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar +maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar +maven://com.google.http-client/google-http-client/1.21.0|lib/gcloud/google-http-client-1.21.0.jar +maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar +maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar +maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar +maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar +maven://com.google.oauth-client/google-oauth-client/1.21.0|lib/gcloud/google-oauth-client-1.21.0.jar +maven://com.google.guava/guava/19.0|lib/gcloud/guava-19.0.jar +maven://com.google.api-client/google-api-client-appengine/1.21.0|lib/gcloud/google-api-client-appengine-1.21.0.jar +maven://com.google.oauth-client/google-oauth-client-appengine/1.21.0|lib/gcloud/google-oauth-client-appengine-1.21.0.jar +maven://com.google.oauth-client/google-oauth-client-servlet/1.21.0|lib/gcloud/google-oauth-client-servlet-1.21.0.jar +maven://com.google.http-client/google-http-client-jdo/1.21.0|lib/gcloud/google-http-client-jdo-1.21.0.jar +maven://com.google.api-client/google-api-client-servlet/1.21.0|lib/gcloud/google-api-client-servlet-1.21.0.jar +maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar +maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar +maven://com.google.http-client/google-http-client-appengine/1.21.0|lib/gcloud/google-http-client-appengine-1.21.0.jar +maven://com.google.http-client/google-http-client-jackson/1.21.0|lib/gcloud/google-http-client-jackson-1.21.0.jar +maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar +maven://joda-time/joda-time/2.9.2|lib/gcloud/joda-time-2.9.2.jar +maven://com.google.protobuf/protobuf-java/3.0.0-beta-3|lib/gcloud/protobuf-java-3.0.0-beta-3.jar +maven://com.google.api/gax/0.0.16|lib/gcloud/gax-0.0.16.jar +maven://io.grpc/grpc-all/0.15.0|lib/gcloud/grpc-all-0.15.0.jar +maven://io.grpc/grpc-auth/0.15.0|lib/gcloud/grpc-auth-0.15.0.jar +maven://io.grpc/grpc-netty/0.15.0|lib/gcloud/grpc-netty-0.15.0.jar +maven://io.netty/netty-codec-http2/4.1.1.Final|lib/gcloud/netty-codec-http2-4.1.1.jar +maven://io.netty/netty-codec-http/4.1.1.Final|lib/gcloud/netty-codec-http-4.1.1.Final.jar +maven://io.netty/netty-codec/4.1.1.Final|lib/gcloud/netty-codec-4.1.1.Final.jar +maven://io.netty/netty-handler/4.1.1.Final|lib/gcloud/netty-handler-4.1.1.Final.jar +maven://io.netty/netty-buffer/4.1.1.Final|lib/gcloud/netty-buffer-4.1.1.Final.jar +maven://io.netty/netty-common/4.1.1.Final|lib/gcloud/netty-common-4.1.1.Final.jar +maven://io.netty/netty-transport/4.1.1.Final|lib/gcloud/netty-transport-4.1.1.Final.jar +maven://io.netty/netty-resolver/4.1.1.Final|lib/gcloud/netty-resolver-4.1.1.Final.jar +maven://io.grpc/grpc-okhttp/0.15.0|lib/gcloud/grpc-okhttp-0.15.0.jar +maven://com.squareup.okio/okio/1.6.0|lib/gcloud/okio-1.6.0.jar +maven://com.squareup.okhttp/okhttp/2.5.0|lib/gcloud/okhttp-2.5.0.jar +maven://io.grpc/grpc-protobuf-nano/0.15.0|lib/gcloud/grpc-protobuf-nano-0.15.0.jar +maven://com.google.protobuf.nano/protobuf-javanano/3.0.0-alpha-5|lib/gcloud/protobuf-javanano-3.0.0-alpha-5.jar +maven://io.grpc/grpc-stub/0.15.0|lib/gcloud/grpc-stub-0.15.0.jar +maven://io.grpc/grpc-protobuf/0.15.0|lib/gcloud/grpc-protobuf-0.15.0.jar +maven://com.google.protobuf/protobuf-java-util/3.0.0-beta-3|lib/gcloud/protobuf-java-util-3.0.0-beta-3.jar +maven://com.google.code.gson/gson/2.3|lib/gcloud/gson-2.3.jar +maven://io.grpc/grpc-protobuf-lite/0.15.0|lib/gcloud/grpc-protobuf-lite-0.15.0.jar +maven://io.grpc/grpc-core/0.15.0|lib/gcloud/grpc-core-0.15.0.jar +maven://com.google.auto.value/auto-value/1.1|lib/gcloud/auto-value-1.1.jar +maven://com.google.inject/guice/4.0|lib/gcloud/guice-4.0.jar +maven://javax.inject/javax.inject/1|lib/gcloud/javax.inject-1.jar +maven://aopalliance/aopalliance/1.0|lib/gcloud/aopalliance-1.0.jar +maven://com.google.api.grpc/grpc-google-common-protos/0.0.7|lib/gcloud/grpc-google-common-protos-0.0.7.jar +maven://org.json/json/20151123|lib/gcloud/json-20151123.jar +maven://com.google.cloud.datastore/datastore-v1-protos/1.0.1|lib/gcloud/datastore-v1-protos-1.0.1-beta.jar +maven://com.google.cloud.datastore/datastore-v1-proto-client/1.1.0|lib/gcloud/datastore-v1-proto-client-1.1.0.jar +maven://com.google.http-client/google-http-client-protobuf/1.20.0|lib/gcloud/google-http-client-protobuf-1.20.0.jar +maven://com.google.api-client/google-api-client/1.20.0|lib/gcloud/google-api-client-1.20.0.jar +maven://com.google.guava/guava-jdk5/13.0|lib/gcloud/guava-jdk5-13.0.jar diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud.mod b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud.mod new file mode 100644 index 00000000000..2039280ee34 --- /dev/null +++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud.mod @@ -0,0 +1,18 @@ +[description] +Control GCloud API classpath + +[Tags] +3rdparty +gcloud + +[lib] +lib/gcloud/*.jar + +[license] +GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license. +https://github.com/GoogleCloudPlatform/gcloud-java +http://www.apache.org/licenses/LICENSE-2.0.html + +[ini-template] +## Hide the gcloud libraries from deployed webapps +jetty.webapp.addServerClasses,=file:${jetty.base}/lib/gcloud/ diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/sessions/gcloud/index.yaml b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud/index.yaml similarity index 100% rename from jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/sessions/gcloud/index.yaml rename to jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/gcloud/index.yaml diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/session-store-gcloud.mod b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/session-store-gcloud.mod index 7e8d0afcf73..c5e933032fa 100644 --- a/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/session-store-gcloud.mod +++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/modules/session-store-gcloud.mod @@ -1,92 +1,28 @@ [description] Enables GCloudDatastore session management. +[Tags] +session +gcloud + [provides] session-store [depends] +gcloud-datastore annotations webapp sessions -jcl-api -jcl-impl [files] -basehome:etc/sessions/gcloud/index.yaml|etc/index.yaml -maven://com.google.cloud/google-cloud-datastore/0.3.0|lib/gcloud/google-cloud-datastore-0.3.0.jar -maven://com.google.cloud/google-cloud-core/0.3.0|lib/gcloud/google-cloud-core-0.3.0.jar -maven://com.google.auth/google-auth-library-credentials/0.3.1|lib/gcloud/google-auth-library-credentials-0.3.1.jar -maven://com.google.auth/google-auth-library-oauth2-http/0.3.1|lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar -maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar -maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar -maven://com.google.http-client/google-http-client/1.21.0|lib/gcloud/google-http-client-1.21.0.jar -maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar -maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar -maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar -maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar -maven://com.google.oauth-client/google-oauth-client/1.21.0|lib/gcloud/google-oauth-client-1.21.0.jar -maven://com.google.guava/guava/19.0|lib/gcloud/guava-19.0.jar -maven://com.google.api-client/google-api-client-appengine/1.21.0|lib/gcloud/google-api-client-appengine-1.21.0.jar -maven://com.google.oauth-client/google-oauth-client-appengine/1.21.0|lib/gcloud/google-oauth-client-appengine-1.21.0.jar -maven://com.google.oauth-client/google-oauth-client-servlet/1.21.0|lib/gcloud/google-oauth-client-servlet-1.21.0.jar -maven://com.google.http-client/google-http-client-jdo/1.21.0|lib/gcloud/google-http-client-jdo-1.21.0.jar -maven://com.google.api-client/google-api-client-servlet/1.21.0|lib/gcloud/google-api-client-servlet-1.21.0.jar -maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar -maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar -maven://com.google.http-client/google-http-client-appengine/1.21.0|lib/gcloud/google-http-client-appengine-1.21.0.jar -maven://com.google.http-client/google-http-client-jackson/1.21.0|lib/gcloud/google-http-client-jackson-1.21.0.jar -maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar -maven://joda-time/joda-time/2.9.2|lib/gcloud/joda-time-2.9.2.jar -maven://com.google.protobuf/protobuf-java/3.0.0-beta-3|lib/gcloud/protobuf-java-3.0.0-beta-3.jar -maven://com.google.api/gax/0.0.16|lib/gcloud/gax-0.0.16.jar -maven://io.grpc/grpc-all/0.15.0|lib/gcloud/grpc-all-0.15.0.jar -maven://io.grpc/grpc-auth/0.15.0|lib/gcloud/grpc-auth-0.15.0.jar -maven://io.grpc/grpc-netty/0.15.0|lib/gcloud/grpc-netty-0.15.0.jar -maven://io.netty/netty-codec-http2/4.1.1.Final|lib/gcloud/netty-codec-http2-4.1.1.jar -maven://io.netty/netty-codec-http/4.1.1.Final|lib/gcloud/netty-codec-http-4.1.1.Final.jar -maven://io.netty/netty-codec/4.1.1.Final|lib/gcloud/netty-codec-4.1.1.Final.jar -maven://io.netty/netty-handler/4.1.1.Final|lib/gcloud/netty-handler-4.1.1.Final.jar -maven://io.netty/netty-buffer/4.1.1.Final|lib/gcloud/netty-buffer-4.1.1.Final.jar -maven://io.netty/netty-common/4.1.1.Final|lib/gcloud/netty-common-4.1.1.Final.jar -maven://io.netty/netty-transport/4.1.1.Final|lib/gcloud/netty-transport-4.1.1.Final.jar -maven://io.netty/netty-resolver/4.1.1.Final|lib/gcloud/netty-resolver-4.1.1.Final.jar -maven://io.grpc/grpc-okhttp/0.15.0|lib/gcloud/grpc-okhttp-0.15.0.jar -maven://com.squareup.okio/okio/1.6.0|lib/gcloud/okio-1.6.0.jar -maven://com.squareup.okhttp/okhttp/2.5.0|lib/gcloud/okhttp-2.5.0.jar -maven://io.grpc/grpc-protobuf-nano/0.15.0|lib/gcloud/grpc-protobuf-nano-0.15.0.jar -maven://com.google.protobuf.nano/protobuf-javanano/3.0.0-alpha-5|lib/gcloud/protobuf-javanano-3.0.0-alpha-5.jar -maven://io.grpc/grpc-stub/0.15.0|lib/gcloud/grpc-stub-0.15.0.jar -maven://io.grpc/grpc-protobuf/0.15.0|lib/gcloud/grpc-protobuf-0.15.0.jar -maven://com.google.protobuf/protobuf-java-util/3.0.0-beta-3|lib/gcloud/protobuf-java-util-3.0.0-beta-3.jar -maven://com.google.code.gson/gson/2.3|lib/gcloud/gson-2.3.jar -maven://io.grpc/grpc-protobuf-lite/0.15.0|lib/gcloud/grpc-protobuf-lite-0.15.0.jar -maven://io.grpc/grpc-core/0.15.0|lib/gcloud/grpc-core-0.15.0.jar -maven://com.google.auto.value/auto-value/1.1|lib/gcloud/auto-value-1.1.jar -maven://com.google.inject/guice/4.0|lib/gcloud/guice-4.0.jar -maven://javax.inject/javax.inject/1|lib/gcloud/javax.inject-1.jar -maven://aopalliance/aopalliance/1.0|lib/gcloud/aopalliance-1.0.jar -maven://com.google.api.grpc/grpc-google-common-protos/0.0.7|lib/gcloud/grpc-google-common-protos-0.0.7.jar -maven://org.json/json/20151123|lib/gcloud/json-20151123.jar -maven://com.google.cloud.datastore/datastore-v1-protos/1.0.1|lib/gcloud/datastore-v1-protos-1.0.1-beta.jar -maven://com.google.cloud.datastore/datastore-v1-proto-client/1.1.0|lib/gcloud/datastore-v1-proto-client-1.1.0.jar -maven://com.google.http-client/google-http-client-protobuf/1.20.0|lib/gcloud/google-http-client-protobuf-1.20.0.jar -maven://com.google.api-client/google-api-client/1.20.0|lib/gcloud/google-api-client-1.20.0.jar -maven://com.google.guava/guava-jdk5/13.0|lib/gcloud/guava-jdk5-13.0.jar - - +basehome:modules/gcloud/index.yaml|etc/index.yaml [lib] lib/jetty-gcloud-session-manager-${jetty.version}.jar -lib/gcloud/*.jar [xml] etc/sessions/gcloud/session-store.xml -[license] -GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license. -https://github.com/GoogleCloudPlatform/gcloud-java -http://www.apache.org/licenses/LICENSE-2.0.html - [ini-template] ## GCloudDatastore Session config diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java index 92704f99e99..72fa3ce5d10 100644 --- a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java +++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java @@ -808,6 +808,9 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore { //need to assume that the problem is the index doesn't exist, because there //is no specific code for that + if (LOG.isDebugEnabled()) + LOG.debug("Check for indexes", e); + return false; } } diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml index 6f2fb0a890c..d73a0ab7667 100644 --- a/jetty-gcloud/pom.xml +++ b/jetty-gcloud/pom.xml @@ -13,7 +13,7 @@ Jetty :: GCloud - 0.3.0 + 0.4.0 diff --git a/jetty-home/src/main/resources/modules/hawtio.mod b/jetty-home/src/main/resources/modules/hawtio.mod index fcc34d15048..2191e37595c 100644 --- a/jetty-home/src/main/resources/modules/hawtio.mod +++ b/jetty-home/src/main/resources/modules/hawtio.mod @@ -1,6 +1,9 @@ [description] Deploys the Hawtio console as a webapplication. +[Tags] +3rdparty + [depend] stats deploy @@ -13,6 +16,7 @@ etc/hawtio.xml etc/hawtio/ lib/hawtio/ https://oss.sonatype.org/content/repositories/public/io/hawt/hawtio-default/1.4.16/hawtio-default-1.4.16.war|lib/hawtio/hawtio.war +basehome:modules/hawtio/hawtio.xml|etc/hawtio.xml [license] Hawtio is a redhat JBoss project released under the Apache License, v2.0 diff --git a/jetty-home/src/main/resources/etc/hawtio.xml b/jetty-home/src/main/resources/modules/hawtio/hawtio.xml similarity index 100% rename from jetty-home/src/main/resources/etc/hawtio.xml rename to jetty-home/src/main/resources/modules/hawtio/hawtio.xml diff --git a/jetty-home/src/main/resources/modules/jamon.mod b/jetty-home/src/main/resources/modules/jamon.mod index 96a331c65ad..5fe87c68754 100644 --- a/jetty-home/src/main/resources/modules/jamon.mod +++ b/jetty-home/src/main/resources/modules/jamon.mod @@ -1,6 +1,9 @@ [description] Deploys the JAMon webapplication +[Tags] +3rdparty + [depend] stats deploy @@ -14,6 +17,7 @@ etc/jamon.xml lib/jamon/ maven://com.jamonapi/jamon/2.81|lib/jamon/jamon-2.81.jar maven://com.jamonapi/jamon_war/2.81/war|lib/jamon/jamon.war +basehome:modules/jamon/jamon.xml|etc/jamon.xml [lib] lib/jamon/**.jar diff --git a/jetty-home/src/main/resources/etc/jamon.xml b/jetty-home/src/main/resources/modules/jamon/jamon.xml similarity index 100% rename from jetty-home/src/main/resources/etc/jamon.xml rename to jetty-home/src/main/resources/modules/jamon/jamon.xml diff --git a/jetty-home/src/main/resources/modules/jminix.mod b/jetty-home/src/main/resources/modules/jminix.mod index 4bd48c9e72d..6310834a283 100644 --- a/jetty-home/src/main/resources/modules/jminix.mod +++ b/jetty-home/src/main/resources/modules/jminix.mod @@ -1,6 +1,9 @@ [description] Deploys the Jminix JMX Console within the server +[Tags] +3rdparty + [depend] stats jmx @@ -26,6 +29,7 @@ maven://commons-collections/commons-collections/3.2|lib/jminix/commons-collectio maven://net.sf.ezmorph/ezmorph/1.0.6|lib/jminix/ezmorph-1.0.6.jar maven://org.jgroups/jgroups/2.12.1.3.Final|lib/jminix/jgroups-2.12.1.3.Final.jar maven://org.jasypt/jasypt/1.8|lib/jminix/jasypt-1.8.jar +basehome:modules/jminix/jminix.xml|etc/jminix.xml [lib] lib/jminix/**.jar diff --git a/jetty-home/src/main/resources/etc/jminix.xml b/jetty-home/src/main/resources/modules/jminix/jminix.xml similarity index 100% rename from jetty-home/src/main/resources/etc/jminix.xml rename to jetty-home/src/main/resources/modules/jminix/jminix.xml diff --git a/jetty-home/src/main/resources/modules/jolokia.mod b/jetty-home/src/main/resources/modules/jolokia.mod index efe8a59185e..a5d21a86ba4 100644 --- a/jetty-home/src/main/resources/modules/jolokia.mod +++ b/jetty-home/src/main/resources/modules/jolokia.mod @@ -1,6 +1,9 @@ [description] Deploys the Jolokia console as a web application. +[Tags] +3rdparty + [depend] stats deploy @@ -11,6 +14,7 @@ etc/jolokia.xml [files] maven://org.jolokia/jolokia-war/1.2.2/war|lib/jolokia/jolokia.war +basehome:modules/jolokia/jolokia.xml|etc/jolokia.xml [license] Jolokia is released under the Apache License 2.0 diff --git a/jetty-home/src/main/resources/etc/jolokia.xml b/jetty-home/src/main/resources/modules/jolokia/jolokia.xml similarity index 100% rename from jetty-home/src/main/resources/etc/jolokia.xml rename to jetty-home/src/main/resources/modules/jolokia/jolokia.xml diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml index 8455a9d3248..9802a9f8a20 100644 --- a/jetty-http/pom.xml +++ b/jetty-http/pom.xml @@ -18,6 +18,11 @@ jetty-util ${project.version} + + org.eclipse.jetty + jetty-io + ${project.version} + org.eclipse.jetty.toolchain jetty-test-helper diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/GZIPContentDecoder.java b/jetty-http/src/main/java/org/eclipse/jetty/http/GZIPContentDecoder.java new file mode 100644 index 00000000000..122421c66c3 --- /dev/null +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/GZIPContentDecoder.java @@ -0,0 +1,423 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.http; + +import java.nio.ByteBuffer; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; +import java.util.zip.ZipException; + +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.component.Destroyable; + +/** + * Decoder for the "gzip" encoding. + *

+ * A decoder that inflates gzip compressed data that has been + * optimized for async usage with minimal data copies. + */ +public class GZIPContentDecoder implements Destroyable +{ + private final Inflater _inflater = new Inflater(true); + private final ByteBufferPool _pool; + private final int _bufferSize; + private State _state; + private int _size; + private int _value; + private byte _flags; + private ByteBuffer _inflated; + + public GZIPContentDecoder() + { + this(null,2048); + } + + public GZIPContentDecoder(int bufferSize) + { + this(null,bufferSize); + } + + public GZIPContentDecoder(ByteBufferPool pool, int bufferSize) + { + _bufferSize = bufferSize; + _pool = pool; + reset(); + } + + /** Inflate compressed data from a buffer. + * + * @param compressed Buffer containing compressed data. + * @return Buffer containing inflated data. + */ + public ByteBuffer decode(ByteBuffer compressed) + { + decodeChunks(compressed); + if (BufferUtil.isEmpty(_inflated) || _state==State.CRC || _state==State.ISIZE ) + return BufferUtil.EMPTY_BUFFER; + + ByteBuffer result = _inflated; + _inflated = null; + return result; + } + + /** Called when a chunk of data is inflated. + *

The default implementation aggregates all the chunks + * into a single buffer returned from {@link #decode(ByteBuffer)}. + * Derived implementations may choose to consume chunks individually + * and return false to prevent further inflation until a subsequent + * call to {@link #decode(ByteBuffer)} or {@link #decodeChunks(ByteBuffer)}. + * + * @param chunk The inflated chunk of data + * @return False if inflating should continue, or True if the call + * to {@link #decodeChunks(ByteBuffer)} or {@link #decode(ByteBuffer)} + * should return, allowing back pressure of compressed data. + */ + protected boolean decodedChunk(ByteBuffer chunk) + { + if (_inflated==null) + { + _inflated=chunk; + } + else + { + int size = _inflated.remaining() + chunk.remaining(); + if (size<=_inflated.capacity()) + { + BufferUtil.append(_inflated,chunk); + BufferUtil.put(chunk,_inflated); + release(chunk); + } + else + { + ByteBuffer bigger=acquire(size); + int pos=BufferUtil.flipToFill(bigger); + BufferUtil.put(_inflated,bigger); + BufferUtil.put(chunk,bigger); + BufferUtil.flipToFlush(bigger,pos); + release(_inflated); + release(chunk); + _inflated = bigger; + } + } + return false; + } + + /** + * Inflate compressed data. + *

Inflation continues until the compressed block end is reached, there is no + * more compressed data or a call to {@link #decodedChunk(ByteBuffer)} returns true. + * @param compressed Buffer of compressed data to inflate + */ + protected void decodeChunks(ByteBuffer compressed) + { + ByteBuffer buffer = null; + try + { + while (true) + { + switch (_state) + { + case INITIAL: + { + _state = State.ID; + break; + } + + case FLAGS: + { + if ((_flags & 0x04) == 0x04) + { + _state = State.EXTRA_LENGTH; + _size = 0; + _value = 0; + } + else if ((_flags & 0x08) == 0x08) + _state = State.NAME; + else if ((_flags & 0x10) == 0x10) + _state = State.COMMENT; + else if ((_flags & 0x2) == 0x2) + { + _state = State.HCRC; + _size = 0; + _value = 0; + } + else + { + _state = State.DATA; + continue; + } + break; + } + + case DATA: + { + while (true) + { + if (buffer==null) + buffer = acquire(_bufferSize); + + try + { + int length = _inflater.inflate(buffer.array(),buffer.arrayOffset(),buffer.capacity()); + buffer.limit(length); + } + catch (DataFormatException x) + { + throw new ZipException(x.getMessage()); + } + + if (buffer.hasRemaining()) + { + ByteBuffer chunk = buffer; + buffer = null; + if (decodedChunk(chunk)) + return; + } + else if (_inflater.needsInput()) + { + if (!compressed.hasRemaining()) + return; + if (compressed.hasArray()) + { + _inflater.setInput(compressed.array(),compressed.arrayOffset()+compressed.position(),compressed.remaining()); + compressed.position(compressed.limit()); + } + else + { + // TODO use the pool + byte[] input = new byte[compressed.remaining()]; + compressed.get(input); + _inflater.setInput(input); + } + } + else if (_inflater.finished()) + { + int remaining = _inflater.getRemaining(); + compressed.position(compressed.limit() - remaining); + _state = State.CRC; + _size = 0; + _value = 0; + break; + } + } + continue; + } + + default: + break; + } + + if (!compressed.hasRemaining()) + break; + + byte currByte = compressed.get(); + switch (_state) + { + case ID: + { + _value += (currByte & 0xFF) << 8 * _size; + ++_size; + if (_size == 2) + { + if (_value != 0x8B1F) + throw new ZipException("Invalid gzip bytes"); + _state = State.CM; + } + break; + } + case CM: + { + if ((currByte & 0xFF) != 0x08) + throw new ZipException("Invalid gzip compression method"); + _state = State.FLG; + break; + } + case FLG: + { + _flags = currByte; + _state = State.MTIME; + _size = 0; + _value = 0; + break; + } + case MTIME: + { + // Skip the 4 MTIME bytes + ++_size; + if (_size == 4) + _state = State.XFL; + break; + } + case XFL: + { + // Skip XFL + _state = State.OS; + break; + } + case OS: + { + // Skip OS + _state = State.FLAGS; + break; + } + case EXTRA_LENGTH: + { + _value += (currByte & 0xFF) << 8 * _size; + ++_size; + if (_size == 2) + _state = State.EXTRA; + break; + } + case EXTRA: + { + // Skip EXTRA bytes + --_value; + if (_value == 0) + { + // Clear the EXTRA flag and loop on the flags + _flags &= ~0x04; + _state = State.FLAGS; + } + break; + } + case NAME: + { + // Skip NAME bytes + if (currByte == 0) + { + // Clear the NAME flag and loop on the flags + _flags &= ~0x08; + _state = State.FLAGS; + } + break; + } + case COMMENT: + { + // Skip COMMENT bytes + if (currByte == 0) + { + // Clear the COMMENT flag and loop on the flags + _flags &= ~0x10; + _state = State.FLAGS; + } + break; + } + case HCRC: + { + // Skip HCRC + ++_size; + if (_size == 2) + { + // Clear the HCRC flag and loop on the flags + _flags &= ~0x02; + _state = State.FLAGS; + } + break; + } + case CRC: + { + _value += (currByte & 0xFF) << 8 * _size; + ++_size; + if (_size == 4) + { + // From RFC 1952, compliant decoders need not to verify the CRC + _state = State.ISIZE; + _size = 0; + _value = 0; + } + break; + } + case ISIZE: + { + _value += (currByte & 0xFF) << 8 * _size; + ++_size; + if (_size == 4) + { + if (_value != _inflater.getBytesWritten()) + throw new ZipException("Invalid input size"); + + // TODO ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output); + reset(); + return ; + } + break; + } + default: + throw new ZipException(); + } + } + } + catch (ZipException x) + { + throw new RuntimeException(x); + } + finally + { + if (buffer!=null) + release(buffer); + } + } + + private void reset() + { + _inflater.reset(); + _state = State.INITIAL; + _size = 0; + _value = 0; + _flags = 0; + } + + @Override + public void destroy() + { + _inflater.end(); + } + + public boolean isFinished() + { + return _state == State.INITIAL; + } + + private enum State + { + INITIAL, ID, CM, FLG, MTIME, XFL, OS, FLAGS, EXTRA_LENGTH, EXTRA, NAME, COMMENT, HCRC, DATA, CRC, ISIZE + } + + /** + * @return An indirect buffer of the configured buffersize either from the pool or freshly allocated. + */ + public ByteBuffer acquire(int capacity) + { + return _pool==null?BufferUtil.allocate(capacity):_pool.acquire(capacity,false); + } + + /** + * Release an allocated buffer. + *

This method will called {@link ByteBufferPool#release(ByteBuffer)} if a buffer pool has + * been configured. This method should be called once for all buffers returned from {@link #decode(ByteBuffer)} + * or passed to {@link #decodedChunk(ByteBuffer)}. + * @param buffer The buffer to release. + */ + public void release(ByteBuffer buffer) + { + if (_pool!=null && buffer!=BufferUtil.EMPTY_BUFFER) + _pool.release(buffer); + } +} diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/GZIPContentDecoderTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/GZIPContentDecoderTest.java new file mode 100644 index 00000000000..2c2d7195baa --- /dev/null +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/GZIPContentDecoderTest.java @@ -0,0 +1,343 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.toolchain.test.TestTracker; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + + +public class GZIPContentDecoderTest +{ + @Rule + public final TestTracker tracker = new TestTracker(); + + + ArrayByteBufferPool pool; + AtomicInteger buffers = new AtomicInteger(0); + + @Before + public void beforeClass() throws Exception + { + buffers.set(0); + pool = new ArrayByteBufferPool() + { + + @Override + public ByteBuffer acquire(int size, boolean direct) + { + buffers.incrementAndGet(); + return super.acquire(size,direct); + } + + @Override + public void release(ByteBuffer buffer) + { + buffers.decrementAndGet(); + super.release(buffer); + } + + }; + } + + @After + public void afterClass() throws Exception + { + assertEquals(0,buffers.get()); + } + + + @Test + public void testStreamNoBlocks() throws Exception + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.close(); + byte[] bytes = baos.toByteArray(); + + GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1); + int read = input.read(); + assertEquals(-1, read); + } + + @Test + public void testStreamBigBlockOneByteAtATime() throws Exception + { + String data = "0123456789ABCDEF"; + for (int i = 0; i < 10; ++i) + data += data; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + baos = new ByteArrayOutputStream(); + GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1); + int read; + while ((read = input.read()) >= 0) + baos.write(read); + assertEquals(data, new String(baos.toByteArray(), StandardCharsets.UTF_8)); + } + + @Test + public void testNoBlocks() throws Exception + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.close(); + byte[] bytes = baos.toByteArray(); + + GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048); + ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); + assertEquals(0, decoded.remaining()); + } + + @Test + public void testSmallBlock() throws Exception + { + String data = "0"; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048); + ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); + decoder.release(decoded); + } + + @Test + public void testSmallBlockWithGZIPChunkedAtBegin() throws Exception + { + String data = "0"; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + // The header is 10 bytes, chunk at 11 bytes + byte[] bytes1 = new byte[11]; + System.arraycopy(bytes, 0, bytes1, 0, bytes1.length); + byte[] bytes2 = new byte[bytes.length - bytes1.length]; + System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); + + GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048); + ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); + assertEquals(0, decoded.capacity()); + decoded = decoder.decode(ByteBuffer.wrap(bytes2)); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); + decoder.release(decoded); + } + + @Test + public void testSmallBlockWithGZIPChunkedAtEnd() throws Exception + { + String data = "0"; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + // The trailer is 8 bytes, chunk the last 9 bytes + byte[] bytes1 = new byte[bytes.length - 9]; + System.arraycopy(bytes, 0, bytes1, 0, bytes1.length); + byte[] bytes2 = new byte[bytes.length - bytes1.length]; + System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); + + GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048); + ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); + assertFalse(decoder.isFinished()); + decoder.release(decoded); + decoded = decoder.decode(ByteBuffer.wrap(bytes2)); + assertEquals(0, decoded.remaining()); + assertTrue(decoder.isFinished()); + decoder.release(decoded); + } + + @Test + public void testSmallBlockWithGZIPTrailerChunked() throws Exception + { + String data = "0"; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + // The trailer is 4+4 bytes, chunk the last 3 bytes + byte[] bytes1 = new byte[bytes.length - 3]; + System.arraycopy(bytes, 0, bytes1, 0, bytes1.length); + byte[] bytes2 = new byte[bytes.length - bytes1.length]; + System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); + + GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048); + ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); + assertEquals(0, decoded.capacity()); + decoder.release(decoded); + decoded = decoder.decode(ByteBuffer.wrap(bytes2)); + assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); + decoder.release(decoded); + } + + @Test + public void testTwoSmallBlocks() throws Exception + { + String data1 = "0"; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data1.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes1 = baos.toByteArray(); + + String data2 = "1"; + baos = new ByteArrayOutputStream(); + output = new GZIPOutputStream(baos); + output.write(data2.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes2 = baos.toByteArray(); + + byte[] bytes = new byte[bytes1.length + bytes2.length]; + System.arraycopy(bytes1, 0, bytes, 0, bytes1.length); + System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length); + + GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + ByteBuffer decoded = decoder.decode(buffer); + assertEquals(data1, StandardCharsets.UTF_8.decode(decoded).toString()); + assertTrue(decoder.isFinished()); + assertTrue(buffer.hasRemaining()); + decoder.release(decoded); + decoded = decoder.decode(buffer); + assertEquals(data2, StandardCharsets.UTF_8.decode(decoded).toString()); + assertTrue(decoder.isFinished()); + assertFalse(buffer.hasRemaining()); + decoder.release(decoded); + } + + @Test + public void testBigBlock() throws Exception + { + String data = "0123456789ABCDEF"; + for (int i = 0; i < 10; ++i) + data += data; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + String result = ""; + GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + while (buffer.hasRemaining()) + { + ByteBuffer decoded = decoder.decode(buffer); + result += StandardCharsets.UTF_8.decode(decoded).toString(); + decoder.release(decoded); + } + assertEquals(data, result); + } + + @Test + public void testBigBlockOneByteAtATime() throws Exception + { + String data = "0123456789ABCDEF"; + for (int i = 0; i < 10; ++i) + data += data; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + String result = ""; + GZIPContentDecoder decoder = new GZIPContentDecoder(64); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + while (buffer.hasRemaining()) + { + ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(new byte[]{buffer.get()})); + if (decoded.hasRemaining()) + result += StandardCharsets.UTF_8.decode(decoded).toString(); + decoder.release(decoded); + } + assertEquals(data, result); + assertTrue(decoder.isFinished()); + } + + @Test + public void testBigBlockWithExtraBytes() throws Exception + { + String data1 = "0123456789ABCDEF"; + for (int i = 0; i < 10; ++i) + data1 += data1; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data1.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes1 = baos.toByteArray(); + + String data2 = "HELLO"; + byte[] bytes2 = data2.getBytes(StandardCharsets.UTF_8); + + byte[] bytes = new byte[bytes1.length + bytes2.length]; + System.arraycopy(bytes1, 0, bytes, 0, bytes1.length); + System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length); + + String result = ""; + GZIPContentDecoder decoder = new GZIPContentDecoder(64); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + while (buffer.hasRemaining()) + { + ByteBuffer decoded = decoder.decode(buffer); + if (decoded.hasRemaining()) + result += StandardCharsets.UTF_8.decode(decoded).toString(); + decoder.release(decoded); + if (decoder.isFinished()) + break; + } + assertEquals(data1, result); + assertTrue(buffer.hasRemaining()); + assertEquals(data2, StandardCharsets.UTF_8.decode(buffer).toString()); + } +} diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java index 1b6c60a9ce9..d6bc41c123f 100644 --- a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java +++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java @@ -118,6 +118,14 @@ public class SessionFailureTest extends AbstractTest Assert.assertTrue(writeLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(clientFailureLatch.await(5, TimeUnit.SECONDS)); - Assert.assertFalse(((HTTP2Session)session).getEndPoint().isOpen()); + long start = System.nanoTime(); + long now = System.nanoTime(); + while (((HTTP2Session)session).getEndPoint().isOpen()) + { + if (TimeUnit.NANOSECONDS.toSeconds(now-start)>5) + Assert.fail(); + Thread.sleep(10); + now = System.nanoTime(); + } } } diff --git a/jetty-http2/http2-server/src/main/config/modules/http2.mod b/jetty-http2/http2-server/src/main/config/modules/http2.mod index 88baddb13ab..5927f571279 100644 --- a/jetty-http2/http2-server/src/main/config/modules/http2.mod +++ b/jetty-http2/http2-server/src/main/config/modules/http2.mod @@ -2,6 +2,12 @@ Enables HTTP2 protocol support on the TLS(SSL) Connector, using the ALPN extension to select which protocol to use. +[Tags] +connector +http2 +http +ssl + [depend] ssl alpn diff --git a/jetty-http2/http2-server/src/main/config/modules/http2c.mod b/jetty-http2/http2-server/src/main/config/modules/http2c.mod index 80b2a28a335..b0fee130af5 100644 --- a/jetty-http2/http2-server/src/main/config/modules/http2c.mod +++ b/jetty-http2/http2-server/src/main/config/modules/http2c.mod @@ -2,6 +2,11 @@ Enables the HTTP2C protocol on the HTTP Connector The connector will accept both HTTP/1 and HTTP/2 connections. +[Tags] +connector +http2 +http + [depend] http diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod b/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod index 76441062683..bd0bb80ea10 100644 --- a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod +++ b/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod @@ -1,6 +1,9 @@ [description] Enables session data store in a local Infinispan cache +[Tags] +session + [provides] session-store diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod b/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod index f9c398a5b1e..70c62076d94 100644 --- a/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod +++ b/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod @@ -1,6 +1,9 @@ [description] Enables session data store in a remote Infinispan cache +[Tags] +session + [provides] session-store diff --git a/jetty-memcached/jetty-memcached-sessions/src/main/config/modules/sessions/session-data-cache/xmemcached.mod b/jetty-memcached/jetty-memcached-sessions/src/main/config/modules/sessions/session-data-cache/xmemcached.mod index 76987983ddc..48c589ae00c 100644 --- a/jetty-memcached/jetty-memcached-sessions/src/main/config/modules/sessions/session-data-cache/xmemcached.mod +++ b/jetty-memcached/jetty-memcached-sessions/src/main/config/modules/sessions/session-data-cache/xmemcached.mod @@ -1,6 +1,9 @@ [description] Memcache cache for SessionData +[Tags] +session + [depends] session-store slf4j-api diff --git a/jetty-nosql/src/main/config/modules/session-store-mongo.mod b/jetty-nosql/src/main/config/modules/session-store-mongo.mod index 77723093f6d..c1ffb5c755e 100644 --- a/jetty-nosql/src/main/config/modules/session-store-mongo.mod +++ b/jetty-nosql/src/main/config/modules/session-store-mongo.mod @@ -1,6 +1,9 @@ [description] Enables NoSql session management with a MongoDB driver. +[Tags] +session + [provides] session-store 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 d92ccd5f4d9..70f8669cad5 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,11 +18,14 @@ package org.eclipse.jetty.osgi.annotations; +import java.io.IOException; +import java.net.MalformedURLException; import java.util.HashSet; import java.util.Set; +import javax.servlet.ServletContainerInitializer; + import org.eclipse.jetty.annotations.AnnotationParser.Handler; -import org.eclipse.jetty.annotations.ClassNameResolver; import org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration; import org.eclipse.jetty.osgi.boot.OSGiWebappConstants; import org.eclipse.jetty.util.log.Log; @@ -44,9 +47,9 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot public class BundleParserTask extends ParserTask { - public BundleParserTask (AnnotationParser parser, Sethandlers, Resource resource, ClassNameResolver resolver) + public BundleParserTask (AnnotationParser parser, Sethandlers, Resource resource) { - super(parser, handlers, resource, resolver); + super(parser, handlers, resource); } public Void call() throws Exception @@ -57,7 +60,7 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot Bundle bundle = osgiAnnotationParser.getBundle(_resource); if (_stat != null) _stat.start(); - osgiAnnotationParser.parse(_handlers, bundle, _resolver); + osgiAnnotationParser.parse(_handlers, bundle); if (_stat != null) _stat.end(); } @@ -79,6 +82,17 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot return new AnnotationParser(); } + @Override + public Resource getJarFor(ServletContainerInitializer service) throws MalformedURLException, IOException + { + Resource resource = super.getJarFor(service); + // TODO This is not correct, but implemented like this to be bug for bug compatible + // with previous implementation that could only handle actual jars and not bundles. + if (resource!=null && !resource.toString().endsWith(".jar")) + return null; + return resource; + } + /** * Here is the order in which jars and osgi artifacts are scanned for discoverable annotations. *

    @@ -195,47 +209,13 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot handlers.add(_classInheritanceHandler); handlers.addAll(_containerInitializerAnnotationHandlers); - ClassNameResolver classNameResolver = createClassNameResolver(context); if (_parserTasks != null) { - BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes, classNameResolver); + BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes); _parserTasks.add(task); if (LOG.isDebugEnabled()) task.setStatistic(new TimeStatistic()); } } - /** - * Returns the same classname resolver than for the webInfjar scanner - * @param context the web app context - * @return the class name resolver - */ - protected ClassNameResolver createClassNameResolver(final WebAppContext context) - { - return createClassNameResolver(context,true,false,false,false); - } - - protected ClassNameResolver createClassNameResolver(final WebAppContext context, - final boolean excludeSysClass, final boolean excludeServerClass, final boolean excludeEverythingElse, - final boolean overrideIsParenLoaderIsPriority) - { - return new ClassNameResolver () - { - public boolean isExcluded (String name) - { - if (context.isSystemClass(name)) return excludeSysClass; - if (context.isServerClass(name)) return excludeServerClass; - return excludeEverythingElse; - } - - public boolean shouldOverride (String name) - { - //looking at system classpath - if (context.isParentLoaderPriority()) - return overrideIsParenLoaderIsPriority; - return !overrideIsParenLoaderIsPriority; - } - }; - } - } 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 a22b4077113..d454e9be7cf 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,7 +29,6 @@ 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.BundleFileLocatorHelperFactory; import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.resource.Resource; @@ -85,7 +84,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa * */ @Override - public void parse (Set handlers, URI[] uris, ClassNameResolver resolver) + public void parse (Set handlers, URI[] uris) throws Exception { for (URI uri : uris) @@ -99,16 +98,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(handlers, new URI[] {uri},resolver); + super.parse(handlers, new URI[] {uri}); } else { - parse(handlers, associatedBundle,resolver); + parse(handlers, associatedBundle); } } } - protected void parse(Set handlers, Bundle bundle, ClassNameResolver resolver) + protected void parse(Set handlers, Bundle bundle) throws Exception { URI uri = _bundleToUri.get(bundle); @@ -205,7 +204,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)))) + if (!isParsed(shortName)) { try (InputStream classInputStream = classUrl.openStream()) { diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java index a04f37adfb5..02c0feb35dc 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java @@ -47,18 +47,16 @@ public abstract class AbstractOSGiApp extends App private static final Logger LOG = Log.getLogger(AbstractOSGiApp.class); protected Bundle _bundle; - protected Dictionary _properties; + protected Dictionary _properties; protected ServiceRegistration _registration; /* ------------------------------------------------------------ */ public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId) { - super(manager, provider, originId); - _properties = bundle.getHeaders(); - _bundle = bundle; + this (manager, provider, bundle, bundle.getHeaders(), originId); } /* ------------------------------------------------------------ */ - public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId) + public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId) { super(manager, provider, originId); _properties = properties; diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java index 0386f013c7e..74d386cc837 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java @@ -38,6 +38,7 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.JarResource; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.Configuration; +import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; import org.osgi.framework.Bundle; @@ -436,18 +437,23 @@ public abstract class AbstractWebAppProvider extends AbstractLifeCycle implement } } } - if (contextXmlUrl == null) return; + if (contextXmlUrl == null) + return; // Apply it just as the standard jetty ContextProvider would do LOG.info("Applying " + contextXmlUrl + " to " + _webApp); XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl); - HashMap properties = new HashMap(); - properties.put("Server", getDeploymentManager().getServer()); - properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString()); - properties.put(OSGiServerConstants.JETTY_HOME, getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); - xmlConfiguration.getProperties().putAll(properties); - xmlConfiguration.configure(_webApp); + WebAppClassLoader.runWithServerClassAccess(()-> + { + HashMap properties = new HashMap<>(); + xmlConfiguration.getIdMap().put("Server",getDeploymentManager().getServer()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString()); + properties.put(OSGiServerConstants.JETTY_HOME, (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + xmlConfiguration.getProperties().putAll(properties); + xmlConfiguration.configure(_webApp); + return null; + }); } finally { diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java index 43bf2c4733c..395c621139f 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java @@ -175,7 +175,7 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund String contextPath = null; try { - Dictionary headers = bundle.getHeaders(); + Dictionary headers = bundle.getHeaders(); //does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH String resourcePath = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH, OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers); @@ -217,7 +217,7 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund { //Could be a static webapp with no web.xml String base = "."; - contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + contextPath = headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); String originId = getOriginId(bundle,base); OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId); diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java index 8c77b130d76..8ba7377e9c9 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java @@ -78,8 +78,6 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe private Bundle _contributor; - private boolean _lookInOsgiFirst = true; - /* ------------------------------------------------------------ */ /** * @param parent The parent classloader. @@ -96,11 +94,26 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe } - + + /* ------------------------------------------------------------ */ @Override public Class loadClass(String name) throws ClassNotFoundException { - return super.loadClass(name); + try + { + return _osgiBundleClassLoader.loadClass(name); + } + catch (ClassNotFoundException cne) + { + try + { + return super.loadClass(name); + } + catch (ClassNotFoundException cne2) + { + throw cne; + } + } } /* ------------------------------------------------------------ */ @@ -121,36 +134,18 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe { Enumeration osgiUrls = _osgiBundleClassLoader.getResources(name); Enumeration urls = super.getResources(name); - if (_lookInOsgiFirst) - { - return Collections.enumeration(toList(osgiUrls, urls)); - } - else - { - return Collections.enumeration(toList(urls, osgiUrls)); - } + List resources = toList(osgiUrls, urls); + return Collections.enumeration(resources); } - - /* ------------------------------------------------------------ */ @Override public URL getResource(String name) { - if (_lookInOsgiFirst) - { - URL url = _osgiBundleClassLoader.getResource(name); - return url != null ? url : super.getResource(name); - } - else - { - URL url = super.getResource(name); - return url != null ? url : _osgiBundleClassLoader.getResource(name); - } + URL url = _osgiBundleClassLoader.getResource(name); + return url != null ? url : super.getResource(name); } - - /* ------------------------------------------------------------ */ private List toList(Enumeration e, Enumeration e2) { @@ -160,30 +155,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe while (e2 != null && e2.hasMoreElements()) list.add(e2.nextElement()); return list; - } - - - /* ------------------------------------------------------------ */ - protected Class findClass(String name) throws ClassNotFoundException - { - try - { - return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name); - } - catch (ClassNotFoundException cne) - { - try - { - return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name); - } - catch (ClassNotFoundException cne2) - { - throw cne; - } - } - } - - + } /* ------------------------------------------------------------ */ /** diff --git a/jetty-server/src/main/config/etc/jetty-gzip.xml b/jetty-server/src/main/config/etc/jetty-gzip.xml index 93b41fd6396..6e5573449e7 100644 --- a/jetty-server/src/main/config/etc/jetty-gzip.xml +++ b/jetty-server/src/main/config/etc/jetty-gzip.xml @@ -15,6 +15,8 @@ + diff --git a/jetty-server/src/main/config/etc/sessions/id-manager.xml b/jetty-server/src/main/config/etc/sessions/id-manager.xml index b31f690ae67..945d5076275 100644 --- a/jetty-server/src/main/config/etc/sessions/id-manager.xml +++ b/jetty-server/src/main/config/etc/sessions/id-manager.xml @@ -12,7 +12,18 @@ - node + + + node + + + 0 + + + + + + diff --git a/jetty-server/src/main/config/modules/debug.mod b/jetty-server/src/main/config/modules/debug.mod index 7b75ecc0e79..9e0ae040b9f 100644 --- a/jetty-server/src/main/config/modules/debug.mod +++ b/jetty-server/src/main/config/modules/debug.mod @@ -3,6 +3,9 @@ Enables the DebugListener to generate additional logging regarding detailed request handling events. Renames threads to include request URI. +[Tags] +debug + [depend] deploy diff --git a/jetty-server/src/main/config/modules/debuglog.mod b/jetty-server/src/main/config/modules/debuglog.mod index a76f728a5b4..81db4894c71 100644 --- a/jetty-server/src/main/config/modules/debuglog.mod +++ b/jetty-server/src/main/config/modules/debuglog.mod @@ -2,6 +2,9 @@ Deprecated Debug Log using the DebugHandle. Replaced with the debug module. +[Tags] +debug + [depend] server diff --git a/jetty-server/src/main/config/modules/ext.mod b/jetty-server/src/main/config/modules/ext.mod index 4171f8dfc21..63081e550dd 100644 --- a/jetty-server/src/main/config/modules/ext.mod +++ b/jetty-server/src/main/config/modules/ext.mod @@ -2,6 +2,9 @@ Adds all jar files discovered in $JETTY_HOME/lib/ext and $JETTY_BASE/lib/ext to the servers classpath. +[Tags] +classpath + [lib] lib/ext/**.jar diff --git a/jetty-server/src/main/config/modules/gzip.mod b/jetty-server/src/main/config/modules/gzip.mod index 65663a16066..50db40b156e 100644 --- a/jetty-server/src/main/config/modules/gzip.mod +++ b/jetty-server/src/main/config/modules/gzip.mod @@ -2,6 +2,9 @@ Enable GzipHandler for dynamic gzip compression for the entire server. +[Tags] +handler + [depend] server @@ -20,3 +23,6 @@ etc/jetty-gzip.xml ## User agents for which gzip is disabled # jetty.gzip.excludedUserAgent=.*MSIE.6\.0.* + +## Inflate request buffer size, or 0 for no request inflation +# jetty.gzip.inflateBufferSize=0 diff --git a/jetty-server/src/main/config/modules/http-forwarded.mod b/jetty-server/src/main/config/modules/http-forwarded.mod index 6508a2b9a3f..2797bc29d3b 100644 --- a/jetty-server/src/main/config/modules/http-forwarded.mod +++ b/jetty-server/src/main/config/modules/http-forwarded.mod @@ -2,6 +2,9 @@ Adds a forwarded request customizer to the HTTP Connector to process forwarded-for style headers from a proxy. +[Tags] +connector + [depend] http diff --git a/jetty-server/src/main/config/modules/http.mod b/jetty-server/src/main/config/modules/http.mod index 3f2766f0e45..caf104cb9b4 100644 --- a/jetty-server/src/main/config/modules/http.mod +++ b/jetty-server/src/main/config/modules/http.mod @@ -3,6 +3,10 @@ Enables a HTTP connector on the server. By default HTTP/1 is support, but HTTP2C can be added to the connector with the http2c module. +[Tags] +connector +http + [depend] server diff --git a/jetty-server/src/main/config/modules/https.mod b/jetty-server/src/main/config/modules/https.mod index 6ffbd69d0cc..9ff301e58d5 100644 --- a/jetty-server/src/main/config/modules/https.mod +++ b/jetty-server/src/main/config/modules/https.mod @@ -1,6 +1,12 @@ [description] Adds HTTPS protocol support to the TLS(SSL) Connector +[Tags] +connector +https +http +ssl + [depend] ssl diff --git a/jetty-server/src/main/config/modules/ipaccess.mod b/jetty-server/src/main/config/modules/ipaccess.mod index 68f04dfc576..5afd05e0a11 100644 --- a/jetty-server/src/main/config/modules/ipaccess.mod +++ b/jetty-server/src/main/config/modules/ipaccess.mod @@ -2,6 +2,9 @@ Enable the ipaccess handler to apply a white/black list control of the remote IP of requests. +[Tags] +handler + [depend] server diff --git a/jetty-server/src/main/config/modules/jvm.mod b/jetty-server/src/main/config/modules/jvm.mod index 173a6d11ba1..30b0d966b96 100644 --- a/jetty-server/src/main/config/modules/jvm.mod +++ b/jetty-server/src/main/config/modules/jvm.mod @@ -1,6 +1,7 @@ [description] A noop module that creates an ini template useful for setting JVM arguments (eg -Xmx ) + [ini-template] ## JVM Configuration ## If JVM args are include in an ini file then --exec is needed @@ -16,7 +17,7 @@ setting JVM arguments (eg -Xmx ) # -XX:+CMSClassUnloadingEnabled # -XX:+UseCMSCompactAtFullCollection # -XX:CMSInitiatingOccupancyFraction=80 -# -verbose:gc +# -internal:gc # -XX:+PrintGCDateStamps # -XX:+PrintGCTimeStamps # -XX:+PrintGCDetails diff --git a/jetty-server/src/main/config/modules/logback-access.mod b/jetty-server/src/main/config/modules/logback-access.mod index b1275ba49b9..0cb8bd0de45 100644 --- a/jetty-server/src/main/config/modules/logback-access.mod +++ b/jetty-server/src/main/config/modules/logback-access.mod @@ -1,6 +1,11 @@ [description] Enables logback request log. +[Tags] +requestlog +logging +logback + [depend] server logback-core diff --git a/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod b/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod index 374763d0b5b..4b41e935bb6 100644 --- a/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod +++ b/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod @@ -5,6 +5,10 @@ This allows a Proxy operating in TCP mode to transport details of the proxied connection to the server. Both V1 and V2 versions of the protocol are supported. +[Tags] +connector +ssl + [depend] ssl diff --git a/jetty-server/src/main/config/modules/requestlog.mod b/jetty-server/src/main/config/modules/requestlog.mod index 628f759e088..0bf60edcdc0 100644 --- a/jetty-server/src/main/config/modules/requestlog.mod +++ b/jetty-server/src/main/config/modules/requestlog.mod @@ -1,6 +1,9 @@ [description] Enables a NCSA style request log. +[Tags] +requestlog + [depend] server @@ -36,4 +39,4 @@ logs/ # jetty.requestlog.timezone=GMT ## Whether to log LogLatency -# jetty.requestlog.loglatency=false \ No newline at end of file +# jetty.requestlog.loglatency=false diff --git a/jetty-server/src/main/config/modules/resources.mod b/jetty-server/src/main/config/modules/resources.mod index 56489486408..4e0bd35b43a 100644 --- a/jetty-server/src/main/config/modules/resources.mod +++ b/jetty-server/src/main/config/modules/resources.mod @@ -3,6 +3,9 @@ Adds the $JETTY_HOME/resources and/or $JETTY_BASE/resources directory to the server classpath. Useful for configuration property files (eg jetty-logging.properties) +[Tags] +classpath + [lib] resources/ diff --git a/jetty-server/src/main/config/modules/session-cache-hash.mod b/jetty-server/src/main/config/modules/session-cache-hash.mod index 9a09ec5dbdb..1f257867b25 100644 --- a/jetty-server/src/main/config/modules/session-cache-hash.mod +++ b/jetty-server/src/main/config/modules/session-cache-hash.mod @@ -4,6 +4,9 @@ If not enabled, sessions will use a HashSessionCache by default, so enabling via this module is only needed if the configuration properties need to be changed. +[Tags] +session + [provides] session-cache diff --git a/jetty-server/src/main/config/modules/session-cache-null.mod b/jetty-server/src/main/config/modules/session-cache-null.mod index 81e5b02b9e4..70c9e97fa37 100644 --- a/jetty-server/src/main/config/modules/session-cache-null.mod +++ b/jetty-server/src/main/config/modules/session-cache-null.mod @@ -1,6 +1,9 @@ [description] A trivial SessionCache that does not actually cache sessions. +[Tags] +session + [provides] session-cache diff --git a/jetty-server/src/main/config/modules/session-store-cache.mod b/jetty-server/src/main/config/modules/session-store-cache.mod index e9a170b823e..cac19d1207d 100644 --- a/jetty-server/src/main/config/modules/session-store-cache.mod +++ b/jetty-server/src/main/config/modules/session-store-cache.mod @@ -1,6 +1,9 @@ [description] Enables caching of SessionData in front of a SessionDataStore. +[Tags] +session + [depend] session-store diff --git a/jetty-server/src/main/config/modules/session-store-file.mod b/jetty-server/src/main/config/modules/session-store-file.mod index 22726641879..d644f67dddd 100644 --- a/jetty-server/src/main/config/modules/session-store-file.mod +++ b/jetty-server/src/main/config/modules/session-store-file.mod @@ -1,6 +1,9 @@ [description] Enables session persistent storage in files. +[Tags] +session + [provides] session-store diff --git a/jetty-server/src/main/config/modules/session-store-jdbc.mod b/jetty-server/src/main/config/modules/session-store-jdbc.mod index a3f41956b24..69bf3125bd0 100644 --- a/jetty-server/src/main/config/modules/session-store-jdbc.mod +++ b/jetty-server/src/main/config/modules/session-store-jdbc.mod @@ -1,6 +1,9 @@ [description] Enables JDBC peristent/distributed session storage. +[Tags] +session + [provides] session-store diff --git a/jetty-server/src/main/config/modules/sessions.mod b/jetty-server/src/main/config/modules/sessions.mod index e90cd8aa2bd..4ce1cde856e 100644 --- a/jetty-server/src/main/config/modules/sessions.mod +++ b/jetty-server/src/main/config/modules/sessions.mod @@ -5,6 +5,9 @@ created or by enabling other session-cache or session-store modules. Without this module enabled, the server may still use sessions, but their management cannot be configured. +[Tags] +session + [depends] server diff --git a/jetty-server/src/main/config/modules/ssl.mod b/jetty-server/src/main/config/modules/ssl.mod index 43c29143bde..db92f255bb2 100644 --- a/jetty-server/src/main/config/modules/ssl.mod +++ b/jetty-server/src/main/config/modules/ssl.mod @@ -3,6 +3,10 @@ Enables a TLS(SSL) Connector on the server. This may be used for HTTPS and/or HTTP2 by enabling the associated support modules. +[Tags] +connector +ssl + [depend] server diff --git a/jetty-server/src/main/config/modules/stats.mod b/jetty-server/src/main/config/modules/stats.mod index 838d54a904d..2b4d3d32f8b 100644 --- a/jetty-server/src/main/config/modules/stats.mod +++ b/jetty-server/src/main/config/modules/stats.mod @@ -2,6 +2,9 @@ Enable detailed statistics collection for the server, available via JMX. +[Tags] +handler + [depend] server diff --git a/jetty-server/src/main/config/modules/threadlimit.mod b/jetty-server/src/main/config/modules/threadlimit.mod index 1a2615bd9b9..b6d41dc1480 100644 --- a/jetty-server/src/main/config/modules/threadlimit.mod +++ b/jetty-server/src/main/config/modules/threadlimit.mod @@ -3,6 +3,9 @@ # Applies ThreadLimiteHandler to entire server # +[Tags] +handler + [depend] server diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index ffc727f060e..52604608e28 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -37,25 +37,94 @@ import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.component.Destroyable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.thread.Invocable.InvocationType; /** * {@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}. *

    - * Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class - * maintains two states: the content state that tells whether there is content to consume and the EOF - * state that tells whether an EOF has arrived. - * Only once the content has been consumed the content state is moved to the EOF state. + * Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class maintains two states: the content state that tells + * whether there is content to consume and the EOF state that tells whether an EOF has arrived. Only once the content has been consumed the content state is + * moved to the EOF state. */ public class HttpInput extends ServletInputStream implements Runnable { + /** + * An interceptor for HTTP Request input. + *

    + * Unlike inputstream wrappers that can be applied by filters, an interceptor + * is entirely transparent and works with async IO APIs. + *

    + * An Interceptor may consume data from the passed content and the interceptor + * will continue to be called for the same content until the interceptor returns + * null or an empty content. Thus even if the passed content is completely consumed + * the interceptor will be called with the same content until it can no longer + * produce more content. + * @see HttpInput#setInterceptor(Interceptor) + * @see HttpInput#addInterceptor(Interceptor) + */ + public interface Interceptor + { + /** + * @param content The content to be intercepted (may be empty or a {@link SentinelContent}. + * The content will be modified with any data the interceptor consumes, but there is no requirement + * that all the data is consumed by the interceptor. + * @return The intercepted content or null if interception is completed for that content. + */ + Content readFrom(Content content); + } + + /** + * An {@link Interceptor} that chains two other {@link Interceptor}s together. + * The {@link #readFrom(Content)} calls the previous {@link Interceptor}'s + * {@link #readFrom(Content)} and then passes any {@link Content} returned + * to the next {@link Interceptor}. + */ + public static class ChainedInterceptor implements Interceptor, Destroyable + { + private final Interceptor _prev; + private final Interceptor _next; + + public ChainedInterceptor(Interceptor prev, Interceptor next) + { + _prev = prev; + _next = next; + } + + public Interceptor getPrev() + { + return _prev; + } + + public Interceptor getNext() + { + return _next; + } + + @Override + public Content readFrom(Content content) + { + return getNext().readFrom(getPrev().readFrom(content)); + } + + @Override + public void destroy() + { + if (_prev instanceof Destroyable) + ((Destroyable)_prev).destroy(); + if (_next instanceof Destroyable) + ((Destroyable)_next).destroy(); + } + } + private final static Logger LOG = Log.getLogger(HttpInput.class); private final static Content EOF_CONTENT = new EofContent("EOF"); private final static Content EARLY_EOF_CONTENT = new EofContent("EARLY_EOF"); private final byte[] _oneByteBuffer = new byte[1]; + private Content _content; + private Content _intercepted; private final Deque _inputQ = new ArrayDeque<>(); private final HttpChannelState _channelState; private ReadListener _listener; @@ -64,6 +133,7 @@ public class HttpInput extends ServletInputStream implements Runnable private long _contentArrived; private long _contentConsumed; private long _blockUntil; + private Interceptor _interceptor; public HttpInput(HttpChannelState state) { @@ -79,6 +149,9 @@ public class HttpInput extends ServletInputStream implements Runnable { synchronized (_inputQ) { + if (_content!=null) + _content.failed(null); + _content = null; Content item = _inputQ.poll(); while (item != null) { @@ -91,9 +164,42 @@ public class HttpInput extends ServletInputStream implements Runnable _contentConsumed = 0; _firstByteTimeStamp = -1; _blockUntil = 0; + if (_interceptor instanceof Destroyable) + ((Destroyable)_interceptor).destroy(); + _interceptor = null; } } + /** + * @return The current Interceptor, or null if none set + */ + public Interceptor getInterceptor() + { + return _interceptor; + } + + /** + * Set the interceptor. + * @param interceptor The interceptor to use. + */ + public void setInterceptor(Interceptor interceptor) + { + _interceptor = interceptor; + } + + /** + * Set the {@link Interceptor}, using a {@link ChainedInterceptor} if + * an {@link Interceptor} is already set. + * @param interceptor the next {@link Interceptor} in a chain + */ + public void addInterceptor(Interceptor interceptor) + { + if (_interceptor == null) + _interceptor = interceptor; + else + _interceptor = new ChainedInterceptor(_interceptor,interceptor); + } + @Override public int available() { @@ -101,8 +207,9 @@ public class HttpInput extends ServletInputStream implements Runnable boolean woken = false; synchronized (_inputQ) { - Content content = _inputQ.peek(); - if (content == null) + if (_content == null) + _content = _inputQ.poll(); + if (_content == null) { try { @@ -112,11 +219,12 @@ public class HttpInput extends ServletInputStream implements Runnable { woken = failed(e); } - content = _inputQ.peek(); + if (_content == null) + _content = _inputQ.poll(); } - if (content != null) - available = remaining(content); + if (_content != null) + available = _content.remaining(); } if (woken) @@ -139,10 +247,10 @@ public class HttpInput extends ServletInputStream implements Runnable @Override public int read() throws IOException { - int read = read(_oneByteBuffer, 0, 1); + int read = read(_oneByteBuffer,0,1); if (read == 0) throw new IllegalStateException("unready read=0"); - return read < 0 ? -1 : _oneByteBuffer[0] & 0xFF; + return read < 0?-1:_oneByteBuffer[0] & 0xFF; } @Override @@ -168,7 +276,7 @@ public class HttpInput extends ServletInputStream implements Runnable { long minimum_data = minRequestDataRate * TimeUnit.NANOSECONDS.toMillis(period) / TimeUnit.SECONDS.toMillis(1); if (_contentArrived < minimum_data) - throw new BadMessageException(HttpStatus.REQUEST_TIMEOUT_408, String.format("Request data rate < %d B/s", minRequestDataRate)); + throw new BadMessageException(HttpStatus.REQUEST_TIMEOUT_408,String.format("Request data rate < %d B/s",minRequestDataRate)); } } @@ -177,11 +285,12 @@ public class HttpInput extends ServletInputStream implements Runnable Content item = nextContent(); if (item != null) { - int l = get(item, b, off, len); + int l = get(item,b,off,len); if (LOG.isDebugEnabled()) - LOG.debug("{} read {} from {}", this, l, item); + LOG.debug("{} read {} from {}",this,l,item); - consumeNonContent(); + // Consume any following poison pills + pollReadableContent(); return l; } @@ -193,191 +302,207 @@ public class HttpInput extends ServletInputStream implements Runnable } /** - * Called when derived implementations should attempt to - * produce more Content and add it via {@link #addContent(Content)}. - * For protocols that are constantly producing (eg HTTP2) this can - * be left as a noop; + * Called when derived implementations should attempt to produce more Content and add it via {@link #addContent(Content)}. For protocols that are constantly + * producing (eg HTTP2) this can be left as a noop; * - * @throws IOException if unable to produce content + * @throws IOException + * if unable to produce content */ protected void produceContent() throws IOException { } /** - * Get the next content from the inputQ, calling {@link #produceContent()} - * if need be. EOF is processed and state changed. + * Get the next content from the inputQ, calling {@link #produceContent()} if need be. EOF is processed and state changed. * * @return the content or null if none available. - * @throws IOException if retrieving the content fails + * @throws IOException + * if retrieving the content fails */ protected Content nextContent() throws IOException { - Content content = pollContent(); + Content content = pollNonEmptyContent(); if (content == null && !isFinished()) { produceContent(); - content = pollContent(); + content = pollNonEmptyContent(); } return content; } /** - * Poll the inputQ for Content. - * Consumed buffers and {@link PoisonPillContent}s are removed and - * EOF state updated if need be. + * Poll the inputQ for Content. Consumed buffers and {@link SentinelContent}s are removed and EOF state updated if need be. * * @return Content or null */ - protected Content pollContent() + protected Content pollNonEmptyContent() { - // Items are removed only when they are fully consumed. - Content content = _inputQ.peek(); - // Skip consumed items at the head of the queue. - while (content != null && remaining(content) == 0) - { - _inputQ.poll(); - content.succeeded(); - if (LOG.isDebugEnabled()) - LOG.debug("{} consumed {}", this, content); - - if (content == EOF_CONTENT) + while (true) + { + // Get the next content (or EOF) + Content content = pollReadableContent(); + + // If it is EOF, consume it here + if (content instanceof SentinelContent) { - if (_listener == null) - _state = EOF; - else + if (content == EARLY_EOF_CONTENT) + _state = EARLY_EOF; + else if (content instanceof EofContent) { - _state = AEOF; - boolean woken = _channelState.onReadReady(); // force callback? - if (woken) - wake(); + if (_listener == null) + _state = EOF; + else + { + _state = AEOF; + boolean woken = _channelState.onReadReady(); // force callback? + if (woken) + wake(); + } } + + // Consume the EOF content, either if it was original content + // or if it was produced by interception + content.succeeded(); + if (_content==content) + _content = null; + else if (_intercepted==content) + _intercepted = null; + continue; } - else if (content == EARLY_EOF_CONTENT) - _state = EARLY_EOF; - content = _inputQ.peek(); + return content; } - - return content; + } /** + * Poll the inputQ for Content or EOF. Consumed buffers and non EOF {@link SentinelContent}s are removed. EOF state is not updated. + * Interception is done within this method. + * @return Content with remaining, a {@link SentinelContent}, or null */ - protected void consumeNonContent() + protected Content pollReadableContent() { - // Items are removed only when they are fully consumed. - Content content = _inputQ.peek(); - // Skip consumed items at the head of the queue. - while (content != null && remaining(content) == 0) + // If we have a chunk produced by interception + if (_intercepted!=null) { - // Defer EOF until read - if (content instanceof EofContent) - break; - - // Consume all other empty content - _inputQ.poll(); - content.succeeded(); - if (LOG.isDebugEnabled()) - LOG.debug("{} consumed {}", this, content); - content = _inputQ.peek(); + // Use it if it has any remaining content + if (_intercepted.hasContent()) + return _intercepted; + + // succeed the chunk + _intercepted.succeeded(); + _intercepted=null; } + + // If we don't have a Content under consideration, get + // the next one off the input Q. + if (_content == null) + _content = _inputQ.poll(); + + // While we have content to consider. + while (_content!=null) + { + // Are we intercepting? + if (_interceptor!=null) + { + // Intercept the current content (may be called several + // times for the same content + _intercepted = _interceptor.readFrom(_content); + + // If interception produced new content + if (_intercepted!=null && _intercepted!=_content) + { + // if it is not empty use it + if (_intercepted.hasContent()) + return _intercepted; + _intercepted.succeeded(); + } + + // intercepted content consumed + _intercepted=null; + + // fall through so that the unintercepted _content is + // considered for any remaining content, for EOF and to + // succeed it if it is entirely consumed. + } + + // If the content has content or is an EOF marker, use it + if (_content.hasContent() || _content instanceof SentinelContent) + return _content; + + // The content is consumed, so get the next one. Note that EOF + // content is never consumed here, but in #pollContent + _content.succeeded(); + _content = _inputQ.poll(); + } + + return null; + } + /** - * Get the next readable from the inputQ, calling {@link #produceContent()} - * if need be. EOF is NOT processed and state is not changed. + * Get the next readable from the inputQ, calling {@link #produceContent()} if need be. EOF is NOT processed and state is not changed. * * @return the content or EOF or null if none available. - * @throws IOException if retrieving the content fails + * @throws IOException + * if retrieving the content fails */ protected Content nextReadable() throws IOException { - Content content = pollReadable(); + Content content = pollReadableContent(); if (content == null && !isFinished()) { produceContent(); - content = pollReadable(); + content = pollReadableContent(); } return content; } - /** - * Poll the inputQ for Content or EOF. - * Consumed buffers and non EOF {@link PoisonPillContent}s are removed. - * EOF state is not updated. - * - * @return Content, EOF or null - */ - protected Content pollReadable() - { - // Items are removed only when they are fully consumed. - Content content = _inputQ.peek(); - - // Skip consumed items at the head of the queue except EOF - while (content != null) - { - if (content == EOF_CONTENT || content == EARLY_EOF_CONTENT || remaining(content) > 0) - return content; - - _inputQ.poll(); - content.succeeded(); - if (LOG.isDebugEnabled()) - LOG.debug("{} consumed {}", this, content); - content = _inputQ.peek(); - } - - return null; - } - - /** - * @param item the content - * @return how many bytes remain in the given content - */ - protected int remaining(Content item) - { - return item.remaining(); - } /** * Copies the given content into the given byte buffer. * - * @param content the content to copy from - * @param buffer the buffer to copy into - * @param offset the buffer offset to start copying from - * @param length the space available in the buffer + * @param content + * the content to copy from + * @param buffer + * the buffer to copy into + * @param offset + * the buffer offset to start copying from + * @param length + * the space available in the buffer * @return the number of bytes actually copied */ protected int get(Content content, byte[] buffer, int offset, int length) { - int l = Math.min(content.remaining(), length); - content.getContent().get(buffer, offset, l); + int l = content.get(buffer,offset,length); _contentConsumed += l; return l; } /** - * Consumes the given content. - * Calls the content succeeded if all content consumed. + * Consumes the given content. Calls the content succeeded if all content consumed. * - * @param content the content to consume - * @param length the number of bytes to consume + * @param content + * the content to consume + * @param length + * the number of bytes to consume */ protected void skip(Content content, int length) { - int l = Math.min(content.remaining(), length); - ByteBuffer buffer = content.getContent(); - buffer.position(buffer.position() + l); + int l = content.skip(length); + _contentConsumed += l; - if (l > 0 && !content.hasContent()) - pollContent(); // hungry succeed + if (l > 0 && content.isEmpty()) + pollNonEmptyContent(); // hungry succeed } /** * Blocks until some content or some end-of-file event arrives. * - * @throws IOException if the wait is interrupted + * @throws IOException + * if the wait is interrupted */ protected void blockForContent() throws IOException { @@ -392,7 +517,7 @@ public class HttpInput extends ServletInputStream implements Runnable } if (LOG.isDebugEnabled()) - LOG.debug("{} blocking for content timeout={}", this, timeout); + LOG.debug("{} blocking for content timeout={}",this,timeout); if (timeout > 0) _inputQ.wait(timeout); else @@ -402,7 +527,7 @@ public class HttpInput extends ServletInputStream implements Runnable // TODO: so spurious wakeups are not handled correctly. if (_blockUntil != 0 && TimeUnit.NANOSECONDS.toMillis(_blockUntil - System.nanoTime()) <= 0) - throw new TimeoutException(String.format("Blocking timeout %d ms", getBlockingTimeout())); + throw new TimeoutException(String.format("Blocking timeout %d ms",getBlockingTimeout())); } catch (Throwable e) { @@ -412,11 +537,12 @@ public class HttpInput extends ServletInputStream implements Runnable /** * Adds some content to the start of this input stream. - *

    Typically used to push back content that has - * been read, perhaps mutated. The bytes prepended are - * deducted for the contentConsumed total

    + *

    + * Typically used to push back content that has been read, perhaps mutated. The bytes prepended are deducted for the contentConsumed total + *

    * - * @param item the content to add + * @param item + * the content to add * @return true if content channel woken for read */ public boolean prependContent(Content item) @@ -424,44 +550,54 @@ public class HttpInput extends ServletInputStream implements Runnable boolean woken = false; synchronized (_inputQ) { - _inputQ.push(item); + if (_content != null) + _inputQ.push(_content); + _content = item; _contentConsumed -= item.remaining(); if (LOG.isDebugEnabled()) - LOG.debug("{} prependContent {}", this, item); + LOG.debug("{} prependContent {}",this,item); if (_listener == null) _inputQ.notify(); else woken = _channelState.onReadPossible(); } - return woken; } /** * Adds some content to this input stream. * - * @param item the content to add + * @param content + * the content to add * @return true if content channel woken for read */ - public boolean addContent(Content item) + public boolean addContent(Content content) { boolean woken = false; synchronized (_inputQ) { if (_firstByteTimeStamp == -1) _firstByteTimeStamp = System.nanoTime(); - _contentArrived += item.remaining(); - _inputQ.offer(item); - if (LOG.isDebugEnabled()) - LOG.debug("{} addContent {}", this, item); - if (_listener == null) - _inputQ.notify(); + _contentArrived += content.remaining(); + + if (_content==null && _inputQ.isEmpty()) + _content=content; else - woken = _channelState.onReadPossible(); - } + _inputQ.offer(content); + + if (LOG.isDebugEnabled()) + LOG.debug("{} addContent {}",this,content); + if (pollReadableContent()!=null) + { + if (_listener == null) + _inputQ.notify(); + else + woken = _channelState.onReadPossible(); + } + } return woken; } @@ -469,7 +605,7 @@ public class HttpInput extends ServletInputStream implements Runnable { synchronized (_inputQ) { - return _inputQ.size() > 0; + return _content!=null || _inputQ.size() > 0; } } @@ -490,11 +626,9 @@ public class HttpInput extends ServletInputStream implements Runnable } /** - * This method should be called to signal that an EOF has been - * detected before all the expected content arrived. + * This method should be called to signal that an EOF has been detected before all the expected content arrived. *

    - * Typically this will result in an EOFException being thrown - * from a subsequent read rather than a -1 return. + * Typically this will result in an EOFException being thrown from a subsequent read rather than a -1 return. * * @return true if content channel woken for read */ @@ -504,8 +638,7 @@ public class HttpInput extends ServletInputStream implements Runnable } /** - * This method should be called to signal that all the expected - * content arrived. + * This method should be called to signal that all the expected content arrived. * * @return true if content channel woken for read */ @@ -526,7 +659,7 @@ public class HttpInput extends ServletInputStream implements Runnable if (item == null) break; // Let's not bother blocking - skip(item, remaining(item)); + skip(item,item.remaining()); } return isFinished() && !isError(); } @@ -641,11 +774,8 @@ public class HttpInput extends ServletInputStream implements Runnable } /* - *

    - * While this class is-a Runnable, it should never be dispatched in it's own thread. It is a - * runnable only so that the calling thread can use {@link ContextHandler#handle(Runnable)} - * to setup classloaders etc. - *

    + *

    While this class is-a Runnable, it should never be dispatched in it's own thread. It is a runnable only so that the calling thread can use {@link + * ContextHandler#handle(Runnable)} to setup classloaders etc.

    */ @Override public void run() @@ -666,7 +796,7 @@ public class HttpInput extends ServletInputStream implements Runnable } listener = _listener; - error = _state instanceof ErrorState ? ((ErrorState)_state).getError() : null; + error = _state instanceof ErrorState?((ErrorState)_state).getError():null; } try @@ -721,19 +851,24 @@ public class HttpInput extends ServletInputStream implements Runnable content = _inputQ.peekFirst(); } return String.format("%s@%x[c=%d,q=%d,[0]=%s,s=%s]", - getClass().getSimpleName(), - hashCode(), - consumed, - q, - content, - state); + getClass().getSimpleName(), + hashCode(), + consumed, + q, + content, + state); } - public static class PoisonPillContent extends Content + /** + * A Sentinel Content, which has zero length content but + * indicates some other event in the input stream (eg EOF) + * + */ + public static class SentinelContent extends Content { private final String _name; - public PoisonPillContent(String name) + public SentinelContent(String name) { super(BufferUtil.EMPTY_BUFFER); _name = name; @@ -746,7 +881,7 @@ public class HttpInput extends ServletInputStream implements Runnable } } - public static class EofContent extends PoisonPillContent + public static class EofContent extends SentinelContent { EofContent(String name) { @@ -756,22 +891,36 @@ public class HttpInput extends ServletInputStream implements Runnable public static class Content implements Callback { - private final ByteBuffer _content; + protected final ByteBuffer _content; public Content(ByteBuffer content) { _content = content; } + public ByteBuffer getByteBuffer() + { + return _content; + } + @Override public InvocationType getInvocationType() { return InvocationType.NON_BLOCKING; } - public ByteBuffer getContent() + public int get(byte[] buffer, int offset, int length) { - return _content; + length = Math.min(_content.remaining(),length); + _content.get(buffer,offset,length); + return length; + } + + public int skip(int length) + { + length = Math.min(_content.remaining(),length); + _content.position(_content.position() + length); + return length; } public boolean hasContent() @@ -783,15 +932,19 @@ public class HttpInput extends ServletInputStream implements Runnable { return _content.remaining(); } + + public boolean isEmpty() + { + return !_content.hasRemaining(); + } @Override public String toString() { - return String.format("Content@%x{%s}", hashCode(), BufferUtil.toDetailString(_content)); + return String.format("Content@%x{%s}",hashCode(),BufferUtil.toDetailString(_content)); } } - protected static abstract class State { public boolean blockForContent(HttpInput in) throws IOException diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index c9a1354c73f..9d1ec89924b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -175,9 +175,9 @@ public class HttpOutput extends ServletOutputStream implements Runnable return _interceptor; } - public void setInterceptor(Interceptor filter) + public void setInterceptor(Interceptor interceptor) { - _interceptor = filter; + _interceptor = interceptor; } public boolean isWritten() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java index 19cccb6326a..07e63fac350 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java @@ -133,7 +133,7 @@ public class BufferedResponseHandler extends HandlerWrapper } // If not a supported method - no Vary because no matter what client, this URI is always excluded - if (!_methods.matches(baseRequest.getMethod())) + if (!_methods.test(baseRequest.getMethod())) { LOG.debug("{} excluded by method {}",this,request); _handler.handle(target,baseRequest, request, response); @@ -173,7 +173,7 @@ public class BufferedResponseHandler extends HandlerWrapper /* ------------------------------------------------------------ */ protected boolean isMimeTypeBufferable(String mimetype) { - return _mimeTypes.matches(mimetype); + return _mimeTypes.test(mimetype); } /* ------------------------------------------------------------ */ @@ -182,7 +182,7 @@ public class BufferedResponseHandler extends HandlerWrapper if (requestURI == null) return true; - return _paths.matches(requestURI); + return _paths.test(requestURI); } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index 1206f1a6c06..2ec6e6e2a53 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -65,6 +65,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory private int _compressionLevel=Deflater.DEFAULT_COMPRESSION; private boolean _checkGzExists = true; private boolean _syncFlush = false; + private int _inflateBufferSize = -1; // non-static, as other GzipHandler instances may have different configurations private final ThreadLocal _deflater = new ThreadLocal<>(); @@ -77,6 +78,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory private HttpField _vary; + /* ------------------------------------------------------------ */ /** * Instantiates a new gzip handler. @@ -398,6 +400,24 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory return _vary; } + /* ------------------------------------------------------------ */ + /** + * @return size in bytes of the buffer to inflate compressed request, or 0 for no inflation. + */ + public int getInflateBufferSize() + { + return _inflateBufferSize; + } + + /* ------------------------------------------------------------ */ + /** + * @param size size in bytes of the buffer to inflate compressed request, or 0 for no inflation. + */ + public void setInflateBufferSize(int size) + { + _inflateBufferSize = size; + } + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @@ -409,6 +429,19 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo()); LOG.debug("{} handle {} in {}",this,baseRequest,context); + // Handle request inflation + if (_inflateBufferSize>0) + { + HttpField ce = baseRequest.getHttpFields().getField(HttpHeader.CONTENT_ENCODING); + if (ce!=null && "gzip".equalsIgnoreCase(ce.getValue())) + { + // TODO should check ce.contains and then remove just the gzip encoding + baseRequest.getHttpFields().remove(HttpHeader.CONTENT_ENCODING); + baseRequest.getHttpFields().add(new HttpField("X-Content-Encoding",ce.getValue())); + baseRequest.getHttpInput().addInterceptor(new GzipHttpInputInterceptor(baseRequest.getHttpChannel().getByteBufferPool(),_inflateBufferSize)); + } + } + HttpOutput out = baseRequest.getResponse().getHttpOutput(); // Are we already being gzipped? HttpOutput.Interceptor interceptor = out.getInterceptor(); @@ -424,7 +457,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory } // If not a supported method - no Vary because no matter what client, this URI is always excluded - if (!_methods.matches(baseRequest.getMethod())) + if (!_methods.test(baseRequest.getMethod())) { LOG.debug("{} excluded by method {}",this,request); _handler.handle(target,baseRequest, request, response); @@ -516,14 +549,14 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory if (ua == null) return false; - return _agentPatterns.matches(ua); + return _agentPatterns.test(ua); } /* ------------------------------------------------------------ */ @Override public boolean isMimeTypeGzipable(String mimetype) { - return _mimeTypes.matches(mimetype); + return _mimeTypes.test(mimetype); } /* ------------------------------------------------------------ */ @@ -539,7 +572,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory if (requestURI == null) return true; - return _paths.matches(requestURI); + return _paths.test(requestURI); } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpInputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpInputInterceptor.java new file mode 100644 index 00000000000..4f55cd956b5 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpInputInterceptor.java @@ -0,0 +1,88 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.handler.gzip; + +import java.nio.ByteBuffer; + +import org.eclipse.jetty.http.GZIPContentDecoder; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.server.HttpInput; +import org.eclipse.jetty.server.HttpInput.Content; +import org.eclipse.jetty.util.component.Destroyable; + +/** + * A HttpInput Interceptor that inflates GZIP encoded request content. + */ +public class GzipHttpInputInterceptor implements HttpInput.Interceptor, Destroyable +{ + private final Decoder _decoder; + private ByteBuffer _chunk; + + public GzipHttpInputInterceptor(ByteBufferPool pool, int bufferSize) + { + _decoder = new Decoder(pool, bufferSize); + } + + @Override + public Content readFrom(Content content) + { + _decoder.decodeChunks(content.getByteBuffer()); + final ByteBuffer chunk = _chunk; + + if (chunk == null) + return null; + + return new Content(chunk) + { + @Override + public void succeeded() + { + _decoder.release(chunk); + } + }; + } + + @Override + public void destroy() + { + _decoder.destroy(); + } + + private class Decoder extends GZIPContentDecoder + { + private Decoder(ByteBufferPool pool, int bufferSize) + { + super(pool, bufferSize); + } + + @Override + protected boolean decodedChunk(final ByteBuffer chunk) + { + _chunk = chunk; + return true; + } + + @Override + public void decodeChunks(ByteBuffer compressed) + { + _chunk = null; + super.decodeChunks(compressed); + } + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java index ad9a1c15504..9ee4635e6aa 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java @@ -336,9 +336,9 @@ public class DefaultSessionIdManager extends AbstractLifeCycle implements Sessio { String inst = System.getenv("JETTY_WORKER_INSTANCE"); _workerName = "node"+ (inst==null?"0":inst); - LOG.warn("No workerName configured for DefaultSessionIdManager, using {}",_workerName); } + LOG.info("DefaultSessionIdManager workerName={}",_workerName); _workerAttr=(_workerName!=null && _workerName.startsWith("$"))?_workerName.substring(1):null; if (_houseKeeper == null) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java index 985d8c01bed..820e7ebf0c2 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java @@ -354,6 +354,7 @@ public class AsyncRequestReadTest for (int i=read;i-->0;) { int c=in.read(); + // System.err.println("in="+c); if (c<0) break; out.write(c); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslContextFactoryReloadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslContextFactoryReloadTest.java new file mode 100644 index 00000000000..c167f30ea96 --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslContextFactoryReloadTest.java @@ -0,0 +1,265 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.ssl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpTester; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; +import org.eclipse.jetty.util.thread.Scheduler; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class SslContextFactoryReloadTest +{ + public static final String KEYSTORE_1 = "src/test/resources/reload_keystore_1.jks"; + public static final String KEYSTORE_2 = "src/test/resources/reload_keystore_2.jks"; + + private Server server; + private SslContextFactory sslContextFactory; + private ServerConnector connector; + + private void start(Handler handler) throws Exception + { + server = new Server(); + + sslContextFactory = new SslContextFactory(); + sslContextFactory.setKeyStorePath(KEYSTORE_1); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setKeyStoreType("JKS"); + sslContextFactory.setKeyStoreProvider(null); + + HttpConfiguration httpsConfig = new HttpConfiguration(); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + connector = new ServerConnector(server, + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(httpsConfig)); + server.addConnector(connector); + + server.setHandler(handler); + + server.start(); + } + + @After + public void dispose() throws Exception + { + if (server != null) + server.stop(); + } + + @Test + public void testReload() throws Exception + { + start(new EchoHandler()); + + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); + ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, null); + SSLSocketFactory socketFactory = ctx.getSocketFactory(); + try (SSLSocket client1 = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort())) + { + String serverDN1 = client1.getSession().getPeerPrincipal().getName(); + Assert.assertThat(serverDN1, Matchers.startsWith("CN=localhost1")); + + String request = "" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "\r\n"; + + OutputStream output1 = client1.getOutputStream(); + output1.write(request.getBytes(StandardCharsets.UTF_8)); + output1.flush(); + + HttpTester.Response response1 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream())); + Assert.assertNotNull(response1); + Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200)); + + // Reconfigure SslContextFactory. + sslContextFactory.reload(sslContextFactory -> + { + sslContextFactory.setKeyStorePath(KEYSTORE_2); + sslContextFactory.setKeyStorePassword("storepwd"); + }); + + // New connection should use the new keystore. + try (SSLSocket client2 = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort())) + { + String serverDN2 = client2.getSession().getPeerPrincipal().getName(); + Assert.assertThat(serverDN2, Matchers.startsWith("CN=localhost2")); + + OutputStream output2 = client1.getOutputStream(); + output2.write(request.getBytes(StandardCharsets.UTF_8)); + output2.flush(); + + HttpTester.Response response2 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream())); + Assert.assertNotNull(response2); + Assert.assertThat(response2.getStatus(), Matchers.equalTo(HttpStatus.OK_200)); + } + + // Must still be possible to make requests with the first connection. + output1.write(request.getBytes(StandardCharsets.UTF_8)); + output1.flush(); + + response1 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream())); + Assert.assertNotNull(response1); + Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200)); + } + } + + @Test + public void testReloadWhileServing() throws Exception + { + start(new EchoHandler()); + + Scheduler scheduler = new ScheduledExecutorScheduler(); + scheduler.start(); + try + { + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); + ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, null); + SSLSocketFactory socketFactory = ctx.getSocketFactory(); + + // Perform 4 reloads while connections are being served. + AtomicInteger reloads = new AtomicInteger(4); + long reloadPeriod = 500; + AtomicBoolean running = new AtomicBoolean(true); + scheduler.schedule(new Runnable() + { + @Override + public void run() + { + if (reloads.decrementAndGet() == 0) + { + running.set(false); + } + else + { + try + { + sslContextFactory.reload(sslContextFactory -> + { + if (sslContextFactory.getKeyStorePath().endsWith(KEYSTORE_1)) + sslContextFactory.setKeyStorePath(KEYSTORE_2); + else + sslContextFactory.setKeyStorePath(KEYSTORE_1); + }); + scheduler.schedule(this, reloadPeriod, TimeUnit.MILLISECONDS); + } + catch (Exception x) + { + running.set(false); + reloads.set(-1); + } + } + } + }, reloadPeriod, TimeUnit.MILLISECONDS); + + byte[] content = new byte[16 * 1024]; + while (running.get()) + { + try (SSLSocket client = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort())) + { + // We need to invalidate the session every time we open a new SSLSocket. + // This is because when the client uses session resumption, it caches + // the server certificates and then checks that it is the same during + // a new TLS handshake. If the SslContextFactory is reloaded during the + // TLS handshake, the client will see the new certificate and blow up. + // Note that browsers can handle this case better: they will just not + // use session resumption and fallback to the normal TLS handshake. + client.getSession().invalidate(); + + String request1 = "" + + "POST / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Content-Length: " + content.length + "\r\n" + + "\r\n"; + OutputStream outputStream = client.getOutputStream(); + outputStream.write(request1.getBytes(StandardCharsets.UTF_8)); + outputStream.write(content); + outputStream.flush(); + + InputStream inputStream = client.getInputStream(); + HttpTester.Response response1 = HttpTester.parseResponse(HttpTester.from(inputStream)); + Assert.assertNotNull(response1); + Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200)); + + String request2 = "" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"; + outputStream.write(request2.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + + HttpTester.Response response2 = HttpTester.parseResponse(HttpTester.from(inputStream)); + Assert.assertNotNull(response2); + Assert.assertThat(response2.getStatus(), Matchers.equalTo(HttpStatus.OK_200)); + } + } + + Assert.assertEquals(0, reloads.get()); + } + finally + { + scheduler.stop(); + } + } + + private static class EchoHandler extends AbstractHandler + { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + if (HttpMethod.POST.is(request.getMethod())) + IO.copy(request.getInputStream(), response.getOutputStream()); + else + response.setContentLength(0); + } + } +} diff --git a/jetty-server/src/test/resources/reload_keystore_1.jks b/jetty-server/src/test/resources/reload_keystore_1.jks new file mode 100644 index 00000000000..d615c22234d Binary files /dev/null and b/jetty-server/src/test/resources/reload_keystore_1.jks differ diff --git a/jetty-server/src/test/resources/reload_keystore_2.jks b/jetty-server/src/test/resources/reload_keystore_2.jks new file mode 100644 index 00000000000..3707c3a3613 Binary files /dev/null and b/jetty-server/src/test/resources/reload_keystore_2.jks differ diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java index e892f389e5a..7b1ef878a5a 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java @@ -33,8 +33,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -45,6 +47,7 @@ import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.gzip.GzipHandler; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.IO; import org.hamcrest.Matchers; import org.junit.After; @@ -86,6 +89,7 @@ public class GzipHandlerTest GzipHandler gzipHandler = new GzipHandler(); gzipHandler.setExcludedAgentPatterns(); gzipHandler.setMinGzipSize(16); + gzipHandler.setInflateBufferSize(4096); ServletContextHandler context = new ServletContextHandler(gzipHandler,"/ctx"); ServletHandler servlets = context.getServletHandler(); @@ -97,6 +101,7 @@ public class GzipHandlerTest servlets.addServletWithMapping(TestServlet.class,"/content"); servlets.addServletWithMapping(ForwardServlet.class,"/forward"); servlets.addServletWithMapping(IncludeServlet.class,"/include"); + servlets.addServletWithMapping(EchoServlet.class,"/echo/*"); _server.start(); } @@ -147,6 +152,21 @@ public class GzipHandlerTest } } } + + public static class EchoServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType(req.getContentType()); + IO.copy(req.getInputStream(),response.getOutputStream()); + } + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException + { + doGet(req,response); + } + } public static class ForwardServlet extends HttpServlet { @@ -392,4 +412,68 @@ public class GzipHandlerTest assertThat("Included Paths.size", includedPaths.length, is(2)); assertThat("Included Paths", Arrays.asList(includedPaths), contains("/foo","^/bar.*$")); } + + + @Test + public void testGzipRequest() throws Exception + { + String data = "Hello Nice World! "; + for (int i = 0; i < 10; ++i) + data += data; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data.getBytes(StandardCharsets.UTF_8)); + output.close(); + byte[] bytes = baos.toByteArray(); + + // generated and parsed test + HttpTester.Request request = HttpTester.newRequest(); + HttpTester.Response response; + + request.setMethod("POST"); + request.setURI("/ctx/echo"); + request.setVersion("HTTP/1.0"); + request.setHeader("Host","tester"); + request.setHeader("Content-Type","text/plain"); + request.setHeader("Content-Encoding","gzip"); + request.setContent(bytes); + + response = HttpTester.parseResponse(_connector.getResponse(request.generate())); + + assertThat(response.getStatus(),is(200)); + assertThat(response.getContent(),is(data)); + + } + + @Test + public void testGzipBomb() throws Exception + { + byte[] data = new byte[512*1024]; + Arrays.fill(data,(byte)'X'); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream output = new GZIPOutputStream(baos); + output.write(data); + output.close(); + byte[] bytes = baos.toByteArray(); + + // generated and parsed test + HttpTester.Request request = HttpTester.newRequest(); + HttpTester.Response response; + + request.setMethod("POST"); + request.setURI("/ctx/echo"); + request.setVersion("HTTP/1.0"); + request.setHeader("Host","tester"); + request.setHeader("Content-Type","text/plain"); + request.setHeader("Content-Encoding","gzip"); + request.setContent(bytes); + + response = HttpTester.parseResponse(_connector.getResponse(request.generate())); + // TODO need to test back pressure works + + assertThat(response.getStatus(),is(200)); + assertThat(response.getContentBytes().length,is(512*1024)); + } + } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java index 11d74334669..bb399f99b20 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java @@ -50,11 +50,10 @@ public class BaseBuilder * * @param module * the module to add - * @return true if module was added, false if module was not added - * (because that module already exists) + * @return The ini file if module was added, null if module was not added. * @throws IOException if unable to add the module */ - public boolean addModule(Module module) throws IOException; + public String addModule(Module module) throws IOException; } private static final String EXITING_LICENSE_NOT_ACKNOWLEDGED = "Exiting: license not acknowledged!"; @@ -181,7 +180,6 @@ public class BaseBuilder if (!newly_added.isEmpty()) { - if (Files.exists(startini) && Files.exists(startd)) StartLog.warn("Use both %s and %s is deprecated",getBaseHome().toShortForm(startd),getBaseHome().toShortForm(startini)); @@ -189,6 +187,7 @@ public class BaseBuilder builder.set(useStartD?new StartDirBuilder(this):new StartIniBuilder(this)); newly_added.stream().map(n->modules.get(n)).forEach(module -> { + String ini=null; try { if (module.isSkipFilesValidation()) @@ -197,8 +196,13 @@ public class BaseBuilder } else { - if (builder.get().addModule(module)) - modified.set(true); + // if (explictly added and ini file modified) + if (startArgs.getStartModules().contains(module.getName())) + { + ini=builder.get().addModule(module); + if (ini!=null) + modified.set(true); + } for (String file : module.getFiles()) files.add(new FileArg(module,startArgs.getProperties().expand(file))); } @@ -207,6 +211,26 @@ public class BaseBuilder { throw new RuntimeException(e); } + + if (module.isDynamic()) + { + for (String s:module.getEnableSources()) + StartLog.info("%-15s %s",module.getName(),s); + } + else if (module.isTransitive()) + { + if (module.hasIniTemplate()) + StartLog.info("%-15s transitively enabled, ini template available with --add-to-start=%s", + module.getName(), + module.getName()); + else + StartLog.info("%-15s transitively enabled",module.getName()); + } + else + StartLog.info("%-15s initialized in %s", + module.getName(), + ini); + }); } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java index 3463ad3ecfe..39c7bbabfa4 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java @@ -243,16 +243,19 @@ public class Main public void listModules(StartArgs args) { + List tags = args.getListModules(); + StartLog.endStartLog(); System.out.println(); - System.out.println("Jetty All Available Modules:"); - System.out.println("----------------------------"); - args.getAllModules().dump(); + System.out.println("Available Modules:"); + System.out.println("=================="); + System.out.println("tags: "+tags); + args.getAllModules().dump(tags); // Dump Enabled Modules System.out.println(); - System.out.println("Jetty Selected Module Ordering:"); - System.out.println("-------------------------------"); + System.out.println("Enabled Modules:"); + System.out.println("================"); Modules modules = args.getAllModules(); modules.dumpEnabled(); } @@ -381,7 +384,7 @@ public class Main } // Show modules - if (args.isListModules()) + if (args.getListModules()!=null) { listModules(args); } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java index 7f31cf3390f..e318cc127a9 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java @@ -55,7 +55,7 @@ import java.util.stream.Collectors; * A module may be enabled, either directly by name or transiently via a dependency * from another module by name or provided capability. */ -public class Module +public class Module implements Comparable { private static final String VERSION_UNSPECIFIED = "9.2"; private static Pattern MOD_NAME = Pattern.compile("^(.*)\\.mod",Pattern.CASE_INSENSITIVE); @@ -96,6 +96,9 @@ public class Module /** List of provides for this Module */ private final Set _provides=new HashSet<>(); + /** List of tags for this Module */ + private final List _tags=new ArrayList<>(); + /** Boolean true if directly enabled, false if all selections are transitive */ private boolean _notTransitive; @@ -328,6 +331,10 @@ public class Module case "FILES": _files.add(line); break; + case "TAG": + case "TAGS": + _tags.add(line); + break; case "DEFAULTS": // old name introduced in 9.2.x case "INI": // new name for 9.3+ _defaultConfig.add(line); @@ -446,6 +453,16 @@ public class Module return _description; } + public List getTags() + { + return _tags; + } + + public String getPrimaryTag() + { + return _tags.isEmpty()?"*":_tags.get(0); + } + public boolean isEnabled() { return !_enables.isEmpty(); @@ -504,4 +521,13 @@ public class Module out.println(); out.flush(); } + + @Override + public int compareTo(Module m) + { + int by_tag = getPrimaryTag().compareTo(m.getPrimaryTag()); + if (by_tag!=0) + return by_tag; + return getName().compareTo(m.getName()); + } } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java index 3fee8802054..0a1a3bf577a 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.start; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -30,6 +29,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -57,59 +57,92 @@ public class Modules implements Iterable } } - public void dump() + public void dump(List tags) { - List ordered = _modules.stream().map(m->{return m.getName();}).collect(Collectors.toList()); - Collections.sort(ordered); - ordered.stream().map(n->{return get(n);}).forEach(module-> - { - String status = "[ ]"; - if (module.isTransitive()) + Set exclude = tags.stream().filter(t->t.startsWith("-")).map(t->t.substring(1)).collect(Collectors.toSet()); + Set include = tags.stream().filter(t->!t.startsWith("-")).collect(Collectors.toSet()); + boolean all = include.contains("*") || include.isEmpty(); + AtomicReference tag = new AtomicReference<>(); + + _modules.stream() + .filter(m-> { - status = "[t]"; - } - else if (module.isEnabled()) + boolean included = all || m.getTags().stream().anyMatch(t->include.contains(t)); + boolean excluded = m.getTags().stream().anyMatch(t->exclude.contains(t)); + return included && !excluded; + }) + .sorted() + .forEach(module-> { - status = "[x]"; - } - - System.out.printf("%n %s Module: %s%n",status,module.getName()); - if (module.getProvides().size()>1) - { - System.out.printf(" Provides: %s%n",module.getProvides()); - } - for (String description : module.getDescription()) - { - System.out.printf(" : %s%n",description); - } - for (String parent : module.getDepends()) - { - System.out.printf(" Depend: %s%n",parent); - } - for (String optional : module.getOptional()) - { - System.out.printf(" Optional: %s%n",optional); - } - for (String lib : module.getLibs()) - { - System.out.printf(" LIB: %s%n",lib); - } - for (String xml : module.getXmls()) - { - System.out.printf(" XML: %s%n",xml); - } - for (String jvm : module.getJvmArgs()) - { - System.out.printf(" JVM: %s%n",jvm); - } - if (module.isEnabled()) - { - for (String selection : module.getEnableSources()) + if (!module.getPrimaryTag().equals(tag.get())) { - System.out.printf(" Enabled: %s%n",selection); + tag.set(module.getPrimaryTag()); + System.out.printf("%nModules for tag '%s':%n",module.getPrimaryTag()); + System.out.print("-------------------"); + for (int i=module.getPrimaryTag().length();i-->0;) + System.out.print("-"); + System.out.println(); + } - } - }); + + String label; + Set provides = module.getProvides(); + provides.remove(module.getName()); + System.out.printf("%n Module: %s %s%n",module.getName(),provides.size()>0?provides:""); + for (String description : module.getDescription()) + { + System.out.printf(" : %s%n",description); + } + if (!module.getTags().isEmpty()) + { + label=" Tags: %s"; + for (String t : module.getTags()) + { + System.out.printf(label,t); + label=", %s"; + } + System.out.println(); + } + if (!module.getDepends().isEmpty()) + { + label=" Depend: %s"; + for (String parent : module.getDepends()) + { + System.out.printf(label,parent); + label=", %s"; + } + System.out.println(); + } + if (!module.getOptional().isEmpty()) + { + label=" Optional: %s"; + for (String parent : module.getOptional()) + { + System.out.printf(label,parent); + label=", %s"; + } + System.out.println(); + } + for (String lib : module.getLibs()) + { + System.out.printf(" LIB: %s%n",lib); + } + for (String xml : module.getXmls()) + { + System.out.printf(" XML: %s%n",xml); + } + for (String jvm : module.getJvmArgs()) + { + System.out.printf(" JVM: %s%n",jvm); + } + if (module.isEnabled()) + { + for (String selection : module.getEnableSources()) + { + System.out.printf(" Enabled: %s%n",selection); + } + } + }); } public void dumpEnabled() @@ -125,6 +158,8 @@ public class Modules implements Iterable index=""; name=""; } + if (module.isTransitive() && module.hasIniTemplate()) + System.out.printf(" init template available with --add-to-start=%s%n",module.getName()); } } @@ -279,9 +314,6 @@ public class Modules implements Iterable m.expandProperties(_args.getProperties()); } } - else if (module.isTransitive() && module.hasIniTemplate()) - newlyEnabled.add(module.getName()); - // Process module dependencies (always processed as may be dynamic) for(String dependsOn:module.getDepends()) @@ -318,7 +350,7 @@ public class Modules implements Iterable // Is there an obvious default? Optional dftProvider = providers.stream().filter(m->m.getName().equals(dependsOn)).findFirst(); if (dftProvider.isPresent()) - enable(newlyEnabled,dftProvider.get(),"default provider of "+dependsOn+" for "+module.getName(),true); + enable(newlyEnabled,dftProvider.get(),"transitive provider of "+dependsOn+" for "+module.getName(),true); else if (StartLog.isDebugEnabled()) StartLog.debug("Module %s requires %s from one of %s",module,dependsOn,providers); } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java index 5b5bc8f5fc8..ec6397fb777 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java @@ -170,7 +170,7 @@ public class StartArgs private boolean help = false; private boolean stopCommand = false; - private boolean listModules = false; + private List listModules = null; private boolean listClasspath = false; private boolean listConfig = false; private boolean version = false; @@ -364,7 +364,7 @@ public class StartArgs } else { - System.out.printf(" %s = %s%n",key,properties.expand(prop.value)); + System.out.printf(" %s = %s%n",key,prop.value); if (StartLog.isDebugEnabled()) { System.out.printf(" origin: %s%n",prop.origin); @@ -372,7 +372,7 @@ public class StartArgs { prop = prop.overrides; System.out.printf(" (overrides)%n"); - System.out.printf(" %s = %s%n",key,properties.expand(prop.value)); + System.out.printf(" %s = %s%n",key,prop.value); System.out.printf(" origin: %s%n",prop.origin); } } @@ -398,7 +398,7 @@ public class StartArgs for (String key : sortedKeys) { String value = System.getProperty(key); - System.out.printf(" %s = %s%n",key,properties.expand(value)); + System.out.printf(" %s = %s%n",key,value); } } @@ -750,7 +750,7 @@ public class StartArgs return listConfig; } - public boolean isListModules() + public List getListModules() { return listModules; } @@ -873,7 +873,7 @@ public class StartArgs if (arg.equals("--create-files")) { run = false; - download = true; + download = true;boolean licenseCheckRequired = true; return; } @@ -938,10 +938,25 @@ public class StartArgs return; } + // Module Management + if ("--list-all-modules".equals(arg)) + { + listModules = Collections.singletonList("*"); + run = false; + return; + } + // Module Management if ("--list-modules".equals(arg)) { - listModules = true; + listModules = Collections.singletonList("-internal"); + run = false; + return; + } + + if (arg.startsWith("--list-modules=")) + { + listModules = Props.getValues(arg); run = false; return; } @@ -1035,13 +1050,33 @@ public class StartArgs } // Is this a raw property declaration? - int idx = arg.indexOf('='); - if (idx >= 0) + int equals = arg.indexOf('='); + if (equals >= 0) { - String key = arg.substring(0,idx); - String value = arg.substring(idx + 1); + String key = arg.substring(0,equals); + String value = arg.substring(equals + 1); - if (replaceProps) + if (key.endsWith("+")) + { + key = key.substring(0,key.length()-1); + String orig = getProperties().getString(key); + if (orig != null && !orig.isEmpty()) + { + value=orig+value; + source=propertySource.get(key)+","+source; + } + } + else if (key.endsWith(",")) + { + key = key.substring(0,key.length()-1); + String orig = getProperties().getString(key); + if (orig != null && !orig.isEmpty()) + { + value=value.isEmpty()?orig:(orig+","+value); + source=propertySource.get(key)+","+source; + } + } + else if (replaceProps) { if (propertySource.containsKey(key)) { @@ -1050,21 +1085,10 @@ public class StartArgs propertySource.put(key,source); } - if ("OPTION".equals(key) || "OPTIONS".equals(key)) - { - StringBuilder warn = new StringBuilder(); - warn.append("The behavior of the argument "); - warn.append(arg).append(" (seen in ").append(source); - warn.append(") has changed, and is now considered a normal property. "); - warn.append(key).append(" no longer controls what libraries are on your classpath,"); - warn.append(" use --module instead. See --help for details."); - StartLog.warn(warn.toString()); - } - setProperty(key,value,source,replaceProps); return; } - + // Is this an xml file? if (FS.isXml(arg)) { @@ -1169,7 +1193,9 @@ public class StartArgs return; } - if (replaceProp || (!properties.containsKey(key))) + if (value==null || value.isEmpty()) + properties.remove(key,value,source); + else if (replaceProp || (!properties.containsKey(key))) { properties.setProperty(key,value,source); if(key.equals("java.version")) diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java index 5f6fca2ae87..23b239c6566 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java @@ -50,7 +50,7 @@ public class StartDirBuilder implements BaseBuilder.Config } @Override - public boolean addModule(Module module) throws IOException + public String addModule(Module module) throws IOException { if (module.isDynamic()) { @@ -59,28 +59,20 @@ public class StartDirBuilder implements BaseBuilder.Config // warn StartLog.warn("%-15s not adding [ini-template] from dynamic module",module.getName()); } - return false; - } - - String mode = ""; - if (module.isTransitive()) - { - mode = "(transitively) "; + return null; } if (module.hasIniTemplate() || !module.isTransitive()) { // Create start.d/{name}.ini Path ini = startDir.resolve(module.getName() + ".ini"); - StartLog.info("%-15s initialised %sin %s",module.getName(),mode,baseHome.toShortForm(ini)); - try (BufferedWriter writer = Files.newBufferedWriter(ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING)) { module.writeIniSection(writer); } - return true; + return baseHome.toShortForm(ini); } - return false; + return null; } } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java index dbfdc209934..369837032be 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.start.builders; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; -import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -86,13 +85,13 @@ public class StartIniBuilder implements BaseBuilder.Config } @Override - public boolean addModule(Module module) throws IOException + public String addModule(Module module) throws IOException { if (modulesPresent.contains(module.getName())) { StartLog.info("%-15s already initialised in %s",module.getName(),baseHome.toShortForm(startIni)); // skip, already present - return false; + return null; } if (module.isDynamic()) @@ -102,27 +101,19 @@ public class StartIniBuilder implements BaseBuilder.Config // warn StartLog.warn("%-15s not adding [ini-template] from dynamic module",module.getName()); } - return false; - } - - String mode = ""; - if (module.isTransitive()) - { - mode = "(transitively) "; + return null; } if (module.hasIniTemplate() || !module.isTransitive()) { - StartLog.info("%-15s initialised %sin %s",module.getName(),mode,baseHome.toShortForm(startIni)); - // Append to start.ini try (BufferedWriter writer = Files.newBufferedWriter(startIni,StandardCharsets.UTF_8,StandardOpenOption.APPEND,StandardOpenOption.CREATE)) { module.writeIniSection(writer); } - return true; + return baseHome.toShortForm(startIni); } - return false; + return null; } } diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt index 27f76f44016..44e39c2c8b8 100644 --- a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt +++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt @@ -59,7 +59,7 @@ Debug and Start Logging: Module Management: ------------------ - --list-modules List all modules defined by the system. + --list-modules List non verbose modules defined by the system. Looking for module files in ${jetty.base}/modules/*.mod and then ${jetty.home}/modules/*.mod Will also list enabled state based on information @@ -67,6 +67,13 @@ Module Management: o The command line o The ${jetty.base}/start.ini o The ${jetty.base}/start.d/*.ini files + + --list-modules=(,)* + List modules by tag. Use '*' for all tags. Prefix a tag + with '-' to exclude the tag. + + --list-all-modules + List all modules. --module=(,)* Temporarily enable a module from the command line. @@ -140,6 +147,15 @@ Startup / Shutdown Command Line: Properties: + name=value + Set a property that can be expanded in XML files with the element. + + name+=value + Add to an existing property. + + name,=value + Add to an existing property as a comma separated list. + STOP.HOST=[string] The host to use to stop the running Jetty server (defaults to 127.0.0.1) Required along with STOP.PORT if you want to use the --stop option above. diff --git a/jetty-start/src/test/resources/usecases/basic-properties.assert.txt b/jetty-start/src/test/resources/usecases/basic-properties.assert.txt index 4e90c5d74ae..e0c620efe12 100644 --- a/jetty-start/src/test/resources/usecases/basic-properties.assert.txt +++ b/jetty-start/src/test/resources/usecases/basic-properties.assert.txt @@ -12,3 +12,5 @@ PROP|main.prop=value0 PROP|port=9090 PROP|other=value PROP|jetty.http.port=9090 +PROP|add=beginningmiddleend +PROP|list=one,two,three diff --git a/jetty-start/src/test/resources/usecases/basic-properties.cmdline.txt b/jetty-start/src/test/resources/usecases/basic-properties.cmdline.txt index 8c38c4dcd41..759df59e929 100644 --- a/jetty-start/src/test/resources/usecases/basic-properties.cmdline.txt +++ b/jetty-start/src/test/resources/usecases/basic-properties.cmdline.txt @@ -1,2 +1,8 @@ other=value port=9090 +add+=beginning +add+=middle +add+=end +list,=one +list,=two +list,=three diff --git a/jetty-start/src/test/resources/usecases/empty.addToStart.assert.txt b/jetty-start/src/test/resources/usecases/empty.addToStart.assert.txt index 10651695c51..9b2e1bbaa03 100644 --- a/jetty-start/src/test/resources/usecases/empty.addToStart.assert.txt +++ b/jetty-start/src/test/resources/usecases/empty.addToStart.assert.txt @@ -14,7 +14,7 @@ LIB|${jetty.home}/lib/extra/extra1.jar # The Properties we expect (order is irrelevant) PROP|extra.prop=value0 -PROP|main.prop=valueT +PROP|main.prop=value0 PROP|optional.prop=value0 # Files / Directories to create diff --git a/jetty-start/src/test/resources/usecases/empty.addToStartCreateStartd.assert.txt b/jetty-start/src/test/resources/usecases/empty.addToStartCreateStartd.assert.txt index 6b2ed3d8921..2445f98b829 100644 --- a/jetty-start/src/test/resources/usecases/empty.addToStartCreateStartd.assert.txt +++ b/jetty-start/src/test/resources/usecases/empty.addToStartCreateStartd.assert.txt @@ -14,12 +14,11 @@ LIB|${jetty.home}/lib/extra/extra1.jar # The Properties we expect (order is irrelevant) PROP|extra.prop=value0 -PROP|main.prop=valueT +PROP|main.prop=value0 PROP|optional.prop=value0 # Files / Directories to create EXISTS|maindir/ -EXISTS|start.d/main.ini EXISTS|start.d/extra.ini EXISTS|start.d/optional.ini diff --git a/jetty-start/src/test/resources/usecases/empty.createStartd.assert.txt b/jetty-start/src/test/resources/usecases/empty.createStartd.assert.txt index 01db3aca0da..91c169d0621 100644 --- a/jetty-start/src/test/resources/usecases/empty.createStartd.assert.txt +++ b/jetty-start/src/test/resources/usecases/empty.createStartd.assert.txt @@ -14,12 +14,11 @@ LIB|${jetty.home}/lib/extra/extra1.jar # The Properties we expect (order is irrelevant) PROP|extra.prop=value0 -PROP|main.prop=valueT +PROP|main.prop=value0 PROP|optional.prop=value0 # Files / Directories to create EXISTS|maindir/ EXISTS|start.d/ -EXISTS|start.d/main.ini EXISTS|start.d/extra.ini EXISTS|start.d/optional.ini diff --git a/jetty-start/src/test/resources/usecases/transientWithoutIniTemplate/modules/transient.mod b/jetty-start/src/test/resources/usecases/transientWithoutIniTemplate/modules/transient.mod index 222f937fecc..57a3e004817 100644 --- a/jetty-start/src/test/resources/usecases/transientWithoutIniTemplate/modules/transient.mod +++ b/jetty-start/src/test/resources/usecases/transientWithoutIniTemplate/modules/transient.mod @@ -4,5 +4,8 @@ etc/t.xml [optional] main +[ini] +transient.option=transient + [ini-template] transient.option=transient diff --git a/jetty-unixsocket/src/main/config/modules/unixsocket-forwarded.mod b/jetty-unixsocket/src/main/config/modules/unixsocket-forwarded.mod index 80d19995880..cb14381ebae 100644 --- a/jetty-unixsocket/src/main/config/modules/unixsocket-forwarded.mod +++ b/jetty-unixsocket/src/main/config/modules/unixsocket-forwarded.mod @@ -4,6 +4,9 @@ by the Unix Domain Socket connector, for use when behind a proxy operating in HTTP mode that adds forwarded-for style HTTP headers. Typically this is an alternate to the Proxy Protocol used mostly for TCP mode. +[Tags] +connector + [depend] unixsocket-http diff --git a/jetty-unixsocket/src/main/config/modules/unixsocket-http.mod b/jetty-unixsocket/src/main/config/modules/unixsocket-http.mod index 05c46bee795..1605722f685 100644 --- a/jetty-unixsocket/src/main/config/modules/unixsocket-http.mod +++ b/jetty-unixsocket/src/main/config/modules/unixsocket-http.mod @@ -4,6 +4,10 @@ It should be used when a proxy is forwarding either HTTP or decrypted HTTPS traffic to the connector and may be used with the unix-socket-http2c modules to upgrade to HTTP/2. +[Tags] +connector +http + [depend] unixsocket diff --git a/jetty-unixsocket/src/main/config/modules/unixsocket-http2c.mod b/jetty-unixsocket/src/main/config/modules/unixsocket-http2c.mod index 59f844977be..6e112221cfe 100644 --- a/jetty-unixsocket/src/main/config/modules/unixsocket-http2c.mod +++ b/jetty-unixsocket/src/main/config/modules/unixsocket-http2c.mod @@ -3,6 +3,10 @@ Adds a HTTP2C connetion factory to the Unix Domain Socket Connector It can be used when either the proxy forwards direct HTTP/2C (unecrypted) or decrypted HTTP/2 traffic. +[Tags] +connector +http2 + [depend] unixsocket-http diff --git a/jetty-unixsocket/src/main/config/modules/unixsocket-proxy-protocol.mod b/jetty-unixsocket/src/main/config/modules/unixsocket-proxy-protocol.mod index 11184d39471..cfa3c726fb7 100644 --- a/jetty-unixsocket/src/main/config/modules/unixsocket-proxy-protocol.mod +++ b/jetty-unixsocket/src/main/config/modules/unixsocket-proxy-protocol.mod @@ -8,6 +8,9 @@ SSL properties may be interpreted by the unixsocket-secure module to indicate secure HTTPS traffic. Typically this is an alternate to the forwarded module. +[Tags] +connector + [depend] unixsocket diff --git a/jetty-unixsocket/src/main/config/modules/unixsocket-secure.mod b/jetty-unixsocket/src/main/config/modules/unixsocket-secure.mod index 43344706038..8d099b539fa 100644 --- a/jetty-unixsocket/src/main/config/modules/unixsocket-secure.mod +++ b/jetty-unixsocket/src/main/config/modules/unixsocket-secure.mod @@ -5,6 +5,9 @@ This looks for a secure scheme transported either by the unixsocket-forwarded, unixsocket-proxy-protocol or in a HTTP2 request. +[Tags] +connector + [depend] unixsocket-http diff --git a/jetty-unixsocket/src/main/config/modules/unixsocket.mod b/jetty-unixsocket/src/main/config/modules/unixsocket.mod index c27ec9d2f43..f4defea923c 100644 --- a/jetty-unixsocket/src/main/config/modules/unixsocket.mod +++ b/jetty-unixsocket/src/main/config/modules/unixsocket.mod @@ -7,6 +7,9 @@ needless fragmentation and have better dispatch behaviours. When enabled with corresponding support modules, the connector can accept HTTP, HTTPS or HTTP2C traffic. +[Tags] +connector + [depend] server diff --git a/jetty-util/src/main/config/modules/jcl.mod b/jetty-util/src/main/config/modules/jcl-impl.mod similarity index 95% rename from jetty-util/src/main/config/modules/jcl.mod rename to jetty-util/src/main/config/modules/jcl-impl.mod index c72533a6c40..6569c5c52e4 100644 --- a/jetty-util/src/main/config/modules/jcl.mod +++ b/jetty-util/src/main/config/modules/jcl-impl.mod @@ -2,6 +2,11 @@ Provides a Java Commons Logging implementation. To receive jetty logs the jetty-slf4j and slf4j-jcl must also be enabled. +[tags] +logging +jcl +internal + [depends] [provides] diff --git a/jetty-util/src/main/config/modules/jcl-slf4j.mod b/jetty-util/src/main/config/modules/jcl-slf4j.mod index bc5ebfbbf03..7cabf92dcaa 100644 --- a/jetty-util/src/main/config/modules/jcl-slf4j.mod +++ b/jetty-util/src/main/config/modules/jcl-slf4j.mod @@ -1,6 +1,11 @@ [description] -Provides a Java Commons Logging implementation that logs to the SLF4J API. -Requires another module that provides and SLF4J implementation. +Provides a Java Commons Logging (JCL) to SLF4J logging bridge. + +[tags] +logging +jcl +slf4j +internal [depends] slf4j-api diff --git a/jetty-util/src/main/config/modules/jetty-jul.mod b/jetty-util/src/main/config/modules/jetty-jul.mod deleted file mode 100644 index ed7340af74c..00000000000 --- a/jetty-util/src/main/config/modules/jetty-jul.mod +++ /dev/null @@ -1,9 +0,0 @@ -[description] -Provides a Jetty Logging implementation that logs to the Java Util Logging API. -Requires another module that provides a Java Util Logging implementation. - -[provide] -logging - -[exec] --Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog diff --git a/jetty-util/src/main/config/modules/jetty-log4j.mod b/jetty-util/src/main/config/modules/jetty-log4j.mod deleted file mode 100644 index 7717400b784..00000000000 --- a/jetty-util/src/main/config/modules/jetty-log4j.mod +++ /dev/null @@ -1,8 +0,0 @@ -[description] -Provides a Jetty Logging implementation that logs to the log4j API. -Uses the slf4j mechanism as an intermediary -Requires another module that provides an log4j implementation. - -[depend] -jetty-slf4j -slf4j-log4j diff --git a/jetty-util/src/main/config/modules/jetty-log4j2.mod b/jetty-util/src/main/config/modules/jetty-log4j2.mod deleted file mode 100644 index c77ca46b8ba..00000000000 --- a/jetty-util/src/main/config/modules/jetty-log4j2.mod +++ /dev/null @@ -1,8 +0,0 @@ -[description] -Provides a Jetty Logging implementation that logs to the log4j API. -Uses the slf4j and log4j v1.2 mechanisms as intermediaries. -Requires another module that provides an log4j2 implementation. - -[depend] -jetty-slf4j -slf4j-log4j2 diff --git a/jetty-util/src/main/config/modules/jetty-logback.mod b/jetty-util/src/main/config/modules/jetty-logback.mod deleted file mode 100644 index e5373fd5301..00000000000 --- a/jetty-util/src/main/config/modules/jetty-logback.mod +++ /dev/null @@ -1,7 +0,0 @@ -[description] -Provides a Jetty Logging implementation that logs to logback. -Uses the slf4j API as an intermediary - -[depend] -jetty-slf4j -slf4j-logback diff --git a/jetty-util/src/main/config/modules/jetty-logging.mod b/jetty-util/src/main/config/modules/jetty-logging.mod deleted file mode 100644 index 3b46e5954ca..00000000000 --- a/jetty-util/src/main/config/modules/jetty-logging.mod +++ /dev/null @@ -1,13 +0,0 @@ -[description] -Enables the Jetty Logging implementation and installs a template -configuration in ${jetty.base} resources/jetty-logging.properties. - -[depends] -resources - -[provide] -logging - -[files] -basehome:modules/jetty-logging/jetty-logging.properties|resources/jetty-logging.properties - diff --git a/jetty-util/src/main/config/modules/jetty-slf4j.mod b/jetty-util/src/main/config/modules/jetty-slf4j.mod deleted file mode 100644 index 3836363748d..00000000000 --- a/jetty-util/src/main/config/modules/jetty-slf4j.mod +++ /dev/null @@ -1,13 +0,0 @@ -[description] -Provides a Jetty Logging implementation that logs to the SLF4J API. -Requires another module that provides and SLF4J implementation. - -[depend] -slf4j-api -slf4j-impl - -[provide] -logging - -[exec] --Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog diff --git a/jetty-util/src/main/config/modules/log4j.mod b/jetty-util/src/main/config/modules/log4j-impl.mod similarity index 95% rename from jetty-util/src/main/config/modules/log4j.mod rename to jetty-util/src/main/config/modules/log4j-impl.mod index 437a297afaa..be7bd2c0249 100644 --- a/jetty-util/src/main/config/modules/log4j.mod +++ b/jetty-util/src/main/config/modules/log4j-impl.mod @@ -2,6 +2,11 @@ Provides a Log4j v1.2 API and implementation. To receive jetty logs enable the jetty-slf4j and slf4j-log4j modules. +[tags] +logging +log4j +internal + [depends] resources diff --git a/jetty-util/src/main/config/modules/log4j-log4j2.mod b/jetty-util/src/main/config/modules/log4j-log4j2.mod index 7a055ee07e1..b17cf60e9c6 100644 --- a/jetty-util/src/main/config/modules/log4j-log4j2.mod +++ b/jetty-util/src/main/config/modules/log4j-log4j2.mod @@ -1,7 +1,11 @@ [description] -Provides a Log4j v1.2 implementation that logs to the Log4j v2 API. -Requires another module that provides and Log4j v2 implementation. -To receive jetty logs the jetty-slf4j and slf4j-log4j must also be enabled. +Provides a Log4j v1.2 to Log4j v2 logging bridge. + +[tags] +logging +log4j2 +log4j +internal [depends] log4j2-api diff --git a/jetty-util/src/main/config/modules/log4j2-api.mod b/jetty-util/src/main/config/modules/log4j2-api.mod index 3244acf19f0..cd54e43e2b5 100644 --- a/jetty-util/src/main/config/modules/log4j2-api.mod +++ b/jetty-util/src/main/config/modules/log4j2-api.mod @@ -1,7 +1,11 @@ [description] Provides the Log4j v2 API -Requires another module that provides an Log4j v2 implementation. -To receive jetty logs enable the jetty-slf4j, slf4j-log4j and log4j-log4j2 modules. + +[tags] +logging +log4j2 +log4j +internal [files] maven://org.apache.logging.log4j/log4j-api/${log4j2.version}|lib/log4j/log4j-api-${log4j2.version}.jar diff --git a/jetty-util/src/main/config/modules/log4j2-core.mod b/jetty-util/src/main/config/modules/log4j2-impl.mod similarity index 91% rename from jetty-util/src/main/config/modules/log4j2-core.mod rename to jetty-util/src/main/config/modules/log4j2-impl.mod index 9b04f2013ad..767c40cee57 100644 --- a/jetty-util/src/main/config/modules/log4j2-core.mod +++ b/jetty-util/src/main/config/modules/log4j2-impl.mod @@ -2,6 +2,12 @@ Provides a Log4j v2 implementation. To receive jetty logs enable the jetty-slf4j, slf4j-log4j and log4j-log4j2 modules. +[tags] +logging +log4j2 +log4j +internal + [depends] log4j2-api resources diff --git a/jetty-util/src/main/config/modules/log4j2-slf4j.mod b/jetty-util/src/main/config/modules/log4j2-slf4j.mod index 6324c0d37d4..20ea8bb72b6 100644 --- a/jetty-util/src/main/config/modules/log4j2-slf4j.mod +++ b/jetty-util/src/main/config/modules/log4j2-slf4j.mod @@ -1,7 +1,12 @@ [description] -Provides a Log4j v2 implementation that logs to the SLF4J API. -Requires another module that provides and SLF4J implementation. -To receive jetty logs enable the jetty-slf4j module. +Provides a Log4j v2 to SLF4J logging bridge. + +[tags] +logging +log4j2 +log4j +slf4j +internal [depends] log4j2-api diff --git a/jetty-util/src/main/config/modules/logback-core.mod b/jetty-util/src/main/config/modules/logback-impl.mod similarity index 93% rename from jetty-util/src/main/config/modules/logback-core.mod rename to jetty-util/src/main/config/modules/logback-impl.mod index fa2832bd52c..a6d3db96d36 100644 --- a/jetty-util/src/main/config/modules/logback-core.mod +++ b/jetty-util/src/main/config/modules/logback-impl.mod @@ -1,7 +1,11 @@ [description] -Provides the logback core implementation, used by slf4j-logback +Provides the logback core implementation and logback-access +[tags] +logging +internal + [files] maven://ch.qos.logback/logback-core/${logback.version}|lib/logback/logback-core-${logback.version}.jar diff --git a/jetty-util/src/main/config/modules/logging-jcl.mod b/jetty-util/src/main/config/modules/logging-jcl.mod new file mode 100644 index 00000000000..718affe557f --- /dev/null +++ b/jetty-util/src/main/config/modules/logging-jcl.mod @@ -0,0 +1,20 @@ +[description] +Configure jetty logging to use Java Commons Logging (jcl) +Uses SLF4j as a logging bridge. + +[tags] +logging + +[depends] +slf4j-jcl +jcl-impl + +[provide] +logging + +[exec] +-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog + +[ini-template] +## Hide logging classes from deployed webapps +jetty.webapp.addServerClasses,=file:${jetty.base}/lib/slf4j/,file:${jetty.base}/lib/jul diff --git a/jetty-util/src/main/config/modules/logging-jetty.mod b/jetty-util/src/main/config/modules/logging-jetty.mod new file mode 100644 index 00000000000..c7ad54ec27e --- /dev/null +++ b/jetty-util/src/main/config/modules/logging-jetty.mod @@ -0,0 +1,15 @@ +[description] +Configure jetty logging mechanism. +Provides a ${jetty.base}/resources/jetty-logging.properties. + +[tags] +logging + +[depends] +resources + +[provide] +logging + +[files] +basehome:modules/logging-jetty/jetty-logging.properties|resources/jetty-logging.properties diff --git a/jetty-util/src/main/config/modules/jetty-logging/jetty-logging.properties b/jetty-util/src/main/config/modules/logging-jetty/jetty-logging.properties similarity index 100% rename from jetty-util/src/main/config/modules/jetty-logging/jetty-logging.properties rename to jetty-util/src/main/config/modules/logging-jetty/jetty-logging.properties diff --git a/jetty-util/src/main/config/modules/logging-jul.mod b/jetty-util/src/main/config/modules/logging-jul.mod new file mode 100644 index 00000000000..7c7323c8a90 --- /dev/null +++ b/jetty-util/src/main/config/modules/logging-jul.mod @@ -0,0 +1,19 @@ +[description] +Configure jetty logging to use Java Util Logging (jul) +Uses SLF4j as a logging bridge. + +[tags] +logging + +[depends] +slf4j-jul + +[provide] +logging + +[exec] +-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog + +[ini-template] +## Hide logging classes from deployed webapps +jetty.webapp.addServerClasses,=file:${jetty.base}/lib/slf4j/ diff --git a/jetty-util/src/main/config/modules/logging-log4j.mod b/jetty-util/src/main/config/modules/logging-log4j.mod new file mode 100644 index 00000000000..ce81ca8ae23 --- /dev/null +++ b/jetty-util/src/main/config/modules/logging-log4j.mod @@ -0,0 +1,20 @@ +[description] +Configure jetty logging to use Log4j Logging +Uses SLF4j as a logging bridge. + +[tags] +logging + +[depends] +slf4j-log4j +log4j-impl + +[provide] +logging + +[exec] +-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog + +[ini-template] +## Hide logging classes from deployed webapps +jetty.webapp.addServerClasses,=file:${jetty.base}/lib/slf4j/,file:${jetty.base}/lib/log4j/ diff --git a/jetty-util/src/main/config/modules/logging-log4j2.mod b/jetty-util/src/main/config/modules/logging-log4j2.mod new file mode 100644 index 00000000000..81bbcf056df --- /dev/null +++ b/jetty-util/src/main/config/modules/logging-log4j2.mod @@ -0,0 +1,20 @@ +[description] +Configure jetty logging to use log4j version 2 +Uses SLF4j as a logging bridge. + +[tags] +logging + +[depends] +slf4j-log4j2 +log4j2-impl + +[provide] +logging + +[exec] +-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog + +[ini-template] +## Hide logging classes from deployed webapps +jetty.webapp.addServerClasses,=file:${jetty.base}/lib/slf4j/,file:${jetty.base}/lib/log4j/ diff --git a/jetty-util/src/main/config/modules/logging-logback.mod b/jetty-util/src/main/config/modules/logging-logback.mod new file mode 100644 index 00000000000..8223bce022a --- /dev/null +++ b/jetty-util/src/main/config/modules/logging-logback.mod @@ -0,0 +1,20 @@ +[description] +Configure jetty logging to use Logback Logging. +Uses SLF4j as a logging bridge. + +[tags] +logging + +[depends] +slf4j-logback +logback-impl + +[provide] +logging + +[exec] +-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog + +[ini-template] +## Hide logging classes from deployed webapps +jetty.webapp.addServerClasses,=file:${jetty.base}/lib/slf4j/,file:${jetty.base}/lib/logback diff --git a/jetty-util/src/main/config/modules/logging-slf4j.mod b/jetty-util/src/main/config/modules/logging-slf4j.mod new file mode 100644 index 00000000000..24b5f367cc7 --- /dev/null +++ b/jetty-util/src/main/config/modules/logging-slf4j.mod @@ -0,0 +1,19 @@ +[description] +Configure jetty logging to use slf4j. +Any slf4j-impl implementation is used + +[tags] +logging + +[depends] +slf4j-impl + +[provide] +logging + +[exec] +-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog + +[ini-template] +## Hide logging classes from deployed webapps +jetty.webapp.addServerClasses,=file:${jetty.base}/lib/slf4j/ diff --git a/jetty-util/src/main/config/modules/logging/logging-classpath.xml b/jetty-util/src/main/config/modules/logging/logging-classpath.xml new file mode 100644 index 00000000000..1f8e737e9bf --- /dev/null +++ b/jetty-util/src/main/config/modules/logging/logging-classpath.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-util/src/main/config/modules/slf4j-api.mod b/jetty-util/src/main/config/modules/slf4j-api.mod index b4eda7f19f8..e2e787f8c4f 100644 --- a/jetty-util/src/main/config/modules/slf4j-api.mod +++ b/jetty-util/src/main/config/modules/slf4j-api.mod @@ -2,6 +2,11 @@ Provides SLF4J API. Requires a slf4j implementation (eg slf4j-simple) otherwise a noop implementation is used. +[tags] +logging +slf4j +internal + [files] maven://org.slf4j/slf4j-api/${slf4j.version}|lib/slf4j/slf4j-api-${slf4j.version}.jar diff --git a/jetty-util/src/main/config/modules/slf4j-simple.mod b/jetty-util/src/main/config/modules/slf4j-impl.mod similarity index 90% rename from jetty-util/src/main/config/modules/slf4j-simple.mod rename to jetty-util/src/main/config/modules/slf4j-impl.mod index 804cde01a03..c44b8bd739c 100644 --- a/jetty-util/src/main/config/modules/slf4j-simple.mod +++ b/jetty-util/src/main/config/modules/slf4j-impl.mod @@ -2,6 +2,11 @@ Provides SLF4J simple logging implementation. To receive jetty logs enable the jetty-slf4j module. +[tags] +logging +slf4j +internal + [depend] slf4j-api diff --git a/jetty-util/src/main/config/modules/slf4j-jcl.mod b/jetty-util/src/main/config/modules/slf4j-jcl.mod index 6a9773a048c..d1819515258 100644 --- a/jetty-util/src/main/config/modules/slf4j-jcl.mod +++ b/jetty-util/src/main/config/modules/slf4j-jcl.mod @@ -1,7 +1,11 @@ [description] -Provides a SLF4J implementation that logs to the Java Commons Logging API. -Requires another module that provides an JCL implementation. -To receive jetty logs enable the jetty-slf4j module. +Provides a SLF4J to Java Commons Logging (JCL) logging bridge. + +[tags] +logging +jcl +slf4j +internal [depend] slf4j-api diff --git a/jetty-util/src/main/config/modules/slf4j-jul.mod b/jetty-util/src/main/config/modules/slf4j-jul.mod index fc858778d2f..f308147e021 100644 --- a/jetty-util/src/main/config/modules/slf4j-jul.mod +++ b/jetty-util/src/main/config/modules/slf4j-jul.mod @@ -1,6 +1,10 @@ [description] -Provides a SLF4J implementation that logs to the Java Util Logging API. -To receive jetty logs enable the jetty-slf4j module. +Provides a SLF4J to Java Util Logging (JUL) logging bridge. + +[tags] +logging +slf4j +internal [depend] slf4j-api diff --git a/jetty-util/src/main/config/modules/slf4j-log4j.mod b/jetty-util/src/main/config/modules/slf4j-log4j.mod index 75c98577d02..e248480ec28 100644 --- a/jetty-util/src/main/config/modules/slf4j-log4j.mod +++ b/jetty-util/src/main/config/modules/slf4j-log4j.mod @@ -1,7 +1,11 @@ [description] -Provides a SLF4J implementation that logs to the Log4j v1.2 API. -Requires another module that provides a Log4j implementation. -To receive jetty logs enable the jetty-slf4j module. +Provides a SLF4J to the Log4j v1.2 API logging bridge. + +[tags] +logging +log4j +slf4j +internal [depend] slf4j-api diff --git a/jetty-util/src/main/config/modules/slf4j-log4j2.mod b/jetty-util/src/main/config/modules/slf4j-log4j2.mod index 2f9d023059e..445d38017f1 100644 --- a/jetty-util/src/main/config/modules/slf4j-log4j2.mod +++ b/jetty-util/src/main/config/modules/slf4j-log4j2.mod @@ -1,7 +1,12 @@ [description] -Provides a SLF4J implementation that logs to the Log4j v2 API. -Requires another module that provides a Log4j2 implementation. -To receive jetty logs enable the jetty-slf4j2 module. +Provides a SLF4J to Log4j v2 logging bridge. + +[tags] +logging +log4j2 +log4j +slf4j +internal [depend] slf4j-api diff --git a/jetty-util/src/main/config/modules/slf4j-logback.mod b/jetty-util/src/main/config/modules/slf4j-logback.mod index 0a23efdad13..6d33a89ced5 100644 --- a/jetty-util/src/main/config/modules/slf4j-logback.mod +++ b/jetty-util/src/main/config/modules/slf4j-logback.mod @@ -1,10 +1,14 @@ [description] -Provides a SLF4J implementation that logs to Logback classic -To receive jetty logs enable the jetty-slf4j module. +Provides a SLF4J to Logback logging bridge. + +[tags] +logging +slf4j +internal [depend] slf4j-api -logback-core +logback-impl resources [provide] diff --git a/jetty-util/src/main/config/modules/stderrout-logging.mod b/jetty-util/src/main/config/modules/stderrout-logging.mod index 846f2dabd84..b1dc2af91e6 100644 --- a/jetty-util/src/main/config/modules/stderrout-logging.mod +++ b/jetty-util/src/main/config/modules/stderrout-logging.mod @@ -2,6 +2,9 @@ Redirects JVMs stderr and stdout to a log file, including output from Jetty's default StdErrLog logging. +[tags] +logging + [xml] etc/stderrout-logging.xml @@ -9,7 +12,6 @@ etc/stderrout-logging.xml logs/ [lib] -lib/logging/**.jar resources/ [ini-template] diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java index 710444d353a..8d095f19a06 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java @@ -19,8 +19,10 @@ package org.eclipse.jetty.util; import java.nio.ByteBuffer; +import java.util.AbstractMap; import java.util.Arrays; import java.util.HashSet; +import java.util.Map; import java.util.Set; @@ -495,6 +497,39 @@ public class ArrayTernaryTrie extends AbstractTrie return keys; } + public int size() + { + int s=0; + for (int r=0;r<=_rows;r++) + { + if (_key[r]!=null && _value[r]!=null) + s++; + } + return s; + } + + public boolean isEmpty() + { + for (int r=0;r<=_rows;r++) + { + if (_key[r]!=null && _value[r]!=null) + return false; + } + return true; + } + + + public Set> entrySet() + { + Set> entries = new HashSet<>(); + for (int r=0;r<=_rows;r++) + { + if (_key[r]!=null && _value[r]!=null) + entries.add(new AbstractMap.SimpleEntry<>(_key[r],_value[r])); + } + return entries; + } + @Override public boolean isFull() { @@ -524,4 +559,143 @@ public class ArrayTernaryTrie extends AbstractTrie } } + + public static class Growing implements Trie + { + private final int _growby; + private ArrayTernaryTrie _trie; + + public Growing() + { + this(1024,1024); + } + + public Growing(int capacity, int growby) + { + _growby=growby; + _trie = new ArrayTernaryTrie<>(capacity); + } + + public Growing(boolean insensitive, int capacity, int growby) + { + _growby=growby; + _trie = new ArrayTernaryTrie<>(insensitive,capacity); + } + + public boolean put(V v) + { + return put(v.toString(),v); + } + + public int hashCode() + { + return _trie.hashCode(); + } + + public V remove(String s) + { + return _trie.remove(s); + } + + public V get(String s) + { + return _trie.get(s); + } + + public V get(ByteBuffer b) + { + return _trie.get(b); + } + + public V getBest(byte[] b, int offset, int len) + { + return _trie.getBest(b,offset,len); + } + + public boolean isCaseInsensitive() + { + return _trie.isCaseInsensitive(); + } + + public boolean equals(Object obj) + { + return _trie.equals(obj); + } + + public void clear() + { + _trie.clear(); + } + + public boolean put(String s, V v) + { + boolean added = _trie.put(s,v); + while (!added) + { + ArrayTernaryTrie bigger = new ArrayTernaryTrie<>(_trie._key.length+_growby); + for (Map.Entry entry : _trie.entrySet()) + bigger.put(entry.getKey(),entry.getValue()); + added = _trie.put(s,v); + } + + return true; + } + + public V get(String s, int offset, int len) + { + return _trie.get(s,offset,len); + } + + public V get(ByteBuffer b, int offset, int len) + { + return _trie.get(b,offset,len); + } + + public V getBest(String s) + { + return _trie.getBest(s); + } + + public V getBest(String s, int offset, int length) + { + return _trie.getBest(s,offset,length); + } + + public V getBest(ByteBuffer b, int offset, int len) + { + return _trie.getBest(b,offset,len); + } + + public String toString() + { + return _trie.toString(); + } + + public Set keySet() + { + return _trie.keySet(); + } + + public boolean isFull() + { + return false; + } + + public void dump() + { + _trie.dump(); + } + + public boolean isEmpty() + { + return _trie.isEmpty(); + } + + public int size() + { + return _trie.size(); + } + + } + } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java index 622587f471d..656b434d03e 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java @@ -25,21 +25,21 @@ import java.util.function.Predicate; /** Utility class to maintain a set of inclusions and exclusions. - *

    Maintains a set of included and excluded elements. The method {@link #matches(Object)} + *

    Maintains a set of included and excluded elements. The method {@link #test(Object)} * will return true IFF the passed object is not in the excluded set AND ( either the * included set is empty OR the object is in the included set) *

    The type of the underlying {@link Set} used may be passed into the * constructor, so special sets like Servlet PathMap may be used. *

    - * @param

    The type of element of the set (often a pattern) - * @param The type of the instance passed to the predicate + * @param The type of element of the set (often a pattern) + * @param

    The type of the instance passed to the predicate */ -public class IncludeExcludeSet implements Predicate +public class IncludeExcludeSet implements Predicate

    { - private final Set

    _includes; - private final Predicate _includePredicate; - private final Set

    _excludes; - private final Predicate _excludePredicate; + private final Set _includes; + private final Predicate

    _includePredicate; + private final Set _excludes; + private final Predicate

    _excludePredicate; private static class SetContainsPredicate implements Predicate { @@ -73,7 +73,7 @@ public class IncludeExcludeSet implements Predicate * is created. * @param The type of a set to use as the backing store */ - public > IncludeExcludeSet(Class setClass) + public > IncludeExcludeSet(Class setClass) { try { @@ -82,7 +82,7 @@ public class IncludeExcludeSet implements Predicate if(_includes instanceof Predicate) { - _includePredicate = (Predicate)_includes; + _includePredicate = (Predicate

    )_includes; } else { @@ -91,7 +91,7 @@ public class IncludeExcludeSet implements Predicate if(_excludes instanceof Predicate) { - _excludePredicate = (Predicate)_excludes; + _excludePredicate = (Predicate

    )_excludes; } else { @@ -113,7 +113,7 @@ public class IncludeExcludeSet implements Predicate * @param excludePredicate the Predicate for excluded item testing (null for simple {@link Set#contains(Object)} test) * @param The type of a set to use as the backing store */ - public > IncludeExcludeSet(Set

    includeSet, Predicate includePredicate, Set

    excludeSet, Predicate excludePredicate) + public > IncludeExcludeSet(Set includeSet, Predicate

    includePredicate, Set excludeSet, Predicate

    excludePredicate) { Objects.requireNonNull(includeSet,"Include Set"); Objects.requireNonNull(includePredicate,"Include Predicate"); @@ -126,51 +126,73 @@ public class IncludeExcludeSet implements Predicate _excludePredicate = excludePredicate; } - public void include(P element) + public void include(T element) { _includes.add(element); } - public void include(P... element) + public void include(T... element) { - for (P e: element) + for (T e: element) _includes.add(e); } - public void exclude(P element) + public void exclude(T element) { _excludes.add(element); } - public void exclude(P... element) + public void exclude(T... element) { - for (P e: element) + for (T e: element) _excludes.add(e); } - - public boolean matches(T t) + + @Deprecated + public boolean matches(P t) { return test(t); } - public boolean test(T t) + @Override + public boolean test(P t) { if (!_includes.isEmpty() && !_includePredicate.test(t)) return false; return !_excludePredicate.test(t); } + /** + * Test Included and not Excluded + * @param t The item to test + * @return Boolean.TRUE if t is included, Boolean.FALSE if t is excluded and null if neither + */ + public Boolean isIncludedAndNotExcluded(P t) + { + if (_excludePredicate.test(t)) + return Boolean.FALSE; + if (_includePredicate.test(t)) + return Boolean.TRUE; + + return null; + } + + public boolean hasIncludes() + { + return !_includes.isEmpty(); + } + public int size() { return _includes.size()+_excludes.size(); } - public Set

    getIncluded() + public Set getIncluded() { return _includes; } - public Set

    getExcluded() + public Set getExcluded() { return _excludes; } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java index 1655f552966..9eb355dafb9 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java @@ -29,7 +29,7 @@ import java.util.Set; public interface Trie { /* ------------------------------------------------------------ */ - /** Put and entry into the Trie + /** Put an entry into the Trie * @param s The key for the entry * @param v The value of the entry * @return True if the Trie had capacity to add the field. @@ -47,14 +47,14 @@ public interface Trie public V remove(String s); /* ------------------------------------------------------------ */ - /** Get and exact match from a String key + /** Get an exact match from a String key * @param s The key * @return the value for the string key */ public V get(String s); /* ------------------------------------------------------------ */ - /** Get and exact match from a String key + /** Get an exact match from a String key * @param s The key * @param offset The offset within the string of the key * @param len the length of the key @@ -63,14 +63,14 @@ public interface Trie public V get(String s,int offset,int len); /* ------------------------------------------------------------ */ - /** Get and exact match from a segment of a ByteBuufer as key + /** Get an exact match from a segment of a ByteBuufer as key * @param b The buffer * @return The value or null if not found */ public V get(ByteBuffer b); /* ------------------------------------------------------------ */ - /** Get and exact match from a segment of a ByteBuufer as key + /** Get an exact match from a segment of a ByteBuufer as key * @param b The buffer * @param offset The offset within the buffer of the key * @param len the length of the key diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index 0173e55723d..31120dbf5e9 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -24,6 +24,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -31,9 +34,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import javax.servlet.ServletContainerInitializer; + import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.resource.Resource; /* ------------------------------------------------------------ */ @@ -693,4 +699,38 @@ public class TypeUtil return !((Boolean)o).booleanValue(); return "false".equalsIgnoreCase(o.toString()); } + + /* ------------------------------------------------------------ */ + public static Resource getLoadedFrom(Class clazz) + { + ProtectionDomain domain = clazz.getProtectionDomain(); + if (domain!=null) + { + CodeSource source = domain.getCodeSource(); + if (source!=null) + { + URL location = source.getLocation(); + + if (location!=null) + return Resource.newResource(location); + } + } + + String rname = clazz.getName().replace('.','/')+".class"; + ClassLoader loader = clazz.getClassLoader(); + URL url = (loader==null?ClassLoader.getSystemClassLoader():loader).getResource(rname); + if (url!=null) + { + try + { + return Resource.newResource(URIUtil.getJarSource(url.toString())); + } + catch(Exception e) + { + LOG.debug(e); + } + } + + return null; + } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 56652ea6147..742a69d31d7 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.util; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.*; @@ -843,4 +844,30 @@ public class URIUtil return URI.create(buf.toString()); } + + public static URI getJarSource(URI uri) + { + try + { + if (!"jar".equals(uri.getScheme())) + return uri; + String s = uri.getSchemeSpecificPart(); + int bang_slash = s.indexOf("!/"); + if (bang_slash>=0) + s=s.substring(0,bang_slash); + return new URI(s); + } + catch(URISyntaxException e) + { + throw new IllegalArgumentException(e); + } + } + + public static String getJarSource(String uri) + { + if (!uri.startsWith("jar:")) + return uri; + int bang_slash = uri.indexOf("!/"); + return (bang_slash>=0)?uri.substring(4,bang_slash):uri.substring(4); + } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java index 6cd707d2156..f911ab23244 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java @@ -43,6 +43,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -104,12 +105,12 @@ public class SslContextFactory extends AbstractLifeCycle private static final Logger LOG = Log.getLogger(SslContextFactory.class); public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM = - (Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ? - KeyManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.KeyManagerFactory.algorithm")); + (Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ? + KeyManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.KeyManagerFactory.algorithm")); public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM = - (Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ? - TrustManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.TrustManagerFactory.algorithm")); + (Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ? + TrustManagerFactory.getDefaultAlgorithm() : Security.getProperty("ssl.TrustManagerFactory.algorithm")); /** String name of key password property. */ public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword"; @@ -117,111 +118,51 @@ public class SslContextFactory extends AbstractLifeCycle /** String name of keystore password property. */ public static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password"; - /** Excluded protocols. */ private final Set _excludeProtocols = new LinkedHashSet<>(); - - /** Included protocols. */ private final Set _includeProtocols = new LinkedHashSet<>(); - - /** Selected protocols. */ - private String[] _selectedProtocols; - - /** Excluded cipher suites. */ private final Set _excludeCipherSuites = new LinkedHashSet<>(); - - /** Included cipher suites. */ private final List _includeCipherSuites = new ArrayList<>(); - private boolean _useCipherSuitesOrder=true; - - /** Cipher comparator for ordering ciphers */ - Comparator _cipherComparator; - - /** Selected cipher suites. Combination of includes, excludes, available and ordering */ + private final Map _aliasX509 = new HashMap<>(); + private final Map _certHosts = new HashMap<>(); + private final Map _certWilds = new HashMap<>(); + private String[] _selectedProtocols; + private boolean _useCipherSuitesOrder = true; + private Comparator _cipherComparator; private String[] _selectedCipherSuites; - - /** Keystore path. */ private Resource _keyStoreResource; - /** Keystore provider name */ private String _keyStoreProvider; - /** Keystore type */ private String _keyStoreType = "JKS"; - - /** SSL certificate alias */ private String _certAlias; - private final Map _aliasX509 = new HashMap<>(); - private final Map _certHosts = new HashMap<>(); - private final Map _certWilds = new HashMap<>(); - - /** Truststore path */ private Resource _trustStoreResource; - /** Truststore provider name */ private String _trustStoreProvider; - /** Truststore type */ private String _trustStoreType = "JKS"; - - /** Set to true if client certificate authentication is required */ private boolean _needClientAuth = false; - /** Set to true if client certificate authentication is desired */ private boolean _wantClientAuth = false; - - /** Keystore password */ private Password _keyStorePassword; - /** Key manager password */ private Password _keyManagerPassword; - /** Truststore password */ private Password _trustStorePassword; - - /** SSL provider name */ private String _sslProvider; - /** SSL protocol name */ private String _sslProtocol = "TLS"; - - /** SecureRandom algorithm */ private String _secureRandomAlgorithm; - /** KeyManager factory algorithm */ private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM; - /** TrustManager factory algorithm */ private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM; - - /** Set to true if SSL certificate validation is required */ private boolean _validateCerts; - /** Set to true if SSL certificate of the peer validation is required */ private boolean _validatePeerCerts; - /** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */ private int _maxCertPathLength = -1; - /** Path to file that contains Certificate Revocation List */ private String _crlPath; - /** Set to true to enable CRL Distribution Points (CRLDP) support */ private boolean _enableCRLDP = false; - /** Set to true to enable On-Line Certificate Status Protocol (OCSP) support */ private boolean _enableOCSP = false; - /** Location of OCSP Responder */ private String _ocspResponderURL; - - /** SSL keystore */ private KeyStore _setKeyStore; - /** SSL truststore */ private KeyStore _setTrustStore; - /** Set to true to enable SSL Session caching */ private boolean _sessionCachingEnabled = true; - /** SSL session cache size */ - private int _sslSessionCacheSize=-1; - /** SSL session timeout */ - private int _sslSessionTimeout=-1; - - /** SSL context */ + private int _sslSessionCacheSize = -1; + private int _sslSessionTimeout = -1; private SSLContext _setContext; - - /** EndpointIdentificationAlgorithm - when set to "HTTPS" hostname verification will be enabled */ private String _endpointIdentificationAlgorithm = null; - - /** Whether to blindly trust certificates */ private boolean _trustAll; - - /** Whether TLS renegotiation is allowed */ private boolean _renegotiationAllowed = true; - - protected Factory _factory; + private Factory _factory; /** * Construct an instance of SslContextFactory @@ -235,6 +176,7 @@ public class SslContextFactory extends AbstractLifeCycle /** * Construct an instance of SslContextFactory * Default constructor for use in XmlConfiguration files + * * @param trustAll whether to blindly trust all certificates * @see #setTrustAll(boolean) */ @@ -245,6 +187,7 @@ public class SslContextFactory extends AbstractLifeCycle /** * Construct an instance of SslContextFactory + * * @param keyStorePath default keystore location */ public SslContextFactory(String keyStorePath) @@ -261,14 +204,157 @@ public class SslContextFactory extends AbstractLifeCycle setKeyStorePath(keyStorePath); } + /** + * Creates the SSLContext object and starts the lifecycle + */ + @Override + protected void doStart() throws Exception + { + super.doStart(); + synchronized (this) + { + load(); + } + } + + private void load() throws Exception + { + SSLContext context = _setContext; + KeyStore keyStore = _setKeyStore; + KeyStore trustStore = _setTrustStore; + + if (context == null) + { + // Is this an empty factory? + if (keyStore == null && _keyStoreResource == null && trustStore == null && _trustStoreResource == null) + { + TrustManager[] trust_managers = null; + + if (isTrustAll()) + { + if (LOG.isDebugEnabled()) + LOG.debug("No keystore or trust store configured. ACCEPTING UNTRUSTED CERTIFICATES!!!!!"); + // Create a trust manager that does not validate certificate chains + trust_managers = TRUST_ALL_CERTS; + } + + String algorithm = getSecureRandomAlgorithm(); + SecureRandom secureRandom = algorithm == null ? null : SecureRandom.getInstance(algorithm); + context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider); + context.init(null, trust_managers, secureRandom); + } + else + { + if (keyStore == null) + keyStore = loadKeyStore(_keyStoreResource); + if (trustStore == null) + trustStore = loadTrustStore(_trustStoreResource); + + Collection crls = loadCRL(getCrlPath()); + + // Look for X.509 certificates to create alias map + if (keyStore != null) + { + for (String alias : Collections.list(keyStore.aliases())) + { + Certificate certificate = keyStore.getCertificate(alias); + if (certificate != null && "X.509".equals(certificate.getType())) + { + X509Certificate x509C = (X509Certificate)certificate; + + // Exclude certificates with special uses + if (X509.isCertSign(x509C)) + { + if (LOG.isDebugEnabled()) + LOG.debug("Skipping " + x509C); + continue; + } + X509 x509 = new X509(alias, x509C); + _aliasX509.put(alias, x509); + + if (isValidateCerts()) + { + CertificateValidator validator = new CertificateValidator(trustStore, crls); + validator.setMaxCertPathLength(getMaxCertPathLength()); + validator.setEnableCRLDP(isEnableCRLDP()); + validator.setEnableOCSP(isEnableOCSP()); + validator.setOcspResponderURL(getOcspResponderURL()); + validator.validate(keyStore, x509C); // TODO what about truststore? + } + + LOG.info("x509={} for {}", x509, this); + + for (String h : x509.getHosts()) + _certHosts.put(h, x509); + for (String w : x509.getWilds()) + _certWilds.put(w, x509); + } + } + } + + // Instantiate key and trust managers + KeyManager[] keyManagers = getKeyManagers(keyStore); + TrustManager[] trustManagers = getTrustManagers(trustStore, crls); + + // Initialize context + SecureRandom secureRandom = (_secureRandomAlgorithm == null) ? null : SecureRandom.getInstance(_secureRandomAlgorithm); + context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider); + context.init(keyManagers, trustManagers, secureRandom); + } + } + + // Initialize cache + SSLSessionContext serverContext = context.getServerSessionContext(); + if (serverContext != null) + { + if (getSslSessionCacheSize() > -1) + serverContext.setSessionCacheSize(getSslSessionCacheSize()); + if (getSslSessionTimeout() > -1) + serverContext.setSessionTimeout(getSslSessionTimeout()); + } + + // select the protocols and ciphers + SSLParameters enabled = context.getDefaultSSLParameters(); + SSLParameters supported = context.getSupportedSSLParameters(); + selectCipherSuites(enabled.getCipherSuites(), supported.getCipherSuites()); + selectProtocols(enabled.getProtocols(), supported.getProtocols()); + + _factory = new Factory(keyStore, trustStore, context); + if (LOG.isDebugEnabled()) + { + LOG.debug("Selected Protocols {} of {}", Arrays.asList(_selectedProtocols), Arrays.asList(supported.getProtocols())); + LOG.debug("Selected Ciphers {} of {}", Arrays.asList(_selectedCipherSuites), Arrays.asList(supported.getCipherSuites())); + } + } + + @Override + protected void doStop() throws Exception + { + synchronized (this) + { + unload(); + } + super.doStop(); + } + + private void unload() + { + _factory = null; + _selectedProtocols = null; + _selectedCipherSuites = null; + _aliasX509.clear(); + _certHosts.clear(); + _certWilds.clear(); + } + public String[] getSelectedProtocols() { - return Arrays.copyOf(_selectedProtocols,_selectedProtocols.length); + return Arrays.copyOf(_selectedProtocols, _selectedProtocols.length); } public String[] getSelectedCipherSuites() { - return Arrays.copyOf(_selectedCipherSuites,_selectedCipherSuites.length); + return Arrays.copyOf(_selectedCipherSuites, _selectedCipherSuites.length); } public Comparator getCipherComparator() @@ -278,7 +364,7 @@ public class SslContextFactory extends AbstractLifeCycle public void setCipherComparator(Comparator cipherComparator) { - if (cipherComparator!=null) + if (cipherComparator != null) setUseCipherSuitesOrder(true); _cipherComparator = cipherComparator; } @@ -293,148 +379,21 @@ public class SslContextFactory extends AbstractLifeCycle return _aliasX509.get(alias); } - /** - * Create the SSLContext object and start the lifecycle - * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() - */ - @Override - protected void doStart() throws Exception - { - SSLContext context = _setContext; - KeyStore keyStore = _setKeyStore; - KeyStore trustStore = _setTrustStore; - - if (context == null) - { - // Is this an empty factory? - if (keyStore==null && _keyStoreResource == null && trustStore==null && _trustStoreResource == null ) - { - TrustManager[] trust_managers=null; - - if (_trustAll) - { - if (LOG.isDebugEnabled()) - LOG.debug("No keystore or trust store configured. ACCEPTING UNTRUSTED CERTIFICATES!!!!!"); - // Create a trust manager that does not validate certificate chains - trust_managers = TRUST_ALL_CERTS; - } - - SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm); - context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider); - context.init(null, trust_managers, secureRandom); - } - else - { - if (keyStore==null) - keyStore=loadKeyStore(_keyStoreResource); - if (trustStore==null) - trustStore=loadTrustStore(_trustStoreResource); - - Collection crls = loadCRL(_crlPath); - - // Look for X.509 certificates to create alias map - _certHosts.clear(); - if (keyStore!=null) - { - for (String alias : Collections.list(keyStore.aliases())) - { - Certificate certificate = keyStore.getCertificate(alias); - if (certificate!=null && "X.509".equals(certificate.getType())) - { - X509Certificate x509C = (X509Certificate)certificate; - - // Exclude certificates with special uses - if (X509.isCertSign(x509C)) - { - if (LOG.isDebugEnabled()) - LOG.debug("Skipping "+x509C); - continue; - } - X509 x509 = new X509(alias,x509C); - _aliasX509.put(alias,x509); - - if (_validateCerts) - { - CertificateValidator validator = new CertificateValidator(trustStore, crls); - validator.setMaxCertPathLength(_maxCertPathLength); - validator.setEnableCRLDP(_enableCRLDP); - validator.setEnableOCSP(_enableOCSP); - validator.setOcspResponderURL(_ocspResponderURL); - validator.validate(keyStore, x509C); // TODO what about truststore? - } - - LOG.info("x509={} for {}",x509,this); - - for (String h:x509.getHosts()) - _certHosts.put(h,x509); - for (String w:x509.getWilds()) - _certWilds.put(w,x509); - } - } - } - - // Instantiate key and trust managers - KeyManager[] keyManagers = getKeyManagers(keyStore); - TrustManager[] trustManagers = getTrustManagers(trustStore,crls); - - // Initialize context - SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm); - context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider); - context.init(keyManagers,trustManagers,secureRandom); - } - } - - // Initialize cache - SSLSessionContext serverContext=context.getServerSessionContext(); - if (serverContext!=null) - { - if (getSslSessionCacheSize()>-1) - serverContext.setSessionCacheSize(getSslSessionCacheSize()); - if (getSslSessionTimeout()>-1) - serverContext.setSessionTimeout(getSslSessionTimeout()); - } - - // select the protocols and ciphers - SSLParameters enabled=context.getDefaultSSLParameters(); - SSLParameters supported=context.getSupportedSSLParameters(); - selectCipherSuites(enabled.getCipherSuites(),supported.getCipherSuites()); - selectProtocols(enabled.getProtocols(),supported.getProtocols()); - - _factory = new Factory(keyStore,trustStore,context); - if (LOG.isDebugEnabled()) - { - LOG.debug("Selected Protocols {} of {}",Arrays.asList(_selectedProtocols),Arrays.asList(supported.getProtocols())); - LOG.debug("Selected Ciphers {} of {}",Arrays.asList(_selectedCipherSuites),Arrays.asList(supported.getCipherSuites())); - } - } - - @Override - protected void doStop() throws Exception - { - _factory = null; - super.doStop(); - _certHosts.clear(); - _certWilds.clear(); - _aliasX509.clear(); - } - /** * @return The array of protocol names to exclude from * {@link SSLEngine#setEnabledProtocols(String[])} */ public String[] getExcludeProtocols() { - return _excludeProtocols.toArray(new String[_excludeProtocols.size()]); + return _excludeProtocols.toArray(new String[0]); } /** - * @param protocols - * The array of protocol names to exclude from - * {@link SSLEngine#setEnabledProtocols(String[])} + * @param protocols The array of protocol names to exclude from + * {@link SSLEngine#setEnabledProtocols(String[])} */ public void setExcludeProtocols(String... protocols) { - checkNotStarted(); _excludeProtocols.clear(); _excludeProtocols.addAll(Arrays.asList(protocols)); } @@ -444,7 +403,6 @@ public class SslContextFactory extends AbstractLifeCycle */ public void addExcludeProtocols(String... protocol) { - checkNotStarted(); _excludeProtocols.addAll(Arrays.asList(protocol)); } @@ -454,17 +412,15 @@ public class SslContextFactory extends AbstractLifeCycle */ public String[] getIncludeProtocols() { - return _includeProtocols.toArray(new String[_includeProtocols.size()]); + return _includeProtocols.toArray(new String[0]); } /** - * @param protocols - * The array of protocol names to include in - * {@link SSLEngine#setEnabledProtocols(String[])} + * @param protocols The array of protocol names to include in + * {@link SSLEngine#setEnabledProtocols(String[])} */ public void setIncludeProtocols(String... protocols) { - checkNotStarted(); _includeProtocols.clear(); _includeProtocols.addAll(Arrays.asList(protocols)); } @@ -475,18 +431,17 @@ public class SslContextFactory extends AbstractLifeCycle */ public String[] getExcludeCipherSuites() { - return _excludeCipherSuites.toArray(new String[_excludeCipherSuites.size()]); + return _excludeCipherSuites.toArray(new String[0]); } /** * You can either use the exact cipher suite name or a a regular expression. - * @param cipherSuites - * The array of cipher suite names to exclude from - * {@link SSLEngine#setEnabledCipherSuites(String[])} + * + * @param cipherSuites The array of cipher suite names to exclude from + * {@link SSLEngine#setEnabledCipherSuites(String[])} */ public void setExcludeCipherSuites(String... cipherSuites) { - checkNotStarted(); _excludeCipherSuites.clear(); _excludeCipherSuites.addAll(Arrays.asList(cipherSuites)); } @@ -496,7 +451,6 @@ public class SslContextFactory extends AbstractLifeCycle */ public void addExcludeCipherSuites(String... cipher) { - checkNotStarted(); _excludeCipherSuites.addAll(Arrays.asList(cipher)); } @@ -506,18 +460,17 @@ public class SslContextFactory extends AbstractLifeCycle */ public String[] getIncludeCipherSuites() { - return _includeCipherSuites.toArray(new String[_includeCipherSuites.size()]); + return _includeCipherSuites.toArray(new String[0]); } /** * You can either use the exact cipher suite name or a a regular expression. - * @param cipherSuites - * The array of cipher suite names to include in - * {@link SSLEngine#setEnabledCipherSuites(String[])} + * + * @param cipherSuites The array of cipher suite names to include in + * {@link SSLEngine#setEnabledCipherSuites(String[])} */ public void setIncludeCipherSuites(String... cipherSuites) { - checkNotStarted(); _includeCipherSuites.clear(); _includeCipherSuites.addAll(Arrays.asList(cipherSuites)); } @@ -541,12 +494,10 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param keyStorePath - * The file or URL of the SSL Key store. + * @param keyStorePath The file or URL of the SSL Key store. */ public void setKeyStorePath(String keyStorePath) { - checkNotStarted(); try { _keyStoreResource = Resource.newResource(keyStorePath); @@ -566,12 +517,10 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param keyStoreProvider - * The provider of the key store + * @param keyStoreProvider The provider of the key store */ public void setKeyStoreProvider(String keyStoreProvider) { - checkNotStarted(); _keyStoreProvider = keyStoreProvider; } @@ -584,12 +533,10 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param keyStoreType - * The type of the key store (default "JKS") + * @param keyStoreType The type of the key store (default "JKS") */ public void setKeyStoreType(String keyStoreType) { - checkNotStarted(); _keyStoreType = keyStoreType; } @@ -607,22 +554,19 @@ public class SslContextFactory extends AbstractLifeCycle * to specify the certificate that should be used, or with SNI * certificates to set a certificate to try if no others match *

    - * @param certAlias - * Alias of SSL certificate for the connector + * + * @param certAlias Alias of SSL certificate for the connector */ public void setCertAlias(String certAlias) { - checkNotStarted(); _certAlias = certAlias; } /** - * @param trustStorePath - * The file name or URL of the trust store location + * @param trustStorePath The file name or URL of the trust store location */ public void setTrustStorePath(String trustStorePath) { - checkNotStarted(); try { _trustStoreResource = Resource.newResource(trustStorePath); @@ -642,12 +586,10 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param trustStoreProvider - * The provider of the trust store + * @param trustStoreProvider The provider of the trust store */ public void setTrustStoreProvider(String trustStoreProvider) { - checkNotStarted(); _trustStoreProvider = trustStoreProvider; } @@ -660,12 +602,10 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param trustStoreType - * The type of the trust store (default "JKS") + * @param trustStoreType The type of the trust store (default "JKS") */ public void setTrustStoreType(String trustStoreType) { - checkNotStarted(); _trustStoreType = trustStoreType; } @@ -679,13 +619,11 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param needClientAuth - * True if SSL needs client authentication. + * @param needClientAuth True if SSL needs client authentication. * @see SSLEngine#getNeedClientAuth() */ public void setNeedClientAuth(boolean needClientAuth) { - checkNotStarted(); _needClientAuth = needClientAuth; } @@ -699,13 +637,11 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param wantClientAuth - * True if SSL wants client authentication. + * @param wantClientAuth True if SSL wants client authentication. * @see SSLEngine#getWantClientAuth() */ public void setWantClientAuth(boolean wantClientAuth) { - checkNotStarted(); _wantClientAuth = wantClientAuth; } @@ -718,12 +654,10 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param validateCerts - * true if SSL certificates have to be validated + * @param validateCerts true if SSL certificates have to be validated */ public void setValidateCerts(boolean validateCerts) { - checkNotStarted(); _validateCerts = validateCerts; } @@ -736,79 +670,76 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param validatePeerCerts - * true if SSL certificates of the peer have to be validated + * @param validatePeerCerts true if SSL certificates of the peer have to be validated */ public void setValidatePeerCerts(boolean validatePeerCerts) { - checkNotStarted(); _validatePeerCerts = validatePeerCerts; } /** - * @param password - * The password for the key store. If null is passed and - * a keystore is set, then - * the {@link Password#getPassword(String, String, String)} is used to - * obtain a password either from the {@value #PASSWORD_PROPERTY} - * System property or by prompting for manual entry. + * @param password The password for the key store. If null is passed and + * a keystore is set, then + * the {@link #getPassword(String)} is used to + * obtain a password either from the {@value #PASSWORD_PROPERTY} + * system property or by prompting for manual entry. */ public void setKeyStorePassword(String password) { - checkNotStarted(); - if (password==null) + if (password == null) { - if (_keyStoreResource!=null) - _keyStorePassword= getPassword(PASSWORD_PROPERTY); + if (_keyStoreResource != null) + _keyStorePassword = getPassword(PASSWORD_PROPERTY); else - _keyStorePassword=null; + _keyStorePassword = null; } else + { _keyStorePassword = newPassword(password); + } } /** - * @param password - * The password (if any) for the specific key within the key store. - * If null is passed and the {@value #KEYPASSWORD_PROPERTY} system property is set, - * then the {@link Password#getPassword(String, String, String)} is used to - * obtain a password from the {@value #KEYPASSWORD_PROPERTY} system property. + * @param password The password (if any) for the specific key within the key store. + * If null is passed and the {@value #KEYPASSWORD_PROPERTY} system property is set, + * then the {@link #getPassword(String)} is used to + * obtain a password from the {@value #KEYPASSWORD_PROPERTY} system property. */ public void setKeyManagerPassword(String password) { - checkNotStarted(); - if (password==null) + if (password == null) { - if (System.getProperty(KEYPASSWORD_PROPERTY)!=null) + if (System.getProperty(KEYPASSWORD_PROPERTY) != null) _keyManagerPassword = getPassword(KEYPASSWORD_PROPERTY); else _keyManagerPassword = null; } else + { _keyManagerPassword = newPassword(password); + } } /** - * @param password - * The password for the trust store. If null is passed and a truststore is set - * that is different from the keystore, then - * the {@link Password#getPassword(String, String, String)} is used to - * obtain a password either from the {@value #PASSWORD_PROPERTY} - * System property or by prompting for manual entry. + * @param password The password for the truststore. If null is passed and a truststore is set + * that is different from the keystore, then + * the {@link #getPassword(String)} is used to + * obtain a password either from the {@value #PASSWORD_PROPERTY} + * system property or by prompting for manual entry. */ public void setTrustStorePassword(String password) { - checkNotStarted(); - if (password==null) + if (password == null) { - // Do we need a truststore password? - if (_trustStoreResource!=null && !_trustStoreResource.equals(_keyStoreResource)) + if (_trustStoreResource != null && !_trustStoreResource.equals(_keyStoreResource)) _trustStorePassword = getPassword(PASSWORD_PROPERTY); else _trustStorePassword = null; } else - _trustStorePassword=newPassword(password); + { + _trustStorePassword = newPassword(password); + } } /** @@ -821,13 +752,11 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param provider - * The SSL provider name, which if set is passed to - * {@link SSLContext#getInstance(String, String)} + * @param provider The SSL provider name, which if set is passed to + * {@link SSLContext#getInstance(String, String)} */ public void setProvider(String provider) { - checkNotStarted(); _sslProvider = provider; } @@ -839,25 +768,13 @@ public class SslContextFactory extends AbstractLifeCycle { return _sslProtocol; } - - /** - * Get the password object for the realm - * @param realm the realm - * @return the Password object - */ - protected Password getPassword(String realm) - { - return Password.getPassword(realm, null, null); - } /** - * @param protocol - * The SSL protocol (default "TLS") passed to - * {@link SSLContext#getInstance(String, String)} + * @param protocol The SSL protocol (default "TLS") passed to + * {@link SSLContext#getInstance(String, String)} */ public void setProtocol(String protocol) { - checkNotStarted(); _sslProtocol = protocol; } @@ -872,32 +789,28 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param algorithm - * The algorithm name, which if set is passed to - * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to - * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} + * @param algorithm The algorithm name, which if set is passed to + * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to + * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} */ public void setSecureRandomAlgorithm(String algorithm) { - checkNotStarted(); _secureRandomAlgorithm = algorithm; } /** * @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory} */ - public String getSslKeyManagerFactoryAlgorithm() + public String getKeyManagerFactoryAlgorithm() { - return (_keyManagerFactoryAlgorithm); + return _keyManagerFactoryAlgorithm; } /** - * @param algorithm - * The algorithm name (default "SunX509") used by the {@link KeyManagerFactory} + * @param algorithm The algorithm name (default "SunX509") used by the {@link KeyManagerFactory} */ - public void setSslKeyManagerFactoryAlgorithm(String algorithm) + public void setKeyManagerFactoryAlgorithm(String algorithm) { - checkNotStarted(); _keyManagerFactoryAlgorithm = algorithm; } @@ -906,7 +819,7 @@ public class SslContextFactory extends AbstractLifeCycle */ public String getTrustManagerFactoryAlgorithm() { - return (_trustManagerFactoryAlgorithm); + return _trustManagerFactoryAlgorithm; } /** @@ -923,18 +836,16 @@ public class SslContextFactory extends AbstractLifeCycle public void setTrustAll(boolean trustAll) { _trustAll = trustAll; - if(trustAll) + if (trustAll) setEndpointIdentificationAlgorithm(null); } /** - * @param algorithm - * The algorithm name (default "SunX509") used by the {@link TrustManagerFactory} - * Use the string "TrustAll" to install a trust manager that trusts all. + * @param algorithm The algorithm name (default "SunX509") used by the {@link TrustManagerFactory} + * Use the string "TrustAll" to install a trust manager that trusts all. */ public void setTrustManagerFactoryAlgorithm(String algorithm) { - checkNotStarted(); _trustManagerFactoryAlgorithm = algorithm; } @@ -963,12 +874,10 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param crlPath - * Path to file that contains Certificate Revocation List + * @param crlPath Path to file that contains Certificate Revocation List */ public void setCrlPath(String crlPath) { - checkNotStarted(); _crlPath = crlPath; } @@ -982,13 +891,11 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @param maxCertPathLength - * maximum number of intermediate certificates in - * the certification path (-1 for unlimited) + * @param maxCertPathLength maximum number of intermediate certificates in + * the certification path (-1 for unlimited) */ public void setMaxCertPathLength(int maxCertPathLength) { - checkNotStarted(); _maxCertPathLength = maxCertPathLength; } @@ -997,19 +904,31 @@ public class SslContextFactory extends AbstractLifeCycle */ public SSLContext getSslContext() { - return isStarted()?_factory._context:_setContext; + if (!isStarted()) + return _setContext; + + synchronized (this) + { + return _factory._context; + } } /** - * @param sslContext - * Set a preconfigured SSLContext + * @param sslContext Set a preconfigured SSLContext */ public void setSslContext(SSLContext sslContext) { - checkNotStarted(); _setContext = sslContext; } + /** + * @return the endpoint identification algorithm + */ + public String getEndpointIdentificationAlgorithm() + { + return _endpointIdentificationAlgorithm; + } + /** * When set to "HTTPS" hostname verification will be enabled * @@ -1017,7 +936,7 @@ public class SslContextFactory extends AbstractLifeCycle */ public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) { - this._endpointIdentificationAlgorithm = endpointIdentificationAlgorithm; + _endpointIdentificationAlgorithm = endpointIdentificationAlgorithm; } /** @@ -1029,7 +948,8 @@ public class SslContextFactory extends AbstractLifeCycle */ protected KeyStore loadKeyStore(Resource resource) throws Exception { - return CertificateUtils.getKeyStore(resource, _keyStoreType, _keyStoreProvider,_keyStorePassword==null? null:_keyStorePassword.toString()); + String storePassword = _keyStorePassword == null ? null : _keyStorePassword.toString(); + return CertificateUtils.getKeyStore(resource, getKeyStoreType(), getKeyStoreProvider(), storePassword); } /** @@ -1041,26 +961,25 @@ public class SslContextFactory extends AbstractLifeCycle */ protected KeyStore loadTrustStore(Resource resource) throws Exception { - String type=_trustStoreType; - String provider= _trustStoreProvider; - String passwd=_trustStorePassword==null? null:_trustStorePassword.toString(); - if (resource==null || resource.equals(_keyStoreResource)) + String type = getTrustStoreType(); + String provider = getTrustStoreProvider(); + String passwd = _trustStorePassword == null ? null : _trustStorePassword.toString(); + if (resource == null || resource.equals(_keyStoreResource)) { - resource=_keyStoreResource; - if (type==null) - type=_keyStoreType; - if (provider==null) - provider= _keyStoreProvider; - if (passwd==null) - passwd=_keyStorePassword==null? null:_keyStorePassword.toString(); + resource = _keyStoreResource; + if (type == null) + type = _keyStoreType; + if (provider == null) + provider = _keyStoreProvider; + if (passwd == null) + passwd = _keyStorePassword == null ? null : _keyStorePassword.toString(); } - - return CertificateUtils.getKeyStore(resource,type,provider,passwd); + return CertificateUtils.getKeyStore(resource, type, provider, passwd); } /** * Loads certificate revocation list (CRL) from a file. - * + *

    * Required for integrations to be able to override the mechanism used to * load CRL in order to provide their own implementation. * @@ -1079,18 +998,19 @@ public class SslContextFactory extends AbstractLifeCycle if (keyStore != null) { - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm); - keyManagerFactory.init(keyStore,_keyManagerPassword == null?(_keyStorePassword == null?null:_keyStorePassword.toString().toCharArray()):_keyManagerPassword.toString().toCharArray()); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(getKeyManagerFactoryAlgorithm()); + keyManagerFactory.init(keyStore, _keyManagerPassword == null ? (_keyStorePassword == null ? null : _keyStorePassword.toString().toCharArray()) : _keyManagerPassword.toString().toCharArray()); managers = keyManagerFactory.getKeyManagers(); - if (managers!=null) + if (managers != null) { - if (_certAlias != null) + String alias = getCertAlias(); + if (alias != null) { for (int idx = 0; idx < managers.length; idx++) { if (managers[idx] instanceof X509ExtendedKeyManager) - managers[idx] = new AliasedX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx],_certAlias); + managers[idx] = new AliasedX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx], alias); } } @@ -1099,14 +1019,14 @@ public class SslContextFactory extends AbstractLifeCycle for (int idx = 0; idx < managers.length; idx++) { if (managers[idx] instanceof X509ExtendedKeyManager) - managers[idx]=new SniX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx]); + managers[idx] = new SniX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx]); } } } } if (LOG.isDebugEnabled()) - LOG.debug("managers={} for {}",managers,this); + LOG.debug("managers={} for {}", managers, this); return managers; } @@ -1117,9 +1037,9 @@ public class SslContextFactory extends AbstractLifeCycle if (trustStore != null) { // Revocation checking is only supported for PKIX algorithm - if (_validatePeerCerts && _trustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX")) + if (isValidatePeerCerts() && "PKIX".equalsIgnoreCase(getTrustManagerFactoryAlgorithm())) { - PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector()); + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); // Set maximum certification path length pbParams.setMaxPathLength(_maxCertPathLength); @@ -1129,19 +1049,19 @@ public class SslContextFactory extends AbstractLifeCycle if (crls != null && !crls.isEmpty()) { - pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls))); + pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(crls))); } if (_enableCRLDP) { // Enable Certificate Revocation List Distribution Points (CRLDP) support - System.setProperty("com.sun.security.enableCRLDP","true"); + System.setProperty("com.sun.security.enableCRLDP", "true"); } if (_enableOCSP) { // Enable On-Line Certificate Status Protocol (OCSP) support - Security.setProperty("ocsp.enable","true"); + Security.setProperty("ocsp.enable", "true"); if (_ocspResponderURL != null) { @@ -1171,7 +1091,8 @@ public class SslContextFactory extends AbstractLifeCycle * Select protocols to be used by the connector * based on configured inclusion and exclusion lists * as well as enabled and supported protocols. - * @param enabledProtocols Array of enabled protocols + * + * @param enabledProtocols Array of enabled protocols * @param supportedProtocols Array of supported protocols */ public void selectProtocols(String[] enabledProtocols, String[] supportedProtocols) @@ -1184,10 +1105,10 @@ public class SslContextFactory extends AbstractLifeCycle // Use only the supported included protocols for (String protocol : _includeProtocols) { - if(Arrays.asList(supportedProtocols).contains(protocol)) + if (Arrays.asList(supportedProtocols).contains(protocol)) selected_protocols.add(protocol); else - LOG.info("Protocol {} not supported in {}",protocol,Arrays.asList(supportedProtocols)); + LOG.info("Protocol {} not supported in {}", protocol, Arrays.asList(supportedProtocols)); } } else @@ -1197,16 +1118,17 @@ public class SslContextFactory extends AbstractLifeCycle selected_protocols.removeAll(_excludeProtocols); if (selected_protocols.isEmpty()) - LOG.warn("No selected protocols from {}",Arrays.asList(supportedProtocols)); + LOG.warn("No selected protocols from {}", Arrays.asList(supportedProtocols)); - _selectedProtocols = selected_protocols.toArray(new String[selected_protocols.size()]); + _selectedProtocols = selected_protocols.toArray(new String[0]); } /** * Select cipher suites to be used by the connector * based on configured inclusion and exclusion lists * as well as enabled and supported cipher suite lists. - * @param enabledCipherSuites Array of enabled cipher suites + * + * @param enabledCipherSuites Array of enabled cipher suites * @param supportedCipherSuites Array of supported cipher suites */ protected void selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites) @@ -1222,16 +1144,17 @@ public class SslContextFactory extends AbstractLifeCycle removeExcludedCipherSuites(selected_ciphers); if (selected_ciphers.isEmpty()) - LOG.warn("No supported ciphers from {}",Arrays.asList(supportedCipherSuites)); + LOG.warn("No supported ciphers from {}", Arrays.asList(supportedCipherSuites)); - if (_cipherComparator!=null) + Comparator comparator = getCipherComparator(); + if (comparator != null) { if (LOG.isDebugEnabled()) - LOG.debug("Sorting selected ciphers with {}",_cipherComparator); - Collections.sort(selected_ciphers,_cipherComparator); + LOG.debug("Sorting selected ciphers with {}", comparator); + Collections.sort(selected_ciphers, comparator); } - _selectedCipherSuites=selected_ciphers.toArray(new String[selected_ciphers.size()]); + _selectedCipherSuites = selected_ciphers.toArray(new String[0]); } protected void processIncludeCipherSuites(String[] supportedCipherSuites, List selected_ciphers) @@ -1239,19 +1162,19 @@ public class SslContextFactory extends AbstractLifeCycle for (String cipherSuite : _includeCipherSuites) { Pattern p = Pattern.compile(cipherSuite); - boolean added=false; + boolean added = false; for (String supportedCipherSuite : supportedCipherSuites) { Matcher m = p.matcher(supportedCipherSuite); if (m.matches()) { - added=true; + added = true; selected_ciphers.add(supportedCipherSuite); } } if (!added) - LOG.info("No Cipher matching '{}' is supported",cipherSuite); + LOG.info("No Cipher matching '{}' is supported", cipherSuite); } } @@ -1260,7 +1183,7 @@ public class SslContextFactory extends AbstractLifeCycle for (String excludeCipherSuite : _excludeCipherSuites) { Pattern excludeCipherPattern = Pattern.compile(excludeCipherSuite); - for (Iterator i=selected_ciphers.iterator();i.hasNext();) + for (Iterator i = selected_ciphers.iterator(); i.hasNext(); ) { String selectedCipherSuite = i.next(); Matcher m = excludeCipherPattern.matcher(selectedCipherSuite); @@ -1273,28 +1196,10 @@ public class SslContextFactory extends AbstractLifeCycle /** * Check if the lifecycle has been started and throw runtime exception */ - protected void checkNotStarted() - { - if (isStarted()) - throw new IllegalStateException("Cannot modify configuration when "+getState()); - } - - /** - * Check if the lifecycle has been started and throw runtime exception - */ - protected void checkIsStarted() + private void checkIsStarted() { if (!isStarted()) - throw new IllegalStateException("!STARTED: "+this); - } - - /** - * Check if the lifecycle has been started and throw runtime exception - */ - protected void checkIsRunning() - { - if (!isRunning()) - throw new IllegalStateException("!RUNNING: "+this); + throw new IllegalStateException("!STARTED: " + this); } /** @@ -1305,12 +1210,13 @@ public class SslContextFactory extends AbstractLifeCycle return _enableCRLDP; } - /** Enables CRL Distribution Points Support + /** + * Enables CRL Distribution Points Support + * * @param enableCRLDP true - turn on, false - turns off */ public void setEnableCRLDP(boolean enableCRLDP) { - checkNotStarted(); _enableCRLDP = enableCRLDP; } @@ -1322,12 +1228,13 @@ public class SslContextFactory extends AbstractLifeCycle return _enableOCSP; } - /** Enables On-Line Certificate Status Protocol support + /** + * Enables On-Line Certificate Status Protocol support + * * @param enableOCSP true - turn on, false - turn off */ public void setEnableOCSP(boolean enableOCSP) { - checkNotStarted(); _enableOCSP = enableOCSP; } @@ -1339,50 +1246,66 @@ public class SslContextFactory extends AbstractLifeCycle return _ocspResponderURL; } - /** Set the location of the OCSP Responder. + /** + * Set the location of the OCSP Responder. + * * @param ocspResponderURL location of the OCSP Responder */ public void setOcspResponderURL(String ocspResponderURL) { - checkNotStarted(); _ocspResponderURL = ocspResponderURL; } - /** Set the key store. + /** + * Set the key store. + * * @param keyStore the key store to set */ public void setKeyStore(KeyStore keyStore) { - checkNotStarted(); _setKeyStore = keyStore; } public KeyStore getKeyStore() { - return isStarted()?_factory._keyStore:_setKeyStore; + if (!isStarted()) + return _setKeyStore; + + synchronized (this) + { + return _factory._keyStore; + } } - /** Set the trust store. + /** + * Set the trust store. + * * @param trustStore the trust store to set */ public void setTrustStore(KeyStore trustStore) { - checkNotStarted(); _setTrustStore = trustStore; } public KeyStore getTrustStore() { - return isStarted()?_factory._trustStore:_setTrustStore; + if (!isStarted()) + return _setTrustStore; + + synchronized (this) + { + return _factory._trustStore; + } } - /** Set the key store resource. + /** + * Set the key store resource. + * * @param resource the key store resource to set */ public void setKeyStoreResource(Resource resource) { - checkNotStarted(); - _keyStoreResource=resource; + _keyStoreResource = resource; } public Resource getKeyStoreResource() @@ -1390,13 +1313,14 @@ public class SslContextFactory extends AbstractLifeCycle return _keyStoreResource; } - /** Set the trust store resource. + /** + * Set the trust store resource. + * * @param resource the trust store resource to set */ public void setTrustStoreResource(Resource resource) { - checkNotStarted(); - _trustStoreResource=resource; + _trustStoreResource = resource; } public Resource getTrustStoreResource() @@ -1405,19 +1329,21 @@ public class SslContextFactory extends AbstractLifeCycle } /** - * @return true if SSL Session caching is enabled - */ + * @return true if SSL Session caching is enabled + */ public boolean isSessionCachingEnabled() { return _sessionCachingEnabled; } - /** Set the flag to enable SSL Session caching. + /** + * Set the flag to enable SSL Session caching. * If set to true, then the {@link SSLContext#createSSLEngine(String, int)} method is * used to pass host and port information as a hint for session reuse. Note that * this is only a hint and session may not be reused. Moreover, the hint is typically - * only used on client side implementations and setting this to false does not + * only used on client side implementations and setting this to false does not * stop a server from accepting an offered session ID to reuse. + * * @param enableSessionCaching the value of the flag */ public void setSessionCachingEnabled(boolean enableSessionCaching) @@ -1425,8 +1351,10 @@ public class SslContextFactory extends AbstractLifeCycle _sessionCachingEnabled = enableSessionCaching; } - /** Get SSL session cache size. + /** + * Get SSL session cache size. * Passed directly to {@link SSLSessionContext#setSessionCacheSize(int)} + * * @return SSL session cache size */ public int getSslSessionCacheSize() @@ -1434,18 +1362,22 @@ public class SslContextFactory extends AbstractLifeCycle return _sslSessionCacheSize; } - /** Set SSL session cache size. + /** + * Set SSL session cache size. *

    Set the max cache size to be set on {@link SSLSessionContext#setSessionCacheSize(int)} * when this factory is started.

    + * * @param sslSessionCacheSize SSL session cache size to set. A value of -1 (default) uses - * the JVM default, 0 means unlimited and positive number is a max size. + * the JVM default, 0 means unlimited and positive number is a max size. */ public void setSslSessionCacheSize(int sslSessionCacheSize) { _sslSessionCacheSize = sslSessionCacheSize; } - /** Get SSL session timeout. + /** + * Get SSL session timeout. + * * @return SSL session timeout */ public int getSslSessionTimeout() @@ -1453,19 +1385,33 @@ public class SslContextFactory extends AbstractLifeCycle return _sslSessionTimeout; } - /** Set SSL session timeout. + /** + * Set SSL session timeout. *

    Set the timeout in seconds to be set on {@link SSLSessionContext#setSessionTimeout(int)} * when this factory is started.

    + * * @param sslSessionTimeout SSL session timeout to set in seconds. A value of -1 (default) uses - * the JVM default, 0 means unlimited and positive number is a timeout in seconds. + * the JVM default, 0 means unlimited and positive number is a timeout in seconds. */ public void setSslSessionTimeout(int sslSessionTimeout) { _sslSessionTimeout = sslSessionTimeout; } - + /** - * Create a new Password object + * Returns the password object for the given realm. + * + * @param realm the realm + * @return the Password object + */ + protected Password getPassword(String realm) + { + return Password.getPassword(realm, null, null); + } + + /** + * Creates a new Password object. + * * @param password the password string * @return the new Password object */ @@ -1474,16 +1420,18 @@ public class SslContextFactory extends AbstractLifeCycle return new Password(password); } - public SSLServerSocket newSslServerSocket(String host,int port,int backlog) throws IOException + public SSLServerSocket newSslServerSocket(String host, int port, int backlog) throws IOException { checkIsStarted(); - SSLServerSocketFactory factory = _factory._context.getServerSocketFactory(); + SSLContext context = getSslContext(); + SSLServerSocketFactory factory = context.getServerSocketFactory(); SSLServerSocket socket = - (SSLServerSocket) (host==null ? - factory.createServerSocket(port,backlog): - factory.createServerSocket(port,backlog,InetAddress.getByName(host))); + (SSLServerSocket)(host == null ? + factory.createServerSocket(port, backlog) : + factory.createServerSocket(port, backlog, InetAddress.getByName(host))); socket.setSSLParameters(customize(socket.getSSLParameters())); + return socket; } @@ -1491,9 +1439,11 @@ public class SslContextFactory extends AbstractLifeCycle { checkIsStarted(); - SSLSocketFactory factory = _factory._context.getSocketFactory(); + SSLContext context = getSslContext(); + SSLSocketFactory factory = context.getSocketFactory(); SSLSocket socket = (SSLSocket)factory.createSocket(); socket.setSSLParameters(customize(socket.getSSLParameters())); + return socket; } @@ -1508,9 +1458,12 @@ public class SslContextFactory extends AbstractLifeCycle */ public SSLEngine newSSLEngine() { - checkIsRunning(); - SSLEngine sslEngine=_factory._context.createSSLEngine(); + checkIsStarted(); + + SSLContext context = getSslContext(); + SSLEngine sslEngine = context.createSSLEngine(); customize(sslEngine); + return sslEngine; } @@ -1525,10 +1478,13 @@ public class SslContextFactory extends AbstractLifeCycle public SSLEngine newSSLEngine(String host, int port) { checkIsStarted(); - SSLEngine sslEngine=isSessionCachingEnabled() - ? _factory._context.createSSLEngine(host, port) - : _factory._context.createSSLEngine(); + + SSLContext context = getSslContext(); + SSLEngine sslEngine = isSessionCachingEnabled() ? + context.createSSLEngine(host, port) : + context.createSSLEngine(); customize(sslEngine); + return sslEngine; } @@ -1563,30 +1519,32 @@ public class SslContextFactory extends AbstractLifeCycle /** * Customize an SslEngine instance with the configuration of this factory, * by calling {@link #customize(SSLParameters)} + * * @param sslEngine the SSLEngine to customize */ public void customize(SSLEngine sslEngine) { if (LOG.isDebugEnabled()) - LOG.debug("Customize {}",sslEngine); + LOG.debug("Customize {}", sslEngine); sslEngine.setSSLParameters(customize(sslEngine.getSSLParameters())); } - + /** * Customize an SslParameters instance with the configuration of this factory. + * * @param sslParams The parameters to customize * @return The passed instance of sslParams (returned as a convenience) */ public SSLParameters customize(SSLParameters sslParams) { - sslParams.setEndpointIdentificationAlgorithm(_endpointIdentificationAlgorithm); - sslParams.setUseCipherSuitesOrder(_useCipherSuitesOrder); + sslParams.setEndpointIdentificationAlgorithm(getEndpointIdentificationAlgorithm()); + sslParams.setUseCipherSuitesOrder(isUseCipherSuitesOrder()); if (!_certHosts.isEmpty() || !_certWilds.isEmpty()) sslParams.setSNIMatchers(Collections.singletonList(new AliasSNIMatcher())); - if (_selectedCipherSuites!=null) + if (_selectedCipherSuites != null) sslParams.setCipherSuites(_selectedCipherSuites); - if (_selectedProtocols!=null) + if (_selectedProtocols != null) sslParams.setProtocols(_selectedProtocols); if (getWantClientAuth()) sslParams.setWantClientAuth(true); @@ -1594,24 +1552,34 @@ public class SslContextFactory extends AbstractLifeCycle sslParams.setNeedClientAuth(true); return sslParams; } - + + public void reload(Consumer consumer) throws Exception + { + synchronized (this) + { + consumer.accept(this); + unload(); + load(); + } + } + public static X509Certificate[] getCertChain(SSLSession sslSession) { try { - Certificate[] javaxCerts=sslSession.getPeerCertificates(); - if (javaxCerts==null||javaxCerts.length==0) + Certificate[] javaxCerts = sslSession.getPeerCertificates(); + if (javaxCerts == null || javaxCerts.length == 0) return null; - int length=javaxCerts.length; - X509Certificate[] javaCerts=new X509Certificate[length]; + int length = javaxCerts.length; + X509Certificate[] javaCerts = new X509Certificate[length]; - java.security.cert.CertificateFactory cf=java.security.cert.CertificateFactory.getInstance("X.509"); - for (int i=0; i * This is based on the information on effective key lengths in RFC 2246 - The TLS Protocol * Version 1.0, Appendix C. CipherSuite definitions: - * + *

    *

          *                         Effective
          *     Cipher       Type    Key Bits
    @@ -1690,25 +1657,19 @@ public class SslContextFactory extends AbstractLifeCycle
                     _trustStoreResource);
         }
     
    -    protected class Factory
    +    class Factory
         {
    -        final KeyStore _keyStore;
    -        final KeyStore _trustStore;
    -        final SSLContext _context;
    +        private final KeyStore _keyStore;
    +        private final KeyStore _trustStore;
    +        private final SSLContext _context;
     
    -        public Factory(KeyStore keyStore, KeyStore trustStore, SSLContext context)
    +        Factory(KeyStore keyStore, KeyStore trustStore, SSLContext context)
             {
                 super();
                 _keyStore = keyStore;
                 _trustStore = trustStore;
                 _context = context;
             }
    -
    -        @Override
    -        public String toString()
    -        {
    -            return String.format("SslFactory@%x{%s}",System.identityHashCode(this),SslContextFactory.this);
    -        }
         }
     
         class AliasSNIMatcher extends SNIMatcher
    @@ -1725,35 +1686,35 @@ public class SslContextFactory extends AbstractLifeCycle
             public boolean matches(SNIServerName serverName)
             {
                 if (LOG.isDebugEnabled())
    -                LOG.debug("SNI matching for {}",serverName);
    +                LOG.debug("SNI matching for {}", serverName);
     
                 if (serverName instanceof SNIHostName)
                 {
                     String host = _host = ((SNIHostName)serverName).getAsciiName();
    -                host=StringUtil.asciiToLowerCase(host);
    +                host = StringUtil.asciiToLowerCase(host);
     
                     // Try an exact match
                     _x509 = _certHosts.get(host);
     
                     // Else try an exact wild match
    -                if (_x509==null)
    +                if (_x509 == null)
                     {
                         _x509 = _certWilds.get(host);
     
                         // Else try an 1 deep wild match
    -                    if (_x509==null)
    +                    if (_x509 == null)
                         {
    -                        int dot=host.indexOf('.');
    -                        if (dot>=0)
    +                        int dot = host.indexOf('.');
    +                        if (dot >= 0)
                             {
    -                            String domain=host.substring(dot+1);
    +                            String domain = host.substring(dot + 1);
                                 _x509 = _certWilds.get(domain);
                             }
                         }
                     }
     
                     if (LOG.isDebugEnabled())
    -                    LOG.debug("SNI matched {}->{}",host,_x509);
    +                    LOG.debug("SNI matched {}->{}", host, _x509);
                 }
                 else
                 {
    diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java
    index 5927218ab10..a7864326db0 100644
    --- a/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java
    +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java
    @@ -32,7 +32,7 @@ public class IncludeExcludeTest
             IncludeExclude ie = new IncludeExclude<>();
             
             assertThat("Empty IncludeExclude", ie.size(), is(0));
    -        assertThat("Matches 'foo'",ie.matches("foo"),is(true));
    +        assertThat("Matches 'foo'",ie.test("foo"),is(true));
         }
         
         @Test
    @@ -43,10 +43,10 @@ public class IncludeExcludeTest
             ie.include("bar");
             
             assertThat("IncludeExclude.size", ie.size(), is(2));
    -        assertEquals(false,ie.matches(""));
    -        assertEquals(true,ie.matches("foo"));
    -        assertEquals(true,ie.matches("bar"));
    -        assertEquals(false,ie.matches("foobar"));
    +        assertEquals(false,ie.test(""));
    +        assertEquals(true,ie.test("foo"));
    +        assertEquals(true,ie.test("bar"));
    +        assertEquals(false,ie.test("foobar"));
         }
         
         @Test
    @@ -58,11 +58,11 @@ public class IncludeExcludeTest
             
             assertEquals(2,ie.size());
             
    -        assertEquals(false,ie.matches("foo"));
    -        assertEquals(false,ie.matches("bar"));
    -        assertEquals(true,ie.matches(""));
    -        assertEquals(true,ie.matches("foobar"));
    -        assertEquals(true,ie.matches("wibble"));
    +        assertEquals(false,ie.test("foo"));
    +        assertEquals(false,ie.test("bar"));
    +        assertEquals(true,ie.test(""));
    +        assertEquals(true,ie.test("foobar"));
    +        assertEquals(true,ie.test("wibble"));
         }
         
         @Test
    @@ -76,11 +76,11 @@ public class IncludeExcludeTest
             
             assertEquals(4,ie.size());
             
    -        assertEquals(true,ie.matches("foo"));
    -        assertEquals(false,ie.matches("bar"));
    -        assertEquals(false,ie.matches(""));
    -        assertEquals(false,ie.matches("foobar"));
    -        assertEquals(false,ie.matches("xxx"));
    +        assertEquals(true,ie.test("foo"));
    +        assertEquals(false,ie.test("bar"));
    +        assertEquals(false,ie.test(""));
    +        assertEquals(false,ie.test("foobar"));
    +        assertEquals(false,ie.test("xxx"));
         }
         
         
    @@ -91,7 +91,7 @@ public class IncludeExcludeTest
             IncludeExclude ie = new IncludeExclude<>(RegexSet.class);
             
             assertEquals(0,ie.size());
    -        assertEquals(true,ie.matches("foo"));
    +        assertEquals(true,ie.test("foo"));
         }
         
         @Test
    @@ -102,13 +102,13 @@ public class IncludeExcludeTest
             ie.include("b((ar)|(oo))");
             
             assertEquals(2,ie.size());
    -        assertEquals(false,ie.matches(""));
    -        assertEquals(true,ie.matches("foo"));
    -        assertEquals(true,ie.matches("far"));
    -        assertEquals(true,ie.matches("bar"));
    -        assertEquals(true,ie.matches("boo"));
    -        assertEquals(false,ie.matches("foobar"));
    -        assertEquals(false,ie.matches("xxx"));
    +        assertEquals(false,ie.test(""));
    +        assertEquals(true,ie.test("foo"));
    +        assertEquals(true,ie.test("far"));
    +        assertEquals(true,ie.test("bar"));
    +        assertEquals(true,ie.test("boo"));
    +        assertEquals(false,ie.test("foobar"));
    +        assertEquals(false,ie.test("xxx"));
         }
         
         @Test
    @@ -120,13 +120,13 @@ public class IncludeExcludeTest
             
             assertEquals(2,ie.size());
             
    -        assertEquals(false,ie.matches("foo"));
    -        assertEquals(false,ie.matches("far"));
    -        assertEquals(false,ie.matches("bar"));
    -        assertEquals(false,ie.matches("boo"));
    -        assertEquals(true,ie.matches(""));
    -        assertEquals(true,ie.matches("foobar"));
    -        assertEquals(true,ie.matches("xxx"));
    +        assertEquals(false,ie.test("foo"));
    +        assertEquals(false,ie.test("far"));
    +        assertEquals(false,ie.test("bar"));
    +        assertEquals(false,ie.test("boo"));
    +        assertEquals(true,ie.test(""));
    +        assertEquals(true,ie.test("foobar"));
    +        assertEquals(true,ie.test("xxx"));
         }
         
         @Test
    @@ -139,14 +139,14 @@ public class IncludeExcludeTest
             ie.exclude("b((ar)|(oo))");
             
             assertEquals(4,ie.size());
    -        assertEquals(false,ie.matches("foo"));
    -        assertEquals(false,ie.matches("far"));
    -        assertEquals(false,ie.matches("bar"));
    -        assertEquals(false,ie.matches("boo"));
    -        assertEquals(false,ie.matches(""));
    -        assertEquals(false,ie.matches("xxx"));
    +        assertEquals(false,ie.test("foo"));
    +        assertEquals(false,ie.test("far"));
    +        assertEquals(false,ie.test("bar"));
    +        assertEquals(false,ie.test("boo"));
    +        assertEquals(false,ie.test(""));
    +        assertEquals(false,ie.test("xxx"));
     
    -        assertEquals(true,ie.matches("foobar"));
    -        assertEquals(true,ie.matches("Ant"));
    +        assertEquals(true,ie.test("foobar"));
    +        assertEquals(true,ie.test("Ant"));
         }
     }
    diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java
    index 84ecb626480..78707bc9312 100644
    --- a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java
    +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java
    @@ -19,6 +19,7 @@
     package org.eclipse.jetty.util;
     
     
    +import org.hamcrest.Matchers;
     import org.junit.Assert;
     import org.junit.Test;
     
    @@ -119,4 +120,12 @@ public class TypeUtilTest
             Assert.assertFalse(TypeUtil.isFalse("blargle"));
             Assert.assertFalse(TypeUtil.isFalse(new Object(){@Override public String toString(){return "true";}}));
         }
    +    
    +    @Test
    +    public void testLoadedFrom() throws Exception
    +    {
    +        Assert.assertThat(TypeUtil.getLoadedFrom(String.class).toString(),Matchers.containsString("/rt.jar"));
    +        Assert.assertThat(TypeUtil.getLoadedFrom(Assert.class).toString(),Matchers.containsString(".jar"));
    +        Assert.assertThat(TypeUtil.getLoadedFrom(TypeUtil.class).toString(),Matchers.containsString("/classes/"));
    +    }
     }
    diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
    index 21909584974..d0e1f66f66d 100644
    --- a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
    +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
    @@ -18,12 +18,17 @@
     
     package org.eclipse.jetty.util;
     
    +import static org.hamcrest.Matchers.is;
     import static org.junit.Assert.assertEquals;
     import static org.junit.Assert.assertFalse;
    +import static org.junit.Assert.assertThat;
     import static org.junit.Assert.assertTrue;
     
    +import java.net.URI;
     import java.nio.charset.StandardCharsets;
     
    +import org.hamcrest.Matchers;
    +import org.junit.Assert;
     import org.junit.Test;
     
     
    @@ -286,4 +291,15 @@ public class URIUtilTest
         }
     
     
    +    /* ------------------------------------------------------------ */
    +    @Test
    +    public void testJarSource() throws Exception
    +    {
    +        assertThat(URIUtil.getJarSource("file:///tmp/"),is("file:///tmp/"));
    +        assertThat(URIUtil.getJarSource("jar:file:///tmp/foo.jar"),is("file:///tmp/foo.jar"));
    +        assertThat(URIUtil.getJarSource("jar:file:///tmp/foo.jar!/some/path"),is("file:///tmp/foo.jar"));
    +        assertThat(URIUtil.getJarSource(new URI("file:///tmp/")),is(new URI("file:///tmp/")));
    +        assertThat(URIUtil.getJarSource(new URI("jar:file:///tmp/foo.jar")),is(new URI("file:///tmp/foo.jar")));
    +        assertThat(URIUtil.getJarSource(new URI("jar:file:///tmp/foo.jar!/some/path")),is(new URI("file:///tmp/foo.jar")));
    +    }
     }
    diff --git a/jetty-webapp/src/main/config/etc/jetty-webapp.xml b/jetty-webapp/src/main/config/etc/jetty-webapp.xml
    new file mode 100644
    index 00000000000..1f8e737e9bf
    --- /dev/null
    +++ b/jetty-webapp/src/main/config/etc/jetty-webapp.xml
    @@ -0,0 +1,23 @@
    +
    +
    +
    +
    +  
    +    
    +    
    +      
    +        
    +      
    +    
    +  
    +
    +  
    +    
    +    
    +      
    +        
    +      
    +    
    +  
    +
    +
    diff --git a/jetty-webapp/src/main/config/modules/webapp.mod b/jetty-webapp/src/main/config/modules/webapp.mod
    index c753f8d761e..83e2d839d1d 100644
    --- a/jetty-webapp/src/main/config/modules/webapp.mod
    +++ b/jetty-webapp/src/main/config/modules/webapp.mod
    @@ -6,5 +6,23 @@ classpath.  Without this, only Jetty specific handlers may be deployed.
     servlet
     security
     
    +[xml]
    +etc/jetty-webapp.xml
    +
     [lib]
     lib/jetty-webapp-${jetty.version}.jar
    +
    +
    +[ini-template]
    +## Add to the server wide default jars and packages protected or hidden from webapps.
    +## System classes are protected and cannot be overridden by a webapp.
    +## Server classes are hidden and cannot be seen by a webapp
    +## Lists of patterns are comma separated and may be either:
    +##  + a qualified classname e.g. 'com.acme.Foo' 
    +##  + a package name e.g. 'net.example.'
    +##  + a jar file e.g. 'file:${jetty.base}/lib/dependency.jar' 
    +##  + a directory of jars,resource or classes e.g. 'file:${jetty.base}/resources' 
    +##  + A pattern preceeded with a '-' is an exclusion, all other patterns are inclusions
    +##
    +jetty.webapp.addSystemClasses,=
    +jetty.webapp.addServerClasses,=
    diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java
    index a1f564e8e4d..32ed9526aad 100644
    --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java
    +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java
    @@ -19,13 +19,30 @@
     
     package org.eclipse.jetty.webapp;
     
    -import java.util.AbstractList;
    +import static java.lang.Boolean.FALSE;
    +import static java.lang.Boolean.TRUE;
    +
    +import java.io.File;
    +import java.net.URL;
    +import java.nio.file.Path;
    +import java.util.AbstractSet;
     import java.util.ArrayList;
     import java.util.Arrays;
    +import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.Iterator;
     import java.util.List;
    -import java.util.ListIterator;
    +import java.util.Map;
    +import java.util.Set;
    +import java.util.function.Predicate;
     
    -import org.eclipse.jetty.util.StringUtil;
    +import org.eclipse.jetty.util.ArrayTernaryTrie;
    +import org.eclipse.jetty.util.IncludeExcludeSet;
    +import org.eclipse.jetty.util.TypeUtil;
    +import org.eclipse.jetty.util.URIUtil;
    +import org.eclipse.jetty.util.log.Log;
    +import org.eclipse.jetty.util.log.Logger;
    +import org.eclipse.jetty.util.resource.Resource;
     
     /* ------------------------------------------------------------ */
     /**
    @@ -45,21 +62,59 @@ import org.eclipse.jetty.util.StringUtil;
      * in this string should be separated by ':' (semicolon) or ',' (comma).
      */
     
    -public class ClasspathPattern extends AbstractList
    +public class ClasspathPattern extends AbstractSet
     {
    +    private static final Logger LOG = Log.getLogger(ClasspathPattern.class);
    +    
    +    enum Type { PACKAGE, CLASSNAME, LOCATION }
    +
         private static class Entry
         {
    -        public final String _pattern;
    -        public final String _name;
    -        public final boolean _inclusive;
    -        public final boolean _package;     
    +        private final String _pattern;
    +        private final String _name;
    +        private final boolean _inclusive;
    +        private final Type _type;
             
             Entry(String pattern)
             {
                 _pattern=pattern;
                 _inclusive = !pattern.startsWith("-");
    -            _package = pattern.endsWith(".");
                 _name = _inclusive ? pattern : pattern.substring(1).trim();
    +            _type = (_name.startsWith("file:"))?Type.LOCATION:(_name.endsWith(".")?Type.PACKAGE:Type.CLASSNAME);
    +        }
    +        
    +        Entry(String name, boolean include)
    +        {
    +            _pattern=include?name:("-"+name);
    +            _inclusive = include;
    +            _name = name;
    +            _type = (_name.startsWith("file:"))?Type.LOCATION:(_name.endsWith(".")?Type.PACKAGE:Type.CLASSNAME);
    +        }
    +        
    +
    +        public String getPattern()
    +        {
    +            return _pattern;
    +        }
    +        
    +        public boolean isPackage()
    +        {
    +            return _type==Type.PACKAGE;
    +        }
    +        
    +        public boolean isClassName()
    +        {
    +            return _type==Type.CLASSNAME;
    +        }
    +        
    +        public boolean isLocation()
    +        {
    +            return _type==Type.LOCATION;
    +        }
    +
    +        public String getName()
    +        {
    +            return _name;
             }
             
             @Override
    @@ -67,86 +122,356 @@ public class ClasspathPattern extends AbstractList
             {
                 return _pattern;
             }
    +        
    +        @Override 
    +        public int hashCode()
    +        {
    +            return _pattern.hashCode();
    +        }
    +        
    +        @Override 
    +        public boolean equals(Object o)
    +        {
    +            return (o instanceof Entry) 
    +                && _pattern.equals(((Entry)o)._pattern);
    +        }
    +
    +        public boolean isInclusive()
    +        {
    +            return _inclusive;
    +        }
         }
         
    -    final private List _entries = new ArrayList();
         
    -    /* ------------------------------------------------------------ */
    +    public static class ByPackage extends AbstractSet implements Predicate 
    +    {
    +        private final ArrayTernaryTrie.Growing _entries = new ArrayTernaryTrie.Growing<>(false,512,512);
    +
    +        @Override
    +        public boolean test(String name)
    +        {
    +            return _entries.getBest(name)!=null;
    +        }
    +
    +        @Override
    +        public Iterator iterator()
    +        {
    +            return _entries.keySet().stream().map(k->_entries.get(k)).iterator();
    +        }
    +
    +        @Override
    +        public int size()
    +        {
    +            return _entries.size();
    +        }
    +        
    +        @Override
    +        public boolean isEmpty()
    +        {
    +            return _entries.isEmpty();
    +        }
    +        
    +        @Override
    +        public boolean add(Entry entry)
    +        {
    +            String name = entry.getName();
    +            if (entry.isClassName())
    +                name+="$";
    +            else if (entry.isLocation())
    +                throw new IllegalArgumentException(entry.toString());
    +            else if (".".equals(name))
    +                name="";
    +                
    +            if (_entries.get(name)!=null)
    +                return false;
    +            
    +            _entries.put(name,entry);
    +            return true;
    +        }
    +        
    +        @Override
    +        public boolean remove(Object entry)
    +        {
    +            if (!(entry instanceof Entry))
    +                return false;
    +
    +            return _entries.remove(((Entry)entry).getName())!=null;
    +        }
    +        
    +        @Override
    +        public void clear()
    +        {
    +            _entries.clear();
    +        }
    +    }
    +    
    +    @SuppressWarnings("serial")
    +    public static class ByName extends HashSet implements Predicate 
    +    {
    +        private final Map _entries = new HashMap<>();
    +
    +        @Override
    +        public boolean test(String name)
    +        {
    +            return _entries.containsKey(name);
    +        }
    +
    +        @Override
    +        public Iterator iterator()
    +        {
    +            return _entries.values().iterator();
    +        }
    +
    +        @Override
    +        public int size()
    +        {
    +            return _entries.size();
    +        }
    +        
    +        @Override
    +        public boolean add(Entry entry)
    +        {
    +            if (!entry.isClassName())
    +                throw new IllegalArgumentException(entry.toString());
    +            return _entries.put(entry.getName(),entry)==null;
    +        }
    +        
    +        @Override
    +        public boolean remove(Object entry)
    +        {
    +            if (!(entry instanceof Entry))
    +                return false;
    +
    +            return _entries.remove(((Entry)entry).getName())!=null;
    +        }
    +    }
    +
    +    public static class ByPackageOrName extends AbstractSet implements Predicate 
    +    {
    +        private final ByName _byName = new ByName();
    +        private final ByPackage _byPackage = new ByPackage();
    +        
    +        @Override
    +        public boolean test(String name)
    +        {
    +            return  _byPackage.test(name) 
    +                || _byName.test(name) ;
    +        }
    +
    +        @Override
    +        public Iterator iterator()
    +        {
    +            // by package contains all entries (classes are also $ packages).
    +            return _byPackage.iterator();
    +        }
    +
    +        @Override
    +        public int size()
    +        {
    +            return _byPackage.size();
    +        }
    +
    +        @Override
    +        public boolean add(Entry e)
    +        {
    +            if (e.isLocation())
    +                throw new IllegalArgumentException();
    +            
    +            if (e.isPackage())
    +                return _byPackage.add(e);
    +            
    +            // Add class name to packages also as classes act
    +            // as packages for nested classes.
    +            boolean added = _byPackage.add(e);
    +            added = _byName.add(e) || added;
    +            return added;
    +        }
    +
    +        @Override
    +        public boolean remove(Object o)
    +        {
    +            if (!(o instanceof Entry))
    +                return false;
    +
    +            boolean removed = _byPackage.remove(o);
    +            
    +            if (!((Entry)o).isPackage())
    +                removed = _byName.remove(o) || removed;
    +            
    +            return removed;
    +        }
    +
    +        @Override
    +        public void clear()
    +        {
    +            _byPackage.clear();
    +            _byName.clear();
    +        }
    +    }
    +    
    +    @SuppressWarnings("serial")
    +    public static class ByLocation extends HashSet implements Predicate
    +    {        
    +        @Override
    +        public boolean test(Path path)
    +        {
    +            for (File file: this)
    +            {
    +                if (file.isDirectory())
    +                {
    +                    if (path.startsWith(file.toPath()))
    +                        return true;
    +                }
    +                else
    +                {
    +                    if (path.equals(file.toPath()))
    +                        return true;
    +                }
    +            }
    +                
    +            return false;
    +        }
    +    }
    +    
    +    
    +    Map _entries = new HashMap<>();
    +    Set _classes = new HashSet<>();
    +    
    +    IncludeExcludeSet _patterns = new IncludeExcludeSet<>(ByPackageOrName.class);
    +    IncludeExcludeSet _locations = new IncludeExcludeSet<>(ByLocation.class);
    +    
         public ClasspathPattern()
         {
         }
         
    -    /* ------------------------------------------------------------ */
         public ClasspathPattern(String[] patterns)
         {
             setAll(patterns);
         }
         
    -    /* ------------------------------------------------------------ */
         public ClasspathPattern(String pattern)
         {
             add(pattern);
         }
         
    -    /* ------------------------------------------------------------ */
    -    @Override
    -    public String get(int index)
    +    public boolean include(String name)
         {
    -        return _entries.get(index)._pattern;
    -    }
    -
    -    /* ------------------------------------------------------------ */
    -    @Override
    -    public String set(int index, String element)
    -    {
    -        Entry e = _entries.set(index,new Entry(element));
    -        return e==null?null:e._pattern;
    -    }
    -
    -    /* ------------------------------------------------------------ */
    -    @Override
    -    public void add(int index, String element)
    -    {
    -        _entries.add(index,new Entry(element));
    -    }
    -
    -    /* ------------------------------------------------------------ */
    -    @Deprecated
    -    public void addPattern(String element)
    -    {
    -        add(element);
    +        if (name==null)
    +            return false;
    +        return add(new Entry(name,true));
         }
         
    -    /* ------------------------------------------------------------ */
    -    @Override
    -    public String remove(int index)
    +    public boolean include(String... name)
         {
    -        Entry e = _entries.remove(index);
    -        return e==null?null:e._pattern;
    +        boolean added = false;
    +        for (String n:name)
    +            if (n!=null)
    +                added = add(new Entry(n,true)) || added;
    +        return added;
         }
         
    -    /* ------------------------------------------------------------ */
    -    public boolean remove(String pattern)
    +    public boolean exclude(String name)
         {
    -        for (int i=_entries.size();i-->0;)
    +        if (name==null)
    +            return false;
    +        return add(new Entry(name,false));
    +    }
    +    
    +    public boolean exclude(String... name)
    +    {
    +        boolean added = false;
    +        for (String n:name)
    +            if (n!=null)
    +                added = add(new Entry(n,false)) || added;
    +        return added;
    +    }
    +    
    +    @Override
    +    public boolean add(String pattern)
    +    {
    +        if (pattern==null)
    +            return false;
    +        return add(new Entry(pattern));
    +    }
    +    
    +    public boolean add(String... pattern)
    +    {
    +        boolean added = false;
    +        for (String p:pattern)
    +            if (p!=null)
    +                added = add(new Entry(p)) || added;
    +        return added;
    +    }
    +    
    +    protected boolean add(Entry entry)
    +    {
    +        if (_entries.containsKey(entry.getPattern()))
    +            return false;
    +        _entries.put(entry.getPattern(),entry);
    +
    +        if (entry.isLocation())
             {
    -            if (pattern.equals(_entries.get(i)._pattern))
    +            try
                 {
    -                _entries.remove(i);
    -                return true;
    +                File file = Resource.newResource(entry.getName()).getFile().getAbsoluteFile().getCanonicalFile();
    +                if (entry.isInclusive())
    +                    _locations.include(file);
    +                else
    +                    _locations.exclude(file);
    +            }
    +            catch (Exception e)
    +            {
    +                throw new IllegalArgumentException(e);
                 }
             }
    -        return false;
    +        else
    +        {
    +            if (entry.isInclusive())
    +                _patterns.include(entry);
    +            else
    +                _patterns.exclude(entry);
    +        }
    +        return true;
    +    }
    +
    +    @Override
    +    public boolean remove(Object o)
    +    {
    +        if (!(o instanceof String))
    +            return false;
    +        String pattern = (String)o;
    +
    +        Entry entry = _entries.remove(pattern);
    +        if (entry==null)
    +            return false;
    +
    +        List saved = new ArrayList<>(_entries.values());
    +        clear();
    +        for (Entry e:saved)
    +            add(e);
    +        return true;
    +    }
    +
    +    @Override
    +    public void clear()
    +    {
    +        _entries.clear();
    +        _patterns.clear();
    +        _locations.clear();
    +    }
    +
    +    @Override
    +    public Iterator iterator()
    +    {
    +        return _entries.keySet().iterator();
         }
     
    -    /* ------------------------------------------------------------ */
         @Override
         public int size()
         {
             return _entries.size();
         }
     
    -    /* ------------------------------------------------------------ */
         /**
          * Initialize the matcher by parsing each classpath pattern in an array
          * 
    @@ -158,7 +483,6 @@ public class ClasspathPattern extends AbstractList
             addAll(classes);
         }
         
    -    /* ------------------------------------------------------------ */
         /**
          * @param classes array of classpath patterns
          */
    @@ -168,30 +492,6 @@ public class ClasspathPattern extends AbstractList
                 addAll(Arrays.asList(classes));
         }
         
    -    /* ------------------------------------------------------------ */
    -    /**
    -     * @param classes array of classpath patterns
    -     */
    -    public void prepend(String[] classes)
    -    {
    -        if (classes != null)
    -        {
    -            int i=0;
    -            for (String c : classes)
    -            {
    -                add(i,c);
    -                i++;
    -            }
    -        }
    -    }
    -
    -    /* ------------------------------------------------------------ */
    -    public void prependPattern(String pattern)
    -    {
    -        add(0,pattern);
    -    }
    -    
    -    /* ------------------------------------------------------------ */
         /**
          * @return array of classpath patterns
          */
    @@ -200,22 +500,7 @@ public class ClasspathPattern extends AbstractList
             return toArray(new String[_entries.size()]);
         }
     
    -    /* ------------------------------------------------------------ */
    -    /**
    -     * @return List of classes excluded class exclusions and package patterns
    -     */
    -    public List getClasses()
    -    {
    -        List list = new ArrayList<>();
    -        for (Entry e:_entries)
    -        {
    -            if (e._inclusive && !e._package)
    -                list.add(e._name);
    -        }
    -        return list;
    -    }
         
    -    /* ------------------------------------------------------------ */
         /**
          * Match the class name against the pattern
          *
    @@ -224,65 +509,66 @@ public class ClasspathPattern extends AbstractList
          */
         public boolean match(String name)
         {       
    -        name = name.replace('/','.');
    -
    -        for (Entry entry : _entries)
    +        return _patterns.test(name);
    +    }
    +    
    +    /**
    +     * Match the class name against the pattern
    +     *
    +     * @param clazz A class to try to match
    +     * @return true if class matches the pattern
    +     */
    +    public boolean match(Class clazz)
    +    {       
    +        try
             {
    -            if (entry==null)
    -                continue;
    -            if (entry._package)
    -            {
    -                if (name.startsWith(entry._name) || ".".equals(entry._pattern))
    -                    return entry._inclusive;
    -            }
    -            else
    -            {
    -                if (name.equals(entry._name))
    -                    return entry._inclusive;
    -                
    -                if (name.length()>entry._name.length() && '$'==name.charAt(entry._name.length()) && name.startsWith(entry._name))
    -                    return entry._inclusive;
    -            }
    +            Resource resource = TypeUtil.getLoadedFrom(clazz);
    +            Path path = resource.getFile().toPath();
    +            
    +            Boolean byName = _patterns.isIncludedAndNotExcluded(clazz.getName());
    +            Boolean byLocation = _locations.isIncludedAndNotExcluded(path);
    +            
    +            // Combine the tri-state match of both IncludeExclude Sets
    +            boolean included = byName==TRUE || byLocation==TRUE
    +                || (byName==null && !_patterns.hasIncludes() && byLocation==null && !_locations.hasIncludes());
    +            boolean excluded = byName==FALSE || byLocation==FALSE;
    +            return included && !excluded;
    +        }
    +        catch (Exception e)
    +        {
    +            LOG.warn(e);
             }
             return false;
         }
     
    -    public void addAfter(String afterPattern,String... patterns)
    +    public boolean match(String name, URL url)
         {
    -        if (patterns!=null && afterPattern!=null)
    -        {
    -            ListIterator iter = listIterator();
    -            while (iter.hasNext())
    -            {
    -                String cc=iter.next();
    -                if (afterPattern.equals(cc))
    -                {
    -                    for (int i=0;i iter = listIterator();
    -            while (iter.hasNext())
    -            {
    -                String cc=iter.next();
    -                if (beforePattern.equals(cc))
    -                {
    -                    iter.previous();
    -                    for (int i=0;i clazz);
     
             /* ------------------------------------------------------------ */
             /** Is the class a Server Class.
    @@ -122,7 +121,7 @@ public class WebAppClassLoader extends URLClassLoader
              * @param clazz The fully qualified name of the class.
              * @return True if the class is a server class.
              */
    -        boolean isServerClass(String clazz);
    +        boolean isServerClass(Class clazz);
     
             /* ------------------------------------------------------------ */
             /**
    @@ -135,6 +134,10 @@ public class WebAppClassLoader extends URLClassLoader
             
             /* ------------------------------------------------------------ */
             String getExtraClasspath();
    +
    +        boolean isServerResource(String name, URL parent_url);
    +
    +        boolean isSystemResource(String name, URL webapp_url);
             
         }
     
    @@ -142,9 +145,10 @@ public class WebAppClassLoader extends URLClassLoader
         /** Run an action with access to ServerClasses
          * 

    Run the passed {@link PrivilegedExceptionAction} with the classloader * configured so as to allow server classes to be visible

    + * @param The type returned by the action * @param action The action to run * @return The return from the action - * @throws Exception + * @throws Exception if thrown by the action */ public static T runWithServerClassAccess(PrivilegedExceptionAction action) throws Exception { @@ -361,27 +365,42 @@ public class WebAppClassLoader extends URLClassLoader @Override public Enumeration getResources(String name) throws IOException { - boolean system_class=_context.isSystemClass(name); - boolean server_class=_context.isServerClass(name) && !Boolean.TRUE.equals(__loadServerClasses.get()); + List from_parent = new ArrayList<>(); + List from_webapp = new ArrayList<>(); + + Enumeration urls = _parent.getResources(name); + while (urls!=null && urls.hasMoreElements()) + { + URL url = urls.nextElement(); + if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerResource(name,url)) + from_parent.add(url); + } + + urls = this.findResources(name); + while (urls!=null && urls.hasMoreElements()) + { + URL url = urls.nextElement(); + if (!_context.isSystemResource(name,url) || from_parent.isEmpty()) + from_webapp.add(url); + } + + List resources; - List from_parent = toList(server_class?null:_parent.getResources(name)); - List from_webapp = toList((system_class&&!from_parent.isEmpty())?null:this.findResources(name)); - if (_context.isParentLoaderPriority()) { from_parent.addAll(from_webapp); - return Collections.enumeration(from_parent); + resources = from_parent; } - from_webapp.addAll(from_parent); - return Collections.enumeration(from_webapp); - } + else + { + from_webapp.addAll(from_parent); + resources = from_webapp; + } + + if (LOG.isDebugEnabled()) + LOG.debug("getResources {} {}",name,resources); - /* ------------------------------------------------------------ */ - private List toList(Enumeration e) - { - if (e==null) - return new ArrayList(); - return Collections.list(e); + return Collections.enumeration(resources); } /* ------------------------------------------------------------ */ @@ -395,59 +414,60 @@ public class WebAppClassLoader extends URLClassLoader @Override public URL getResource(String name) { - URL url= null; - boolean tried_parent= false; - - //If the resource is a class name with .class suffix, strip it off before comparison - //as the server and system patterns are specified without a .class suffix - String tmp = name; - if (tmp != null && tmp.endsWith(".class")) - tmp = tmp.substring(0, tmp.length()-6); - - boolean system_class=_context.isSystemClass(tmp); - boolean server_class=_context.isServerClass(tmp) && !Boolean.TRUE.equals(__loadServerClasses.get()); - - if (LOG.isDebugEnabled()) - LOG.debug("getResource({}) system={} server={} cl={}",name,system_class,server_class,this); - - if (system_class && server_class) - return null; - - ClassLoader source=null; - - if (_parent!=null &&(_context.isParentLoaderPriority() || system_class ) && !server_class) + URL resource=null; + if (_context.isParentLoaderPriority()) { - tried_parent= true; + URL parent_url=_parent.getResource(name); - if (_parent!=null) + // return if we have a url the webapp is allowed to see + if (parent_url!=null + && (Boolean.TRUE.equals(__loadServerClasses.get()) + || !_context.isServerResource(name,parent_url))) + resource = parent_url; + else { - source=_parent; - url=_parent.getResource(name); + URL webapp_url = this.findResource(name); + + // If found here then OK to use regardless of system or server classes + // If it is a system resource, we've already tried to load from parent, so + // would have returned it. + // If it is a server resource, doesn't matter as we have loaded it from the + // webapp + if (webapp_url!=null) + resource = webapp_url; } } - - if (url == null) + else { - url= this.findResource(name); - source=this; - if (url == null && name.startsWith("/")) - url= this.findResource(name.substring(1)); - } + URL webapp_url = this.findResource(name); - if (url == null && !tried_parent && !server_class ) - { - if (_parent!=null) + if (webapp_url!=null && !_context.isSystemResource(name,webapp_url)) + resource = webapp_url; + else { - tried_parent=true; - source=_parent; - url= _parent.getResource(name); + + // Couldn't find or see a webapp resource, so try a parent + URL parent_url=_parent.getResource(name); + if (parent_url!=null + && (Boolean.TRUE.equals(__loadServerClasses.get()) + || !_context.isServerResource(name,parent_url))) + resource = parent_url; + // We couldn't find a parent resource, so OK to return a webapp one if it exists + // and we just couldn't see it before + else if (webapp_url!=null) + resource = webapp_url; } } + + // Perhaps this failed due to leading / + if (resource==null && name.startsWith("/")) + resource = getResource(name.substring(1)); if (LOG.isDebugEnabled()) - LOG.debug("gotResource({})=={} from={} tried_parent={}",name,url,source,tried_parent); - - return url; + LOG.debug("getResource {} {}",name,resource); + + return resource; + } /* ------------------------------------------------------------ */ @@ -455,78 +475,116 @@ public class WebAppClassLoader extends URLClassLoader protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) - { - Class c= findLoadedClass(name); + { ClassNotFoundException ex= null; - boolean tried_parent= false; - - boolean system_class=_context.isSystemClass(name); - boolean server_class=_context.isServerClass(name) && !Boolean.TRUE.equals(__loadServerClasses.get()); - - if (LOG.isDebugEnabled()) - LOG.debug("loadClass({}) system={} server={} cl={}",name,system_class,server_class,this); + Class parent_class = null; + Class webapp_class = null; - ClassLoader source=null; - - if (system_class && server_class) - { - return null; - } - - if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class) - { - tried_parent= true; - source=_parent; - try - { - c= _parent.loadClass(name); - if (LOG.isDebugEnabled()) - LOG.debug("loaded " + c); - } - catch (ClassNotFoundException e) - { - ex= e; - } - } - - if (c == null) - { - try - { - source=this; - c= this.findClass(name); - } - catch (ClassNotFoundException e) - { - ex= e; - } - } - - if (c == null && _parent!=null && !tried_parent && !server_class ) - { - tried_parent=true; - source=_parent; - c= _parent.loadClass(name); - } - - if (c == null && ex!=null) + // Has this loader loaded the class already? + webapp_class = findLoadedClass(name); + if (webapp_class != null) { if (LOG.isDebugEnabled()) - LOG.debug("!loadedClass({}) from={} tried_parent={}",name,this,tried_parent); + LOG.debug("found webapp loaded {}",webapp_class); + return webapp_class; + } + + // Should we try the parent loader first? + if (_context.isParentLoaderPriority()) + { + // Try the parent loader + try + { + parent_class = _parent.loadClass(name); + + // If the webapp is allowed to see this class + if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerClass(parent_class)) + { + if (LOG.isDebugEnabled()) + LOG.debug("PLP parent loaded {}",parent_class); + return parent_class; + } + } + catch (ClassNotFoundException e) + { + // Save it for later + ex = e; + } + + // Try the webapp loader + try + { + // If found here then OK to use regardless of system or server classes + // If it is a system class, we've already tried to load from parent, so + // would have returned it. + // If it is a server class, doesn't matter as we have loaded it from the + // webapp + webapp_class = this.findClass(name); + resolveClass(webapp_class); + if (LOG.isDebugEnabled()) + LOG.debug("PLP webapp loaded {}",webapp_class); + return webapp_class; + } + catch (ClassNotFoundException e) + { + if (ex==null) + ex = e; + else + ex.addSuppressed(e); + } + throw ex; } - - if (LOG.isDebugEnabled()) - LOG.debug("loadedClass({})=={} from={} tried_parent={}",name,c,source,tried_parent); - - if (resolve) + else { - resolveClass(c); - if (LOG.isDebugEnabled()) - LOG.debug("resolved({})=={} from={} tried_parent={}",name,c,source,tried_parent); - } + // Not parent loader priority, so... - return c; + // Try the webapp classloader first + // Look in the webapp classloader as a resource, to avoid + // loading a system class. + String path = name.replace('.', '/').concat(".class"); + URL webapp_url = findResource(path); + + if (webapp_url!=null && !_context.isSystemResource(name,webapp_url)) + { + webapp_class = this.foundClass(name,webapp_url); + resolveClass(webapp_class); + if (LOG.isDebugEnabled()) + LOG.debug("WAP webapp loaded {}",webapp_class); + return webapp_class; + } + + // Try the parent loader + try + { + parent_class = _parent.loadClass(name); + + // If the webapp is allowed to see this class + if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerClass(parent_class)) + { + if (LOG.isDebugEnabled()) + LOG.debug("WAP parent loaded {}",parent_class); + return parent_class; + } + } + catch (ClassNotFoundException e) + { + ex=e; + } + + // We couldn't find a parent class, so OK to return a webapp one if it exists + // and we just couldn't see it before + if (webapp_url!=null) + { + webapp_class = this.foundClass(name,webapp_url); + resolveClass(webapp_class); + if (LOG.isDebugEnabled()) + LOG.debug("WAP !server webapp loaded {}",webapp_class); + return webapp_class; + } + + throw ex==null?new ClassNotFoundException(name):ex; + } } } @@ -564,69 +622,71 @@ public class WebAppClassLoader extends URLClassLoader { return _transformers.remove(transformer); } - - + /* ------------------------------------------------------------ */ @Override protected Class findClass(final String name) throws ClassNotFoundException { - Class clazz=null; - if (_transformers.isEmpty()) - clazz = super.findClass(name); - else + return super.findClass(name); + + String path = name.replace('.', '/').concat(".class"); + URL url = findResource(path); + if (url==null) + throw new ClassNotFoundException(name); + return foundClass(name,url); + } + + /* ------------------------------------------------------------ */ + protected Class foundClass(final String name, URL url) throws ClassNotFoundException + { + if (_transformers.isEmpty()) + return super.findClass(name); + + InputStream content=null; + try { - String path = name.replace('.', '/').concat(".class"); - URL url = getResource(path); - if (url==null) - throw new ClassNotFoundException(name); + content = url.openStream(); + byte[] bytes = IO.readBytes(content); - InputStream content=null; - try + if (LOG.isDebugEnabled()) + LOG.debug("foundClass({}) url={} cl={}",name,url,this); + + for (ClassFileTransformer transformer : _transformers) { - content = url.openStream(); - byte[] bytes = IO.readBytes(content); + byte[] tmp = transformer.transform(this,name,null,null,bytes); + if (tmp != null) + bytes = tmp; + } - if (LOG.isDebugEnabled()) - LOG.debug("foundClass({}) url={} cl={}",name,url,this); - - for (ClassFileTransformer transformer : _transformers) + return defineClass(name,bytes,0,bytes.length); + } + catch (IOException e) + { + throw new ClassNotFoundException(name,e); + } + catch (IllegalClassFormatException e) + { + throw new ClassNotFoundException(name,e); + } + finally + { + if (content!=null) + { + try { - byte[] tmp = transformer.transform(this,name,null,null,bytes); - if (tmp != null) - bytes = tmp; + content.close(); } - - clazz=defineClass(name,bytes,0,bytes.length); - } - catch (IOException e) - { - throw new ClassNotFoundException(name,e); - } - catch (IllegalClassFormatException e) - { - throw new ClassNotFoundException(name,e); - } - finally - { - if (content!=null) + catch (IOException e) { - try - { - content.close(); - } - catch (IOException e) - { - throw new ClassNotFoundException(name,e); - } + throw new ClassNotFoundException(name,e); } } } - - return clazz; } + - + /* ------------------------------------------------------------ */ @Override public void close() throws IOException { @@ -639,4 +699,5 @@ public class WebAppClassLoader extends URLClassLoader { return "WebAppClassLoader=" + _name+"@"+Long.toHexString(hashCode()); } + } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index c29098d49c8..9e7447eff85 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -24,7 +24,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.PermissionCollection; -import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -35,7 +34,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; import javax.servlet.ServletContext; import javax.servlet.ServletRegistration.Dynamic; @@ -63,6 +61,7 @@ import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.util.AttributesMap; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.MultiException; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -87,7 +86,7 @@ import org.eclipse.jetty.util.resource.ResourceCollection; @ManagedObject("Web Application ContextHandler") public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context { - private static final Logger LOG = Log.getLogger(WebAppContext.class); + static final Logger LOG = Log.getLogger(WebAppContext.class); public static final String TEMPDIR = "javax.servlet.context.tempdir"; public static final String BASETEMPDIR = "org.eclipse.jetty.webapp.basetempdir"; @@ -131,12 +130,30 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL "org.eclipse.jetty.servlets.PushSessionCacheFilter" //must be loaded by container classpath } ; + // Find the location of the JVM lib directory + public final static String __jvmlib; + static + { + String lib=null; + try + { + lib=TypeUtil.getLoadedFrom(System.class).getFile().getParentFile().toURI().toString(); + } + catch(Exception e) + { + LOG.warn(e); + lib=null; + } + __jvmlib=lib; + } + // Server classes are classes that are hidden from being // loaded by the web application using system classloader, // so if web application needs to load any of such classes, // it has to include them in its distribution. // TODO This centrally managed list of features that are exposed/hidden needs to be replaced // with a more automatic distributed mechanism + // TODO should be white list rather than black list public final static String[] __dftServerClasses = { "-org.eclipse.jetty.server.session.SessionData", //don't hide SessionData for de/serialization purposes @@ -666,21 +683,25 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL } /* ------------------------------------------------------------ */ - /** Add to the list of Server classes. - * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) - * or a qualified package name ending with '.' (eg com.foo.). If the class - * or package has '-' it is excluded from the server classes and order is thus - * important when added system class patterns. This argument may also be a comma - * separated list of classOrPackage patterns. - * @see #setServerClasses(String[]) - * @see Jetty Documentation: Classloading + /** + * @return The ClasspathPattern used to match Server (hidden) classes */ - public void addServerClass(String classOrPackage) + public ClasspathPattern getServerClasspathPattern() { if (_serverClasses == null) loadServerClasses(); - _serverClasses.add(classOrPackage); + return _serverClasses; + } + + /* ------------------------------------------------------------ */ + @Deprecated + public void addServerClass(String classOrPackageOrLocation) + { + if (_serverClasses == null) + loadServerClasses(); + + _serverClasses.add(classOrPackageOrLocation); } /* ------------------------------------------------------------ */ @@ -693,12 +714,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL * @see #setServerClasses(String[]) * @see Jetty Documentation: Classloading */ + @Deprecated public void prependServerClass(String classOrPackage) { if (_serverClasses == null) loadServerClasses(); - _serverClasses.prependPattern(classOrPackage); + _serverClasses.add(classOrPackage); } /* ------------------------------------------------------------ */ @@ -714,17 +736,21 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return _systemClasses.getPatterns(); } + + /* ------------------------------------------------------------ */ + /** + * @return The ClasspathPattern used to match System (protected) classes + */ + public ClasspathPattern getSystemClasspathPattern() + { + if (_systemClasses == null) + loadSystemClasses(); + + return _systemClasses; + } /* ------------------------------------------------------------ */ - /** Add to the list of System classes. - * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) - * or a qualified package name ending with '.' (eg com.foo.). If the class - * or package has '-' it is excluded from the system classes and order is thus - * important when added system class patterns. This argument may also be a comma - * separated list of classOrPackage patterns. - * @see #setSystemClasses(String[]) - * @see Jetty Documentation: Classloading - */ + @Deprecated public void addSystemClass(String classOrPackage) { if (_systemClasses == null) @@ -744,16 +770,17 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL * @see #setSystemClasses(String[]) * @see Jetty Documentation: Classloading */ + @Deprecated public void prependSystemClass(String classOrPackage) { if (_systemClasses == null) loadSystemClasses(); - _systemClasses.prependPattern(classOrPackage); + _systemClasses.add(classOrPackage); } /* ------------------------------------------------------------ */ - @Override + @Deprecated public boolean isServerClass(String name) { if (_serverClasses == null) @@ -763,7 +790,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL } /* ------------------------------------------------------------ */ - @Override + @Deprecated public boolean isSystemClass(String name) { if (_systemClasses == null) @@ -771,6 +798,58 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return _systemClasses.match(name); } + + /* ------------------------------------------------------------ */ + @Override + public boolean isServerClass(Class clazz) + { + if (_serverClasses == null) + loadServerClasses(); + + boolean result = _serverClasses.match(clazz); + if (LOG.isDebugEnabled()) + LOG.debug("isServerClass=={} {}",result,clazz); + return result; + } + + /* ------------------------------------------------------------ */ + @Override + public boolean isSystemClass(Class clazz) + { + if (_systemClasses == null) + loadSystemClasses(); + + boolean result = _systemClasses.match(clazz); + if (LOG.isDebugEnabled()) + LOG.debug("isSystemClass=={} {}",result,clazz); + return result; + } + + /* ------------------------------------------------------------ */ + @Override + public boolean isServerResource(String name, URL url) + { + if (_serverClasses == null) + loadServerClasses(); + + boolean result = _serverClasses.match(name,url); + if (LOG.isDebugEnabled()) + LOG.debug("isServerResource=={} {} {}",result,name,url); + return result; + } + + /* ------------------------------------------------------------ */ + @Override + public boolean isSystemResource(String name, URL url) + { + if (_systemClasses == null) + loadSystemClasses(); + + boolean result = _systemClasses.match(name,url); + if (LOG.isDebugEnabled()) + LOG.debug("isSystemResource=={} {} {}",result,name,url); + return result; + } /* ------------------------------------------------------------ */ protected void loadSystemClasses() @@ -784,8 +863,10 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL if (server != null) { Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES); - if (systemClasses != null && systemClasses instanceof String[]) + if (systemClasses instanceof String[]) _systemClasses = new ClasspathPattern((String[])systemClasses); + else if (systemClasses instanceof ClasspathPattern) + _systemClasses = new ClasspathPattern(((ClasspathPattern)systemClasses).getPatterns()); } if (_systemClasses == null) @@ -793,7 +874,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL } /* ------------------------------------------------------------ */ - private void loadServerClasses() + protected void loadServerClasses() { if (_serverClasses != null) { @@ -806,10 +887,10 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL if (server != null) { Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES); - if (serverClasses != null && serverClasses instanceof String[]) - { + if (serverClasses instanceof String[]) _serverClasses = new ClasspathPattern((String[])serverClasses); - } + else if (serverClasses instanceof ClasspathPattern) + _serverClasses = new ClasspathPattern(((ClasspathPattern)serverClasses).getPatterns()); } if (_serverClasses == null) @@ -949,10 +1030,24 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL @Override public void dump(Appendable out, String indent) throws IOException { + List system_classes=null; + if (_systemClasses!=null) + { + system_classes=new ArrayList<>(_systemClasses); + Collections.sort(system_classes); + } + + List server_classes=null; + if (_serverClasses!=null) + { + server_classes=new ArrayList<>(_serverClasses); + Collections.sort(server_classes); + } + dumpBeans(out,indent, Collections.singletonList(new ClassLoaderDump(getClassLoader())), - Collections.singletonList(new DumpableCollection("Systemclasses "+this,_systemClasses)), - Collections.singletonList(new DumpableCollection("Serverclasses "+this,_serverClasses)), + Collections.singletonList(new DumpableCollection("Systemclasses "+this,system_classes)), + Collections.singletonList(new DumpableCollection("Serverclasses "+this,server_classes)), Collections.singletonList(new DumpableCollection("Configurations "+this,_configurations)), Collections.singletonList(new DumpableCollection("Handler attributes "+this,((AttributesMap)getAttributes()).getAttributeEntrySet())), Collections.singletonList(new DumpableCollection("Context attributes "+this,((Context)getServletContext()).getAttributeEntrySet())), @@ -1478,7 +1573,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { //not one of the standard servlet listeners, check our extended session listener types boolean ok = false; - for (Class l:SessionHandler.SESSION_LISTENER_TYPES) + for (Class l:SessionHandler.SESSION_LISTENER_TYPES) { if (l.isAssignableFrom(listener)) { @@ -1506,11 +1601,11 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL for (int i=resources.length;i-->0;) { if (resources[i].getName().startsWith("jar:file")) - return resources[i].getURL(); + return resources[i].getURI().toURL(); } } - return resource.getURL(); + return resource.getURI().toURL(); } /* ------------------------------------------------------------ */ @@ -1536,7 +1631,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return servletContext; } } - } /* ------------------------------------------------------------ */ @@ -1544,4 +1638,57 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { return _metadata; } + + /* ------------------------------------------------------------ */ + public static void addServerClasses(Server server,String... pattern ) + { + if (pattern == null || pattern.length == 0) + return; + + // look for a Server attribute with the list of Server classes + // to apply to every web application. If not present, use our defaults. + Object o = server.getAttribute(SERVER_SRV_CLASSES); + if (o instanceof ClasspathPattern) + { + ((ClasspathPattern)o).add(pattern); + return; + } + + String[] server_classes; + if (o instanceof String[]) + server_classes = (String[])o; + else + server_classes = __dftServerClasses; + int l = server_classes.length; + server_classes = Arrays.copyOf(server_classes,l+pattern.length); + System.arraycopy(pattern,0,server_classes,l,pattern.length); + server.setAttribute(SERVER_SRV_CLASSES,server_classes); + } + + /* ------------------------------------------------------------ */ + public static void addSystemClasses(Server server,String... pattern ) + { + if (pattern == null || pattern.length == 0) + return; + + // look for a Server attribute with the list of System classes + // to apply to every web application. If not present, use our defaults. + Object o = server.getAttribute(SERVER_SYS_CLASSES); + if (o instanceof ClasspathPattern) + { + ((ClasspathPattern)o).add(pattern); + return; + } + + String[] system_classes; + if (o instanceof String[]) + system_classes = (String[])o; + else + system_classes = __dftSystemClasses; + int l = system_classes.length; + system_classes = Arrays.copyOf(system_classes,l+pattern.length); + System.arraycopy(pattern,0,system_classes,l,pattern.length); + server.setAttribute(SERVER_SYS_CLASSES,system_classes); + } + } diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClasspathPatternTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClasspathPatternTest.java index 2091ffc0d56..cde8c4edfc9 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClasspathPatternTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClasspathPatternTest.java @@ -18,101 +18,214 @@ package org.eclipse.jetty.webapp; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.util.Arrays; +import org.eclipse.jetty.toolchain.test.JDK; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.hamcrest.Matchers; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import sun.security.provider.Sun; + public class ClasspathPatternTest { - private final ClasspathPattern pattern = new ClasspathPattern(); + private final ClasspathPattern _pattern = new ClasspathPattern(); @Before public void before() { - pattern.clear(); - pattern.add("org.package."); - pattern.add("-org.excluded."); - pattern.add("org.example.FooBar"); - pattern.add("-org.example.Excluded"); - pattern.addAll(Arrays.asList(new String[]{"-org.example.Nested$Minus","org.example.Nested","org.example.Nested$Something"})); + _pattern.clear(); + _pattern.add("org.package."); + _pattern.add("-org.excluded."); + _pattern.add("org.example.FooBar"); + _pattern.add("-org.example.Excluded"); + _pattern.addAll(Arrays.asList(new String[]{ + "-org.example.Nested$Minus", + "org.example.Nested", + "org.example.Nested$Something"})); + + + assertThat(_pattern,containsInAnyOrder( + "org.package.", + "-org.excluded.", + "org.example.FooBar", + "-org.example.Excluded", + "-org.example.Nested$Minus", + "org.example.Nested", + "org.example.Nested$Something" + )); } + @Test public void testClassMatch() { - assertTrue(pattern.match("org.example.FooBar")); - assertTrue(pattern.match("org.example.Nested")); + assertTrue(_pattern.match("org.example.FooBar")); + assertTrue(_pattern.match("org.example.Nested")); - assertFalse(pattern.match("org.example.Unknown")); - assertFalse(pattern.match("org.example.FooBar.Unknown")); + assertFalse(_pattern.match("org.example.Unknown")); + assertFalse(_pattern.match("org.example.FooBar.Unknown")); } @Test public void testPackageMatch() { - assertTrue(pattern.match("org.package.Something")); - assertTrue(pattern.match("org.package.other.Something")); + assertTrue(_pattern.match("org.package.Something")); + assertTrue(_pattern.match("org.package.other.Something")); - assertFalse(pattern.match("org.example.Unknown")); - assertFalse(pattern.match("org.example.FooBar.Unknown")); - assertFalse(pattern.match("org.example.FooBarElse")); + assertFalse(_pattern.match("org.example.Unknown")); + assertFalse(_pattern.match("org.example.FooBar.Unknown")); + assertFalse(_pattern.match("org.example.FooBarElse")); } @Test public void testExplicitNestedMatch() { - assertTrue(pattern.match("org.example.Nested$Something")); - assertFalse(pattern.match("org.example.Nested$Minus")); - assertTrue(pattern.match("org.example.Nested$Other")); + assertTrue(_pattern.match("org.example.Nested$Something")); + assertFalse(_pattern.match("org.example.Nested$Minus")); + assertTrue(_pattern.match("org.example.Nested$Other")); } @Test public void testImplicitNestedMatch() { - assertTrue(pattern.match("org.example.FooBar$Other")); - assertTrue(pattern.match("org.example.Nested$Other")); + assertTrue(_pattern.match("org.example.FooBar$Other")); + assertTrue(_pattern.match("org.example.Nested$Other")); } - @Test - public void testAddBefore() - { - pattern.addBefore("-org.excluded.","org.excluded.ExceptionOne","org.excluded.ExceptionTwo"); - - assertTrue(pattern.match("org.excluded.ExceptionOne")); - assertTrue(pattern.match("org.excluded.ExceptionTwo")); - - assertFalse(pattern.match("org.example.Unknown")); - } - - @Test - public void testAddAfter() - { - pattern.addAfter("org.package.","org.excluded.ExceptionOne","org.excluded.ExceptionTwo"); - - assertTrue(pattern.match("org.excluded.ExceptionOne")); - assertTrue(pattern.match("org.excluded.ExceptionTwo")); - - assertFalse(pattern.match("org.example.Unknown")); - } - @Test public void testDoubledNested() { - assertTrue(pattern.match("org.example.Nested$Something$Else")); + assertTrue(_pattern.match("org.example.Nested$Something$Else")); - assertFalse(pattern.match("org.example.Nested$Minus$Else")); + assertFalse(_pattern.match("org.example.Nested$Minus$Else")); } @Test public void testMatchAll() { - pattern.clear(); - pattern.add("."); - assertTrue(pattern.match("org.example.Anything")); - assertTrue(pattern.match("org.example.Anything$Else")); + _pattern.clear(); + _pattern.add("."); + assertTrue(_pattern.match("org.example.Anything")); + assertTrue(_pattern.match("org.example.Anything$Else")); + } + + /** + * + */ + @SuppressWarnings("restriction") + @Test + public void testiIncludedLocations() throws Exception + { + // jar from JVM classloader + Resource loc_string = TypeUtil.getLoadedFrom(String.class); + // System.err.println(loc_string); + + // another jar from JVM classloader + Resource loc_jsse = TypeUtil.getLoadedFrom(Sun.class); + // System.err.println(loc_jsse); + + // a jar from maven repo jar + Resource loc_junit = TypeUtil.getLoadedFrom(Test.class); + // System.err.println(loc_junit); + + // a jar from another maven repo jar + Resource loc_tool = TypeUtil.getLoadedFrom(JDK.class); + // System.err.println(loc_tool); + + // class file + Resource loc_test = TypeUtil.getLoadedFrom(ClasspathPatternTest.class); + // System.err.println(loc_test); + + ClasspathPattern pattern = new ClasspathPattern(); + pattern.include("something"); + assertThat(pattern.match(String.class),is(false)); + assertThat(pattern.match(Sun.class),is(false)); + assertThat(pattern.match(Test.class),is(false)); + assertThat(pattern.match(JDK.class),is(false)); + assertThat(pattern.match(ClasspathPatternTest.class),is(false)); + + // Add directory for both JVM classes + pattern.include(loc_string.getFile().getParentFile().toURI().toString()); + + // Add jar for individual class and classes directory + pattern.include(loc_junit.toString(),loc_test.toString()); + + assertThat(pattern.match(String.class),is(true)); + assertThat(pattern.match(Sun.class),is(true)); + assertThat(pattern.match(Test.class),is(true)); + assertThat(pattern.match(JDK.class),is(false)); + assertThat(pattern.match(ClasspathPatternTest.class),is(true)); + + // exclude by package name still works + pattern.add("-sun.security.provider.Sun"); + assertThat(pattern.match(String.class),is(true)); + assertThat(pattern.match(Sun.class),is(false)); + assertThat(pattern.match(Test.class),is(true)); + assertThat(pattern.match(JDK.class),is(false)); + assertThat(pattern.match(ClasspathPatternTest.class),is(true)); + + + } + + /** + * + */ + @SuppressWarnings("restriction") + @Test + public void testExcludeLocations() throws Exception + { + // jar from JVM classloader + Resource loc_string = TypeUtil.getLoadedFrom(String.class); + // System.err.println(loc_string); + + // another jar from JVM classloader + Resource loc_jsse = TypeUtil.getLoadedFrom(Sun.class); + // System.err.println(loc_jsse); + + // a jar from maven repo jar + Resource loc_junit = TypeUtil.getLoadedFrom(Test.class); + // System.err.println(loc_junit); + + // a jar from another maven repo jar + Resource loc_tool = TypeUtil.getLoadedFrom(JDK.class); + // System.err.println(loc_tool); + + // class file + Resource loc_test = TypeUtil.getLoadedFrom(ClasspathPatternTest.class); + // System.err.println(loc_test); + + ClasspathPattern pattern = new ClasspathPattern(); + + // include everything + pattern.include("."); + + assertThat(pattern.match(String.class),is(true)); + assertThat(pattern.match(Sun.class),is(true)); + assertThat(pattern.match(Test.class),is(true)); + assertThat(pattern.match(JDK.class),is(true)); + assertThat(pattern.match(ClasspathPatternTest.class),is(true)); + + // Add directory for both JVM classes + pattern.exclude(loc_string.getFile().getParentFile().toURI().toString()); + + // Add jar for individual class and classes directory + pattern.exclude(loc_junit.toString(),loc_test.toString()); + + assertThat(pattern.match(String.class),is(false)); + assertThat(pattern.match(Sun.class),is(false)); + assertThat(pattern.match(Test.class),is(false)); + assertThat(pattern.match(JDK.class),is(true)); + assertThat(pattern.match(ClasspathPatternTest.class),is(false)); } } diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java index 373c7464b4f..93e991f054c 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java @@ -56,8 +56,6 @@ public class WebAppClassLoaderTest this.testWebappDir = MavenTestingUtils.getProjectDirPath("src/test/webapp"); Resource webapp = new PathResource(testWebappDir); - System.err.printf("testWebappDir = %s%n", testWebappDir); - _context = new WebAppContext(); _context.setBaseResource(webapp); _context.setContextPath("/test"); @@ -66,6 +64,9 @@ public class WebAppClassLoaderTest _loader.addJars(webapp.addPath("WEB-INF/lib")); _loader.addClassPath(webapp.addPath("WEB-INF/classes")); _loader.setName("test"); + + _context.loadSystemClasses(); + _context.loadServerClasses(); } public void assertCanLoadClass(String clazz) throws ClassNotFoundException @@ -250,7 +251,7 @@ public class WebAppClassLoaderTest URL targetTestClasses = this.getClass().getClassLoader().getResource("org/acme/resource.txt"); _context.setParentLoaderPriority(false); - dump(_context); + resources =Collections.list(_loader.getResources("org/acme/resource.txt")); expected.clear(); @@ -260,12 +261,6 @@ public class WebAppClassLoaderTest assertThat("Resources Found (Parent Loader Priority == false)",resources,ordered(expected)); -// dump(resources); -// assertEquals(3,resources.size()); -// assertEquals(0,resources.get(0).toString().indexOf("jar:file:")); -// assertEquals(-1,resources.get(1).toString().indexOf("test-classes")); -// assertEquals(0,resources.get(2).toString().indexOf("file:")); - _context.setParentLoaderPriority(true); // dump(_context); resources =Collections.list(_loader.getResources("org/acme/resource.txt")); @@ -320,49 +315,6 @@ public class WebAppClassLoaderTest assertThat("Resources Found (Parent Loader Priority == true) (with systemClasses filtering)",resources,ordered(expected)); -// dump(resources); -// assertEquals(1,resources.size()); -// assertEquals(0,resources.get(0).toString().indexOf("file:")); } - private void dump(WebAppContext wac) - { - System.err.println("--Dump WebAppContext - " + wac); - System.err.printf(" context.getClass().getClassLoader() = %s%n",wac.getClass().getClassLoader()); - dumpClassLoaderHierarchy(" ",wac.getClass().getClassLoader()); - System.err.printf(" context.getClassLoader() = %s%n",wac.getClassLoader()); - dumpClassLoaderHierarchy(" ",wac.getClassLoader()); - } - - private void dumpClassLoaderHierarchy(String indent, ClassLoader classLoader) - { - if (classLoader != null) - { - if(classLoader instanceof URLClassLoader) - { - URLClassLoader urlCL = (URLClassLoader)classLoader; - URL urls[] = urlCL.getURLs(); - for (URL url : urls) - { - System.err.printf("%s url[] = %s%n",indent,url); - } - } - - ClassLoader parent = classLoader.getParent(); - if (parent != null) - { - System.err.printf("%s .parent = %s%n",indent,parent); - dumpClassLoaderHierarchy(indent + " ",parent); - } - } - } - - private void dump(List resources) - { - System.err.println("--Dump--"); - for(URL url: resources) - { - System.err.printf(" \"%s\"%n",url); - } - } } diff --git a/pom.xml b/pom.xml index a446fc9405d..e71e27814bb 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ scm:git:https://github.com/eclipse/jetty.project.git scm:git:git@github.com:eclipse/jetty.project.git https://github.com/eclipse/jetty.project - HEAD + jetty-9.3.13.M0 diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java index 61f38956422..1110eac8f64 100644 --- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java +++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.http.client; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InterruptedIOException; import java.io.UncheckedIOException; @@ -58,9 +59,12 @@ import org.eclipse.jetty.http2.client.http.HttpConnectionOverHTTP2; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpChannel; +import org.eclipse.jetty.server.HttpInput; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.HttpInput.Content; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandler.Context; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.log.StacklessLogging; import org.hamcrest.Matchers; @@ -68,6 +72,8 @@ import org.junit.Assert; import org.junit.Assume; import org.junit.Test; +import static java.nio.ByteBuffer.wrap; +import static org.eclipse.jetty.util.BufferUtil.toArray; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; @@ -1038,4 +1044,174 @@ public class AsyncIOServletTest extends AbstractTest assertTrue(errorLatch.await(5, TimeUnit.SECONDS)); assertTrue(clientLatch.await(5, TimeUnit.SECONDS)); } + + + @Test + public void testAsyncIntercepted() throws Exception + { + start(new HttpServlet() + { + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + System.err.println("Service "+request); + + final HttpInput httpInput = ((Request)request).getHttpInput(); + httpInput.addInterceptor(new HttpInput.Interceptor() + { + int state = 0; + Content saved; + + @Override + public Content readFrom(Content content) + { + // System.err.printf("readFrom s=%d saved=%b %s%n",state,saved!=null,content); + switch(state) + { + case 0: + // null transform + if (content.isEmpty()) + state++; + return null; + + case 1: + { + // copy transform + if (content.isEmpty()) + { + state++; + return content; + } + ByteBuffer copy = wrap(toArray(content.getByteBuffer())); + content.skip(copy.remaining()); + return new Content(copy); + } + + case 2: + // byte by byte + if (content.isEmpty()) + { + state++; + return content; + } + byte[] b = new byte[1]; + int l = content.get(b,0,1); + return new Content(wrap(b,0,l)); + + case 3: + { + // double vision + if (content.isEmpty()) + { + if (saved==null) + { + state++; + return content; + } + Content copy = saved; + saved=null; + return copy; + } + + byte[] data = toArray(content.getByteBuffer()); + content.skip(data.length); + saved = new Content(wrap(data)); + return new Content(wrap(data)); + } + + default: + return null; + } + } + }); + + AsyncContext asyncContext = request.startAsync(); + ServletInputStream input = request.getInputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + input.setReadListener(new ReadListener() + { + @Override + public void onDataAvailable() throws IOException + { + while (input.isReady() && !input.isFinished()) + { + int b = input.read(); + if (b>0) + { + // System.err.printf("0x%2x %s %n", b, Character.isISOControl(b)?"?":(""+(char)b)); + out.write(b); + } + else + onAllDataRead(); + } + } + + @Override + public void onAllDataRead() throws IOException + { + response.getOutputStream().write(out.toByteArray()); + asyncContext.complete(); + } + + @Override + public void onError(Throwable x) + { + } + }); + } + }); + + DeferredContentProvider contentProvider = new DeferredContentProvider(); + CountDownLatch clientLatch = new CountDownLatch(1); + + String expected = + "S0" + + "S1" + + "S2" + + "S3S3" + + "S4" + + "S5" + + "S6"; + + client.newRequest(newURI()) + .method(HttpMethod.POST) + .path(servletPath) + .content(contentProvider) + .send(new BufferingResponseListener() + { + @Override + public void onComplete(Result result) + { + if (result.isSucceeded()) + { + Response response = result.getResponse(); + assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200)); + assertThat(getContentAsString(), Matchers.equalTo(expected)); + clientLatch.countDown(); + } + } + }); + + contentProvider.offer(BufferUtil.toBuffer("S0")); + contentProvider.flush(); + contentProvider.offer(BufferUtil.toBuffer("S1")); + contentProvider.flush(); + contentProvider.offer(BufferUtil.toBuffer("S2")); + contentProvider.flush(); + contentProvider.offer(BufferUtil.toBuffer("S3")); + contentProvider.flush(); + contentProvider.offer(BufferUtil.toBuffer("S4")); + contentProvider.flush(); + contentProvider.offer(BufferUtil.toBuffer("S5")); + contentProvider.flush(); + contentProvider.offer(BufferUtil.toBuffer("S6")); + contentProvider.close(); + + + Assert.assertTrue(clientLatch.await(10,TimeUnit.SECONDS)); + + + } + }