Unify the handling of ServletContainerInitializers (#5959)
* Unify ServletContainerInitializer handling. Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
parent
ddec50eb2e
commit
3a32a80b9c
|
@ -18,6 +18,7 @@ import java.net.MalformedURLException;
|
|||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -39,9 +40,12 @@ import javax.servlet.ServletContainerInitializer;
|
|||
import javax.servlet.annotation.HandlesTypes;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.Handler;
|
||||
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
||||
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
|
||||
import org.eclipse.jetty.servlet.ServletContainerInitializerHolder;
|
||||
import org.eclipse.jetty.servlet.Source;
|
||||
import org.eclipse.jetty.servlet.Source.Origin;
|
||||
import org.eclipse.jetty.util.JavaVersion;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.MultiException;
|
||||
import org.eclipse.jetty.util.ProcessorUtils;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
@ -53,6 +57,7 @@ import org.eclipse.jetty.webapp.FragmentConfiguration;
|
|||
import org.eclipse.jetty.webapp.FragmentDescriptor;
|
||||
import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
|
||||
import org.eclipse.jetty.webapp.MetaInfConfiguration;
|
||||
import org.eclipse.jetty.webapp.WebAppClassLoader;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebDescriptor;
|
||||
import org.eclipse.jetty.webapp.WebXmlConfiguration;
|
||||
|
@ -80,6 +85,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
protected final List<AbstractDiscoverableAnnotationHandler> _discoverableAnnotationHandlers = new ArrayList<>();
|
||||
protected ClassInheritanceHandler _classInheritanceHandler;
|
||||
protected final List<ContainerInitializerAnnotationHandler> _containerInitializerAnnotationHandlers = new ArrayList<>();
|
||||
protected final List<DiscoveredServletContainerInitializerHolder> _sciHolders = new ArrayList<>();
|
||||
|
||||
protected List<ParserTask> _parserTasks;
|
||||
|
||||
|
@ -87,7 +93,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
protected CounterStatistic _webInfLibStats;
|
||||
protected CounterStatistic _webInfClassesStats;
|
||||
protected Pattern _sciExcludePattern;
|
||||
protected List<ServletContainerInitializer> _initializers;
|
||||
|
||||
public AnnotationConfiguration()
|
||||
{
|
||||
|
@ -314,6 +319,116 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
return Integer.compare(i1, i2);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DiscoveredServletContainerInitializerHolder extends ServletContainerInitializerHolder
|
||||
{
|
||||
private Set<Class<?>> _handlesTypes = new HashSet<>();
|
||||
private Set<String> _discoveredClassNames = new HashSet<>();
|
||||
|
||||
public DiscoveredServletContainerInitializerHolder(Source source, ServletContainerInitializer sci, Class<?>... startupClasses)
|
||||
{
|
||||
super(source, sci);
|
||||
//take the classes and set them aside until we can calculate all of their
|
||||
//subclasses as necessary
|
||||
_handlesTypes.addAll(_startupClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes that have annotations that are listed in @HandlesTypes
|
||||
* are discovered by the ContainerInitializerAnnotationHandler
|
||||
* and added here.
|
||||
* @param names of classnames that have an annotation that is listed as a class in HandlesTypes
|
||||
*/
|
||||
@Override
|
||||
public void addStartupClasses(String... names)
|
||||
{
|
||||
_discoveredClassNames.addAll(Arrays.asList(names));
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes that are listed in @HandlesTypes and found by
|
||||
* the createServletContainerInitializerAnnotationHandlers method.
|
||||
* @param clazzes classes listed in HandlesTypes
|
||||
*/
|
||||
@Override
|
||||
public void addStartupClasses(Class<?>... clazzes)
|
||||
{
|
||||
_handlesTypes.addAll(Arrays.asList(clazzes));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> resolveStartupClasses() throws Exception
|
||||
{
|
||||
final Set<Class<?>> classes = new HashSet<>();
|
||||
WebAppClassLoader.runWithServerClassAccess(() ->
|
||||
{
|
||||
for (String name:_startupClassNames)
|
||||
{
|
||||
classes.add(Loader.loadClass(name));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process each of the classes that are not annotations from @HandlesTypes and
|
||||
* find all of the subclasses/implementations.
|
||||
* Also process all of the classes that were discovered to have an annotation
|
||||
* that was listed in @HandlesTypes, and find all of their subclasses/implementations
|
||||
* in order to generate a complete set of classnames that can be passed into the
|
||||
* onStartup method.
|
||||
*
|
||||
* @param classMap complete inheritance tree of all classes in the webapp, can be
|
||||
* null if @HandlesTypes did not specify any classes.
|
||||
*/
|
||||
void resolveClasses(Map<String, Set<String>> classMap)
|
||||
{
|
||||
Set<String> finalClassnames = new HashSet<>();
|
||||
|
||||
if (classMap != null)
|
||||
{
|
||||
for (Class<?> c : _handlesTypes)
|
||||
{
|
||||
//find all subclasses/implementations of the classes (not annotations) named in @HandlesTypes
|
||||
if (!c.isAnnotation())
|
||||
addInheritedTypes(finalClassnames, classMap, (Set<String>)classMap.get(c.getName()));
|
||||
}
|
||||
|
||||
for (String classname:_discoveredClassNames)
|
||||
{
|
||||
//add each of the classes that were discovered to have an annotation listed in @HandlesTypes
|
||||
finalClassnames.add(classname);
|
||||
//walk its hierarchy and find all types that extend or implement the class
|
||||
addInheritedTypes(finalClassnames, classMap, (Set<String>)classMap.get(classname));
|
||||
}
|
||||
}
|
||||
|
||||
//finally, add the complete set of startup classnames
|
||||
super.addStartupClasses(finalClassnames.toArray(new String[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walk the class hierarchy for the given set of classnames.
|
||||
*
|
||||
* @param results all classes related to the set of classnames in names
|
||||
* @param classMap full inheritance tree for all classes in the webapp
|
||||
* @param names the names of classes for which to walk the hierarchy
|
||||
*/
|
||||
private void addInheritedTypes(Set<String> results, Map<String, Set<String>> classMap, Set<String> names)
|
||||
{
|
||||
if (names == null || names.isEmpty())
|
||||
return;
|
||||
|
||||
for (String s : names)
|
||||
{
|
||||
results.add(s);
|
||||
|
||||
//walk the hierarchy and find all types that extend or implement the class
|
||||
addInheritedTypes(results, classMap, (Set<String>)classMap.get(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preConfigure(final WebAppContext context) throws Exception
|
||||
|
@ -351,17 +466,12 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
|
||||
if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
|
||||
scanForAnnotations(context);
|
||||
|
||||
// Resolve container initializers
|
||||
List<ContainerInitializer> initializers =
|
||||
(List<ContainerInitializer>)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
|
||||
if (initializers != null && initializers.size() > 0)
|
||||
|
||||
Map<String, Set<String>> map = (Map<String, Set<String>>)context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
|
||||
for (DiscoveredServletContainerInitializerHolder holder:_sciHolders)
|
||||
{
|
||||
Map<String, Set<String>> map = (Map<String, Set<String>>)context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
|
||||
for (ContainerInitializer i : initializers)
|
||||
{
|
||||
i.resolveClasses(context, map);
|
||||
}
|
||||
holder.resolveClasses(map);
|
||||
context.addServletContainerInitializer(holder); //only add the holder now all classes are fully available
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,17 +483,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
classMap.clear();
|
||||
context.removeAttribute(CLASS_INHERITANCE_MAP);
|
||||
|
||||
List<ContainerInitializer> initializers = (List<ContainerInitializer>)context.getAttribute(CONTAINER_INITIALIZERS);
|
||||
if (initializers != null)
|
||||
initializers.clear();
|
||||
context.removeAttribute(CONTAINER_INITIALIZERS);
|
||||
|
||||
if (_discoverableAnnotationHandlers != null)
|
||||
_discoverableAnnotationHandlers.clear();
|
||||
|
||||
_discoverableAnnotationHandlers.clear();
|
||||
_classInheritanceHandler = null;
|
||||
if (_containerInitializerAnnotationHandlers != null)
|
||||
_containerInitializerAnnotationHandlers.clear();
|
||||
_containerInitializerAnnotationHandlers.clear();
|
||||
_sciHolders.clear();
|
||||
|
||||
if (_parserTasks != null)
|
||||
{
|
||||
|
@ -391,16 +494,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
_parserTasks = null;
|
||||
}
|
||||
|
||||
ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(CONTAINER_INITIALIZER_STARTER);
|
||||
if (starter != null)
|
||||
{
|
||||
context.removeBean(starter);
|
||||
context.removeAttribute(CONTAINER_INITIALIZER_STARTER);
|
||||
}
|
||||
|
||||
if (_initializers != null)
|
||||
_initializers.clear();
|
||||
|
||||
super.postConfigure(context);
|
||||
}
|
||||
|
||||
|
@ -568,74 +661,46 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
{
|
||||
if (scis == null || scis.isEmpty())
|
||||
return; // nothing to do
|
||||
|
||||
List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
|
||||
context.setAttribute(CONTAINER_INITIALIZERS, initializers);
|
||||
|
||||
for (ServletContainerInitializer service : scis)
|
||||
|
||||
for (ServletContainerInitializer sci : scis)
|
||||
{
|
||||
HandlesTypes annotation = service.getClass().getAnnotation(HandlesTypes.class);
|
||||
ContainerInitializer initializer = null;
|
||||
Class<?>[] classes = new Class<?>[0];
|
||||
HandlesTypes annotation = sci.getClass().getAnnotation(HandlesTypes.class);
|
||||
if (annotation != null)
|
||||
{
|
||||
//There is a HandlesTypes annotation on the on the ServletContainerInitializer
|
||||
Class<?>[] classes = annotation.value();
|
||||
if (classes != null)
|
||||
{
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("HandlesTypes {} on initializer {}", Arrays.asList(classes), service.getClass());
|
||||
}
|
||||
|
||||
initializer = new ContainerInitializer(service, classes);
|
||||
|
||||
//If we haven't already done so, we need to register a handler that will
|
||||
//process the whole class hierarchy to satisfy the ServletContainerInitializer
|
||||
if (context.getAttribute(CLASS_INHERITANCE_MAP) == null)
|
||||
{
|
||||
//MultiMap<String> map = new MultiMap<>();
|
||||
Map<String, Set<String>> map = new ClassInheritanceMap();
|
||||
context.setAttribute(CLASS_INHERITANCE_MAP, map);
|
||||
_classInheritanceHandler = new ClassInheritanceHandler(map);
|
||||
}
|
||||
|
||||
for (Class<?> c : classes)
|
||||
{
|
||||
//The value of one of the HandlesTypes classes is actually an Annotation itself so
|
||||
//register a handler for it
|
||||
if (c.isAnnotation())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Registering annotation handler for {}", c.getName());
|
||||
_containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(initializer, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initializer = new ContainerInitializer(service, null);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("No classes in HandlesTypes on initializer {}", service.getClass());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initializer = new ContainerInitializer(service, null);
|
||||
classes = annotation.value();
|
||||
|
||||
DiscoveredServletContainerInitializerHolder holder = new DiscoveredServletContainerInitializerHolder(new Source(Origin.ANNOTATION, sci.getClass().getName()), sci);
|
||||
_sciHolders.add(holder);
|
||||
|
||||
if (classes.length > 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("No HandlesTypes annotation on initializer {}", service.getClass());
|
||||
LOG.debug("HandlesTypes {} on initializer {}", Arrays.asList(classes), sci.getClass());
|
||||
|
||||
//If we haven't already done so, we need to register a handler that will
|
||||
//process the whole class hierarchy to satisfy the ServletContainerInitializer
|
||||
if (context.getAttribute(CLASS_INHERITANCE_MAP) == null)
|
||||
{
|
||||
Map<String, Set<String>> map = new ClassInheritanceMap();
|
||||
context.setAttribute(CLASS_INHERITANCE_MAP, map);
|
||||
_classInheritanceHandler = new ClassInheritanceHandler(map);
|
||||
}
|
||||
|
||||
for (Class<?> c : classes)
|
||||
{
|
||||
//The value of one of the HandlesTypes classes is actually an Annotation itself so
|
||||
//register a handler for it to discover all classes that contain this annotation
|
||||
if (c.isAnnotation())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Registering annotation handler for {}", c.getName());
|
||||
_containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(holder, c));
|
||||
}
|
||||
|
||||
holder.addStartupClasses(c);
|
||||
}
|
||||
}
|
||||
|
||||
initializers.add(initializer);
|
||||
}
|
||||
|
||||
//add a bean to the context which will call the servletcontainerinitializers when appropriate
|
||||
ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(CONTAINER_INITIALIZER_STARTER);
|
||||
if (starter != null)
|
||||
throw new IllegalStateException("ServletContainerInitializersStarter already exists");
|
||||
starter = new ServletContainerInitializersStarter(context);
|
||||
context.setAttribute(CONTAINER_INITIALIZER_STARTER, starter);
|
||||
context.addBean(starter, true);
|
||||
}
|
||||
|
||||
public Resource getJarFor(ServletContainerInitializer service)
|
||||
|
|
|
@ -13,11 +13,14 @@
|
|||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler;
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo;
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo;
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo;
|
||||
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
||||
import org.eclipse.jetty.servlet.ServletContainerInitializerHolder;
|
||||
|
||||
/**
|
||||
* ContainerInitializerAnnotationHandler
|
||||
|
@ -29,12 +32,22 @@ import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
|||
public class ContainerInitializerAnnotationHandler extends AbstractHandler
|
||||
{
|
||||
final ContainerInitializer _initializer;
|
||||
final Class _annotation;
|
||||
final ServletContainerInitializerHolder _holder;
|
||||
final Class<?> _annotation;
|
||||
|
||||
public ContainerInitializerAnnotationHandler(ContainerInitializer initializer, Class annotation)
|
||||
@Deprecated
|
||||
public ContainerInitializerAnnotationHandler(ContainerInitializer initializer, Class<?> annotation)
|
||||
{
|
||||
_holder = null;
|
||||
_annotation = Objects.requireNonNull(annotation);
|
||||
_initializer = initializer;
|
||||
_annotation = annotation;
|
||||
}
|
||||
|
||||
public ContainerInitializerAnnotationHandler(ServletContainerInitializerHolder holder, Class<?> annotation)
|
||||
{
|
||||
_holder = Objects.requireNonNull(holder);
|
||||
_annotation = Objects.requireNonNull(annotation);
|
||||
_initializer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,10 +58,13 @@ public class ContainerInitializerAnnotationHandler extends AbstractHandler
|
|||
@Override
|
||||
public void handle(ClassInfo info, String annotationName)
|
||||
{
|
||||
if (annotationName == null || !_annotation.getName().equals(annotationName))
|
||||
if (!_annotation.getName().equals(annotationName))
|
||||
return;
|
||||
|
||||
_initializer.addAnnotatedTypeName(info.getClassName());
|
||||
if (_initializer != null)
|
||||
_initializer.addAnnotatedTypeName(info.getClassName());
|
||||
else
|
||||
_holder.addStartupClasses(info.getClassName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,9 +75,13 @@ public class ContainerInitializerAnnotationHandler extends AbstractHandler
|
|||
@Override
|
||||
public void handle(FieldInfo info, String annotationName)
|
||||
{
|
||||
if (annotationName == null || !_annotation.getName().equals(annotationName))
|
||||
if (!_annotation.getName().equals(annotationName))
|
||||
return;
|
||||
_initializer.addAnnotatedTypeName(info.getClassInfo().getClassName());
|
||||
|
||||
if (_initializer != null)
|
||||
_initializer.addAnnotatedTypeName(info.getClassInfo().getClassName());
|
||||
else
|
||||
_holder.addStartupClasses(info.getClassInfo().getClassName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,11 +92,15 @@ public class ContainerInitializerAnnotationHandler extends AbstractHandler
|
|||
@Override
|
||||
public void handle(MethodInfo info, String annotationName)
|
||||
{
|
||||
if (annotationName == null || !_annotation.getName().equals(annotationName))
|
||||
if (!_annotation.getName().equals(annotationName))
|
||||
return;
|
||||
_initializer.addAnnotatedTypeName(info.getClassInfo().getClassName());
|
||||
if (_initializer != null)
|
||||
_initializer.addAnnotatedTypeName(info.getClassInfo().getClassName());
|
||||
else
|
||||
_holder.addStartupClasses(info.getClassInfo().getClassName());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ContainerInitializer getContainerInitializer()
|
||||
{
|
||||
return _initializer;
|
||||
|
|
|
@ -27,7 +27,9 @@ import org.slf4j.LoggerFactory;
|
|||
*
|
||||
* Call the onStartup() method on all ServletContainerInitializers, after having
|
||||
* found all applicable classes (if any) to pass in as args.
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public class ServletContainerInitializersStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ServletContainerInitializersStarter.class);
|
||||
|
|
|
@ -16,10 +16,18 @@ package org.eclipse.jetty.annotations;
|
|||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration.ClassInheritanceMap;
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration.DiscoveredServletContainerInitializerHolder;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.JAR;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
|
@ -30,6 +38,8 @@ import org.eclipse.jetty.webapp.WebDescriptor;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -46,7 +56,7 @@ public class TestAnnotationConfiguration
|
|||
else
|
||||
assertFalse(_discoverableAnnotationHandlers.isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public File web25;
|
||||
|
||||
|
@ -194,6 +204,57 @@ public class TestAnnotationConfiguration
|
|||
Thread.currentThread().setContextClassLoader(old);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassScanHandlersForSCIs() throws Exception
|
||||
{
|
||||
//test that SCIs with a @HandlesTypes that is an annotation registers
|
||||
//handlers for the scanning phase that will capture the class hierarchy,
|
||||
//and also capture all classes that contain the annotation
|
||||
ClassLoader old = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(webAppLoader);
|
||||
|
||||
try
|
||||
{
|
||||
class MyAnnotationConfiguration extends AnnotationConfiguration
|
||||
{
|
||||
|
||||
@Override
|
||||
public void createServletContainerInitializerAnnotationHandlers(WebAppContext context, List<ServletContainerInitializer> scis) throws Exception
|
||||
{
|
||||
super.createServletContainerInitializerAnnotationHandlers(context, scis);
|
||||
//check class hierarchy scanner handler is registered
|
||||
assertNotNull(_classInheritanceHandler);
|
||||
//check
|
||||
assertEquals(1, _containerInitializerAnnotationHandlers.size());
|
||||
ContainerInitializerAnnotationHandler handler = _containerInitializerAnnotationHandlers.get(0);
|
||||
assertThat(handler._holder.toString(), containsString("com.acme.initializer.FooInitializer"));
|
||||
assertEquals("com.acme.initializer.Foo", handler._annotation.getName());
|
||||
}
|
||||
}
|
||||
|
||||
MyAnnotationConfiguration config = new MyAnnotationConfiguration();
|
||||
|
||||
WebAppContext context = new WebAppContext();
|
||||
List<ServletContainerInitializer> scis;
|
||||
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
|
||||
context.getMetaData().setWebInfClassesResources(classes);
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
assertNotNull(scis);
|
||||
assertEquals(3, scis.size());
|
||||
|
||||
config.createServletContainerInitializerAnnotationHandlers(context, scis);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(old);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetaDataCompleteSCIs() throws Exception
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.HandlesTypes;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration.DiscoveredServletContainerInitializerHolder;
|
||||
import org.eclipse.jetty.servlet.Source;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
public class TestDiscoveredServletContainerInitializerHolder
|
||||
{
|
||||
/**
|
||||
* A marker type that is passed as an arg to @HandlesTypes
|
||||
*/
|
||||
interface Ordinary
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An class with an annotation (that is listed in @HandlesTypes)
|
||||
*/
|
||||
@Sample(value = 1)
|
||||
public static class ASample
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that extends a class with an annotation
|
||||
*/
|
||||
public static class BSample extends ASample
|
||||
{
|
||||
}
|
||||
|
||||
@HandlesTypes({Sample.class})
|
||||
public static class SampleServletContainerInitializer implements ServletContainerInitializer
|
||||
{
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception
|
||||
{
|
||||
//SCI with @HandlesTypes[Ordinary, Sample]
|
||||
SampleServletContainerInitializer sci = new SampleServletContainerInitializer();
|
||||
|
||||
DiscoveredServletContainerInitializerHolder holder =
|
||||
new DiscoveredServletContainerInitializerHolder(new Source(Source.Origin.ANNOTATION, sci.getClass().getName()),
|
||||
sci);
|
||||
|
||||
//add the @HandlesTypes to the holder
|
||||
holder.addStartupClasses(Ordinary.class, Sample.class);
|
||||
|
||||
//pretend scanned and discovered that ASample has the Sample annotation
|
||||
holder.addStartupClasses(ASample.class.getName());
|
||||
|
||||
//pretend we scanned the entire class hierarchy and found:
|
||||
// com.acme.tom and com.acme.dick both extend Ordinary
|
||||
// ASample has subclass BSample
|
||||
Map<String, Set<String>> classMap = new HashMap<>();
|
||||
classMap.put(Ordinary.class.getName(), new HashSet(Arrays.asList("com.acme.tom", "com.acme.dick")));
|
||||
classMap.put(ASample.class.getName(), new HashSet(Arrays.asList(BSample.class.getName())));
|
||||
holder.resolveClasses(classMap);
|
||||
|
||||
//we should now have the following classes that will be passed to the SampleServletContainerInitializer.onStartup
|
||||
String toString = holder.toString();
|
||||
assertThat(toString, containsString("com.acme.tom"));
|
||||
assertThat(toString, containsString("com.acme.dick"));
|
||||
assertThat(toString, containsString(ASample.class.getName()));
|
||||
assertThat(toString, containsString(BSample.class.getName()));
|
||||
assertThat(toString, containsString("applicable=[],annotated=[]"));
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Deprecated
|
||||
public class ContainerInitializer
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ContainerInitializer.class);
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.servlet.ServletContext;
|
|||
import org.eclipse.jetty.annotations.AnnotationConfiguration;
|
||||
import org.eclipse.jetty.annotations.ServletContainerInitializersStarter;
|
||||
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
||||
import org.eclipse.jetty.servlet.ServletContainerInitializerHolder;
|
||||
import org.eclipse.jetty.servlet.ServletMapping;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
@ -171,9 +172,10 @@ public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
|
||||
case AnnotationConfiguration.CONTAINER_INITIALIZERS:
|
||||
{
|
||||
for (String i : values)
|
||||
for (String s : values)
|
||||
{
|
||||
visitContainerInitializer(context, new ContainerInitializer(Thread.currentThread().getContextClassLoader(), i));
|
||||
visitServletContainerInitializerHolder(context,
|
||||
ServletContainerInitializerHolder.fromString(Thread.currentThread().getContextClassLoader(), s));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -216,6 +218,7 @@ public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void visitContainerInitializer(WebAppContext context, ContainerInitializer containerInitializer)
|
||||
{
|
||||
if (containerInitializer == null)
|
||||
|
@ -240,6 +243,19 @@ public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
context.addBean(starter, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the ServletContainerInitializerHolder will be started by adding it to the context.
|
||||
*
|
||||
* @param context the context to which to add the ServletContainerInitializerHolder
|
||||
* @param sciHolder the ServletContainerInitializerHolder
|
||||
*/
|
||||
public void visitServletContainerInitializerHolder(WebAppContext context, ServletContainerInitializerHolder sciHolder)
|
||||
{
|
||||
if (sciHolder == null)
|
||||
return;
|
||||
context.addServletContainerInitializer(sciHolder);
|
||||
}
|
||||
|
||||
public void visitMetaInfResource(WebAppContext context, Resource dir)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.eclipse.jetty.servlet.FilterHolder;
|
|||
import org.eclipse.jetty.servlet.FilterMapping;
|
||||
import org.eclipse.jetty.servlet.ListenerHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler.ServletContainerInitializerStarter;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.ServletMapping;
|
||||
|
@ -168,7 +169,11 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
|
|||
// The library order
|
||||
addContextParamFromAttribute(context, out, ServletContext.ORDERED_LIBS);
|
||||
//the servlet container initializers
|
||||
addContextParamFromAttribute(context, out, AnnotationConfiguration.CONTAINER_INITIALIZERS);
|
||||
//addContextParamFromAttribute(context, out, AnnotationConfiguration.CONTAINER_INITIALIZERS);
|
||||
//TODO think of better label rather than the unused attribute, also how to retrieve the scis
|
||||
ServletContainerInitializerStarter sciStarter = context.getBean(ServletContainerInitializerStarter.class);
|
||||
addContextParamFromCollection(context, out, AnnotationConfiguration.CONTAINER_INITIALIZERS,
|
||||
sciStarter == null ? Collections.emptySet() : sciStarter.getServletContainerInitializerHolders());
|
||||
//the tlds discovered
|
||||
addContextParamFromAttribute(context, out, MetaInfConfiguration.METAINF_TLDS, normalizer);
|
||||
//the META-INF/resources discovered
|
||||
|
@ -602,18 +607,14 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
|
|||
out.closeTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn attribute into context-param to store.
|
||||
*/
|
||||
private void addContextParamFromAttribute(WebAppContext context, XmlAppendable out, String attribute) throws IOException
|
||||
private void addContextParamFromCollection(WebAppContext context, XmlAppendable out, String name, Collection<?> collection)
|
||||
throws IOException
|
||||
{
|
||||
Object o = context.getAttribute(attribute);
|
||||
if (o == null)
|
||||
if (collection == null)
|
||||
return;
|
||||
|
||||
Collection<?> c = (o instanceof Collection) ? (Collection<?>)o : Collections.singletonList(o);
|
||||
StringBuilder v = new StringBuilder();
|
||||
for (Object i : c)
|
||||
for (Object i : collection)
|
||||
{
|
||||
if (i != null)
|
||||
{
|
||||
|
@ -625,10 +626,23 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
|
|||
}
|
||||
}
|
||||
out.openTag("context-param")
|
||||
.tag("param-name", attribute)
|
||||
.tag("param-name", name)
|
||||
.tagCDATA("param-value", v.toString())
|
||||
.closeTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn attribute into context-param to store.
|
||||
*/
|
||||
private void addContextParamFromAttribute(WebAppContext context, XmlAppendable out, String attribute) throws IOException
|
||||
{
|
||||
Object o = context.getAttribute(attribute);
|
||||
if (o == null)
|
||||
return;
|
||||
|
||||
Collection<?> c = (o instanceof Collection) ? (Collection<?>)o : Collections.singletonList(o);
|
||||
addContextParamFromCollection(context, out, attribute, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn context attribute into context-param to store.
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Holds a ServletContainerInitializer.
|
||||
*/
|
||||
public class ServletContainerInitializerHolder extends BaseHolder<ServletContainerInitializer>
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ServletContainerInitializerHolder.class);
|
||||
protected Set<String> _startupClassNames = new HashSet<>();
|
||||
protected Set<Class<?>> _startupClasses = new HashSet<>();
|
||||
public static final Pattern __pattern = Pattern.compile("ContainerInitializer\\{([^,]*),interested=(\\[[^\\]]*\\])(,applicable=(\\[[^\\]]*\\]))?(,annotated=(\\[[^\\]]*\\]))?\\}");
|
||||
|
||||
protected ServletContainerInitializerHolder(Source source)
|
||||
{
|
||||
super(source);
|
||||
}
|
||||
|
||||
public ServletContainerInitializerHolder()
|
||||
{
|
||||
this(Source.EMBEDDED);
|
||||
}
|
||||
|
||||
public ServletContainerInitializerHolder(Class<? extends ServletContainerInitializer> sciClass)
|
||||
{
|
||||
super(Source.EMBEDDED);
|
||||
setHeldClass(sciClass);
|
||||
}
|
||||
|
||||
public ServletContainerInitializerHolder(Class<? extends ServletContainerInitializer> sciClass, Class<?>... startupClasses)
|
||||
{
|
||||
super(Source.EMBEDDED);
|
||||
setHeldClass(sciClass);
|
||||
_startupClasses.addAll(Arrays.asList(startupClasses));
|
||||
}
|
||||
|
||||
public ServletContainerInitializerHolder(ServletContainerInitializer sci, Class<?>... startupClasses)
|
||||
{
|
||||
this(Source.EMBEDDED, sci, startupClasses);
|
||||
}
|
||||
|
||||
public ServletContainerInitializerHolder(Source source, ServletContainerInitializer sci, Class<?>... startupClasses)
|
||||
{
|
||||
super(source);
|
||||
setInstance(sci);
|
||||
if (startupClasses != null)
|
||||
_startupClasses.addAll(Arrays.asList(startupClasses));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param names the names of classes which should be passed to the SCI onStartup method
|
||||
*/
|
||||
public void addStartupClasses(String... names)
|
||||
{
|
||||
Collections.addAll(_startupClassNames, names);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazzes classes that should be passed to the SCI onStartup method
|
||||
*/
|
||||
public void addStartupClasses(Class<?>... clazzes)
|
||||
{
|
||||
Collections.addAll(_startupClasses, clazzes);
|
||||
}
|
||||
|
||||
protected Set<Class<?>> resolveStartupClasses() throws Exception
|
||||
{
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
for (String name : _startupClassNames)
|
||||
{
|
||||
classes.add(Loader.loadClass(name)); //TODO catch CNFE?
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doStart() throws Exception
|
||||
{
|
||||
//Ensure all startup classes are also loaded
|
||||
Set<Class<?>> classes = new HashSet<>(_startupClasses);
|
||||
|
||||
//Ensure SCI class is loaded
|
||||
super.doStart();
|
||||
|
||||
//load all classnames
|
||||
classes.addAll(resolveStartupClasses());
|
||||
|
||||
ContextHandler.Context ctx = null;
|
||||
if (getServletHandler() != null)
|
||||
{
|
||||
ctx = getServletHandler().getServletContextHandler().getServletContext();
|
||||
}
|
||||
|
||||
if (ctx == null && ContextHandler.getCurrentContext() != null)
|
||||
ctx = ContextHandler.getCurrentContext();
|
||||
if (ctx == null)
|
||||
throw new IllegalStateException("No Context");
|
||||
|
||||
ServletContainerInitializer initializer = getInstance();
|
||||
if (initializer == null)
|
||||
{
|
||||
//create an instance of the SCI
|
||||
initializer = createInstance();
|
||||
initializer = wrap(initializer, WrapFunction.class, WrapFunction::wrapServletContainerInitializer);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
ctx.setExtendedListenerTypes(true);
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
initializer.onStartup(classes, ctx);
|
||||
LOG.debug("ServletContainerInitializer {} called in {}ms", getClassName(), TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS));
|
||||
}
|
||||
else
|
||||
initializer.onStartup(classes, ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ctx.setExtendedListenerTypes(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-inflate a stringified ServletContainerInitializerHolder.
|
||||
*
|
||||
* @param loader the classloader to use to load the startup classes
|
||||
* @param string the stringified representation of the ServletContainerInitializerHolder
|
||||
*
|
||||
* @return a new ServletContainerInitializerHolder instance populated by the info in the string
|
||||
*/
|
||||
public static ServletContainerInitializerHolder fromString(ClassLoader loader, String string)
|
||||
{
|
||||
Matcher m = __pattern.matcher(string);
|
||||
|
||||
if (!m.matches())
|
||||
throw new IllegalArgumentException(string);
|
||||
|
||||
try
|
||||
{
|
||||
//load the ServletContainerInitializer and create an instance
|
||||
String sciClassname = m.group(1);
|
||||
ServletContainerInitializer sci = (ServletContainerInitializer)loader.loadClass(sciClassname).getDeclaredConstructor().newInstance();
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(new Source(Source.Origin.ANNOTATION, sciClassname));
|
||||
holder.setInstance(sci);
|
||||
|
||||
//ensure all classes to be passed to onStartup are resolved
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
String[] classnames = StringUtil.arrayFromString(m.group(2));
|
||||
for (String name:classnames)
|
||||
classes.add(loader.loadClass(name));
|
||||
|
||||
classnames = StringUtil.arrayFromString(m.group(4));
|
||||
for (String name:classnames)
|
||||
classes.add(loader.loadClass(name));
|
||||
|
||||
classnames = StringUtil.arrayFromString(m.group(6));
|
||||
for (String name:classnames)
|
||||
classes.add(loader.loadClass(name));
|
||||
|
||||
holder.addStartupClasses(classes.toArray(new Class<?>[0]));
|
||||
|
||||
return holder;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IllegalArgumentException(string, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
Set<String> interested = new HashSet<>(_startupClassNames);
|
||||
_startupClasses.forEach((c) -> interested.add(c.getName()));
|
||||
//for backward compatibility the old output format must be retained
|
||||
return String.format("ContainerInitializer{%s,interested=%s,applicable=%s,annotated=%s}", getClassName(), interested, Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Experimental Wrapper mechanism for ServletContainerInitializer objects.
|
||||
* <p>
|
||||
* Beans in {@code ServletContextHandler} or {@code WebAppContext} that implement this interface
|
||||
* will be called to optionally wrap any newly created ServletContainerInitializers
|
||||
* (before their onStartup method is called)
|
||||
* </p>
|
||||
*/
|
||||
public interface WrapFunction
|
||||
{
|
||||
/**
|
||||
* Optionally wrap the ServletContainerInitializer.
|
||||
*
|
||||
* @param sci the ServletContainerInitializer being passed in.
|
||||
* @return the sci(extend from {@link ServletContainerInitializerHolder.Wrapper} if you do wrap the ServletContainerInitializer)
|
||||
*/
|
||||
ServletContainerInitializer wrapServletContainerInitializer(ServletContainerInitializer sci);
|
||||
}
|
||||
|
||||
public static class Wrapper implements ServletContainerInitializer, Wrapped<ServletContainerInitializer>
|
||||
{
|
||||
private final ServletContainerInitializer _wrappedSCI;
|
||||
|
||||
public Wrapper(ServletContainerInitializer sci)
|
||||
{
|
||||
_wrappedSCI = Objects.requireNonNull(sci, "ServletContainerInitializer cannot be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContainerInitializer getWrapped()
|
||||
{
|
||||
return _wrappedSCI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
|
||||
{
|
||||
_wrappedSCI.onStartup(c, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s:%s", this.getClass().getSimpleName(), _wrappedSCI.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,6 +68,7 @@ import org.eclipse.jetty.util.StringUtil;
|
|||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -505,6 +506,56 @@ public class ServletContextHandler extends ContextHandler
|
|||
return getServletHandler().addFilterWithMapping(filterClass, pathSpec, dispatches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to programmatically add a {@link javax.servlet.ServletContainerInitializer}.
|
||||
* @param sci the ServletContainerInitializer to register.
|
||||
* @return the ServletContainerInitializerHolder that was created
|
||||
*/
|
||||
public ServletContainerInitializerHolder addServletContainerInitializer(ServletContainerInitializer sci)
|
||||
{
|
||||
if (!isStopped())
|
||||
throw new IllegalStateException("ServletContainerInitializers should be added before starting");
|
||||
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(sci);
|
||||
addServletContainerInitializer(holder);
|
||||
return holder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to programmatically add a {@link javax.servlet.ServletContainerInitializer}.
|
||||
* @param sci the ServletContainerInitializer to register.
|
||||
* @param classes the Set of application classes.
|
||||
* @return the ServletContainerInitializerHolder that was created
|
||||
*/
|
||||
public ServletContainerInitializerHolder addServletContainerInitializer(ServletContainerInitializer sci, Class<?>... classes)
|
||||
{
|
||||
if (!isStopped())
|
||||
throw new IllegalStateException("ServletContainerInitializers should be added before starting");
|
||||
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(sci, classes);
|
||||
addServletContainerInitializer(holder);
|
||||
return holder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to programmatically add a list of {@link javax.servlet.ServletContainerInitializer}.
|
||||
* The initializers are guaranteed to be called in the order they are passed into this method.
|
||||
* @param sciHolders the ServletContainerInitializerHolders
|
||||
*/
|
||||
public void addServletContainerInitializer(ServletContainerInitializerHolder... sciHolders)
|
||||
{
|
||||
ServletContainerInitializerStarter starter = getBean(ServletContainerInitializerStarter.class);
|
||||
if (starter == null)
|
||||
{
|
||||
//add the starter as bean which will start when the context is started
|
||||
//NOTE: do not use addManaged(starter) because this will start the
|
||||
//starter immediately, which may not be before we have parsed web.xml
|
||||
starter = new ServletContainerInitializerStarter();
|
||||
addBean(starter, true);
|
||||
}
|
||||
starter.addServletContainerInitializerHolders(sciHolders);
|
||||
}
|
||||
|
||||
/**
|
||||
* notification that a ServletRegistration has been created so we can track the annotations
|
||||
*
|
||||
|
@ -1545,7 +1596,9 @@ public class ServletContextHandler extends ContextHandler
|
|||
* A utility class to hold a {@link ServletContainerInitializer} and implement the
|
||||
* {@link ServletContainerInitializerCaller} interface so that the SCI is correctly
|
||||
* started if an instance of this class is added as a bean to a {@link ServletContextHandler}.
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public static class Initializer extends AbstractLifeCycle implements ServletContainerInitializerCaller
|
||||
{
|
||||
private final ServletContextHandler _context;
|
||||
|
@ -1579,4 +1632,45 @@ public class ServletContextHandler extends ContextHandler
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean that is added to the ServletContextHandler to start all of the
|
||||
* ServletContainerInitializers by starting their corresponding
|
||||
* ServletContainerInitializerHolders when this bean is itself started.
|
||||
* Note that the SCIs will be started in order of addition.
|
||||
*/
|
||||
public static class ServletContainerInitializerStarter extends ContainerLifeCycle implements ServletContainerInitializerCaller
|
||||
{
|
||||
public void addServletContainerInitializerHolders(ServletContainerInitializerHolder... holders)
|
||||
{
|
||||
for (ServletContainerInitializerHolder holder:holders)
|
||||
addBean(holder, true);
|
||||
}
|
||||
|
||||
public Collection<ServletContainerInitializerHolder> getServletContainerInitializerHolders()
|
||||
{
|
||||
return getContainedBeans(ServletContainerInitializerHolder.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Starting SCIs");
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
//remove all of the non-programmatic holders
|
||||
Collection<ServletContainerInitializerHolder> holders = getServletContainerInitializerHolders();
|
||||
for (ServletContainerInitializerHolder h : holders)
|
||||
{
|
||||
if (h.getSource().getOrigin() != Source.Origin.EMBEDDED)
|
||||
removeBean(h);
|
||||
}
|
||||
super.doStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.servlet.Source.Origin;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ServletContainerInitializerHolderTest
|
||||
{
|
||||
public static final String[] EMPTY_ARRAY = {};
|
||||
|
||||
static class SimpleSCI implements ServletContainerInitializer
|
||||
{
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
|
||||
{
|
||||
ctx.setAttribute("SimpleSCI-onStartup", Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassNoArgs() throws Exception
|
||||
{
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(SimpleSCI.class);
|
||||
assertEquals(Source.EMBEDDED, holder.getSource());
|
||||
assertEquals("ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[],applicable=[],annotated=[]}", holder.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassWithStartupClasses() throws Exception
|
||||
{
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(SimpleSCI.class, Integer.class);
|
||||
assertEquals(Source.EMBEDDED, holder.getSource());
|
||||
assertEquals(SimpleSCI.class, holder.getHeldClass());
|
||||
assertEquals("ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[java.lang.Integer],applicable=[],annotated=[]}", holder.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstanceWithStartupClasses() throws Exception
|
||||
{
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(new SimpleSCI(), Integer.class);
|
||||
assertEquals(Source.EMBEDDED, holder.getSource());
|
||||
assertEquals(SimpleSCI.class, holder.getHeldClass());
|
||||
assertEquals("ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[java.lang.Integer],applicable=[],annotated=[]}", holder.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstanceWithStartupClassesAndSource() throws Exception
|
||||
{
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(new Source(Origin.ANNOTATION, null), new SimpleSCI(), Integer.class);
|
||||
assertEquals(Origin.ANNOTATION, holder.getSource().getOrigin());
|
||||
assertEquals(SimpleSCI.class, holder.getHeldClass());
|
||||
assertEquals("ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[java.lang.Integer],applicable=[],annotated=[]}", holder.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() throws Exception
|
||||
{
|
||||
//test that the stringified ServletContainerInitializerHolder is backward compatible with what was generated by the old ContainerInitializer
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(SimpleSCI.class);
|
||||
assertEquals("ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[],applicable=[],annotated=[]}", holder.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromString() throws Exception
|
||||
{
|
||||
//test for backward compatibility of string format
|
||||
String sci0 = "ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[],applicable=[],annotated=[]}";
|
||||
ServletContainerInitializerHolder holder = ServletContainerInitializerHolder.fromString(Thread.currentThread().getContextClassLoader(), sci0);
|
||||
assertEquals(sci0, holder.toString());
|
||||
|
||||
//test with no classes
|
||||
String sci1 = "ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[]}";
|
||||
String sci1Expected = "ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[],applicable=[],annotated=[]}";
|
||||
holder = ServletContainerInitializerHolder.fromString(Thread.currentThread().getContextClassLoader(), sci1);
|
||||
assertEquals(sci1Expected, holder.toString());
|
||||
|
||||
//test with some startup classes
|
||||
String sci2 = "ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[java.lang.String, java.lang.Integer]}";
|
||||
holder = ServletContainerInitializerHolder.fromString(Thread.currentThread().getContextClassLoader(), sci2);
|
||||
|
||||
final Matcher matcher2 = ServletContainerInitializerHolder.__pattern.matcher(holder.toString());
|
||||
matcher2.matches();
|
||||
assertEquals("org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI", matcher2.group(1));
|
||||
assertThat(StringUtil.arrayFromString("[java.lang.String, java.lang.Integer]"), arrayContainingInAnyOrder(StringUtil.arrayFromString(matcher2.group(2))));
|
||||
assertThat(EMPTY_ARRAY, arrayContainingInAnyOrder(StringUtil.arrayFromString(matcher2.group(4))));
|
||||
assertThat(EMPTY_ARRAY, arrayContainingInAnyOrder(StringUtil.arrayFromString(matcher2.group(6))));
|
||||
|
||||
//test with old format with startup classes
|
||||
String sci3 = "ContainerInitializer{org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI,interested=[java.lang.String, java.lang.Integer],applicable=[java.lang.Boolean],annotated=[java.lang.Long]}";
|
||||
holder = ServletContainerInitializerHolder.fromString(Thread.currentThread().getContextClassLoader(), sci3);
|
||||
final Matcher matcher3 = ServletContainerInitializerHolder.__pattern.matcher(holder.toString());
|
||||
matcher3.matches();
|
||||
assertEquals("org.eclipse.jetty.servlet.ServletContainerInitializerHolderTest$SimpleSCI", matcher3.group(1));
|
||||
assertThat(StringUtil.arrayFromString("[java.lang.String, java.lang.Integer, java.lang.Boolean, java.lang.Long]"), arrayContainingInAnyOrder(StringUtil.arrayFromString(matcher3.group(2))));
|
||||
assertThat(EMPTY_ARRAY, arrayContainingInAnyOrder(StringUtil.arrayFromString(matcher3.group(4))));
|
||||
assertThat(EMPTY_ARRAY, arrayContainingInAnyOrder(StringUtil.arrayFromString(matcher3.group(6))));
|
||||
}
|
||||
}
|
|
@ -16,9 +16,11 @@ package org.eclipse.jetty.servlet;
|
|||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.EventListener;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -74,6 +76,7 @@ import org.eclipse.jetty.server.handler.HandlerList;
|
|||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler.ServletContainerInitializerStarter;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.Decorator;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
|
@ -1541,6 +1544,83 @@ public class ServletContextHandlerTest
|
|||
String response = _connector.getResponse(request.toString());
|
||||
assertThat("Response", response, containsString("Test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddServletContainerInitializer() throws Exception
|
||||
{
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
final AtomicBoolean called = new AtomicBoolean();
|
||||
context.addServletContainerInitializer(new ServletContainerInitializer()
|
||||
{
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
|
||||
{
|
||||
called.set(true);
|
||||
}
|
||||
});
|
||||
|
||||
_server.setHandler(context);
|
||||
_server.start();
|
||||
ServletContainerInitializerStarter starter = context.getBean(ServletContainerInitializerStarter.class);
|
||||
assertNotNull(starter);
|
||||
Collection<ServletContainerInitializerHolder> holders = starter.getContainedBeans(ServletContainerInitializerHolder.class);
|
||||
assertEquals(1, holders.size());
|
||||
assertTrue(called.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddServletContainerInitializerWithArgs() throws Exception
|
||||
{
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
|
||||
final Set<Class<?>> onStartupClasses = new HashSet<>();
|
||||
context.addServletContainerInitializer(new ServletContainerInitializer()
|
||||
{
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
|
||||
{
|
||||
onStartupClasses.addAll(c);
|
||||
}
|
||||
}, HelloServlet.class, TestServlet.class);
|
||||
|
||||
_server.setHandler(context);
|
||||
_server.start();
|
||||
|
||||
ServletContainerInitializerStarter starter = context.getBean(ServletContainerInitializerStarter.class);
|
||||
assertNotNull(starter);
|
||||
Collection<ServletContainerInitializerHolder> holders = starter.getContainedBeans(ServletContainerInitializerHolder.class);
|
||||
assertEquals(1, holders.size());
|
||||
assertThat(onStartupClasses, Matchers.containsInAnyOrder(HelloServlet.class, TestServlet.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddServletContainerInitializerHolder() throws Exception
|
||||
{
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
|
||||
final Set<Class<?>> onStartupClasses = new HashSet<>();
|
||||
ServletContainerInitializerHolder holder = new ServletContainerInitializerHolder(Source.EMBEDDED,
|
||||
new ServletContainerInitializer()
|
||||
{
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException
|
||||
{
|
||||
onStartupClasses.addAll(c);
|
||||
}
|
||||
}, HelloServlet.class, TestServlet.class);
|
||||
|
||||
context.addServletContainerInitializer(holder);
|
||||
_server.setHandler(context);
|
||||
_server.start();
|
||||
ServletContainerInitializerStarter starter = context.getBean(ServletContainerInitializerStarter.class);
|
||||
assertNotNull(starter);
|
||||
Collection<ServletContainerInitializerHolder> holders = starter.getContainedBeans(ServletContainerInitializerHolder.class);
|
||||
assertEquals(1, holders.size());
|
||||
assertThat(onStartupClasses, Matchers.containsInAnyOrder(HelloServlet.class, TestServlet.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlerBeforeServletHandler() throws Exception
|
||||
|
|
Loading…
Reference in New Issue