Unify the handling of ServletContainerInitializers (#5959)

* Unify ServletContainerInitializer handling.

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2021-02-19 10:20:27 +01:00 committed by GitHub
parent ddec50eb2e
commit 3a32a80b9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 952 additions and 117 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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=[]"));
}
}

View File

@ -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);

View File

@ -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)
{

View File

@ -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.

View File

@ -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());
}
}
}

View File

@ -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();
}
}
}

View File

@ -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))));
}
}

View File

@ -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