blob: e32e45e32e1846c20fd91c0456ca5cb26354c866 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.plus.annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContainerInitializer;
import org.eclipse.jetty.util.ConcurrentHashSet;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
public class ContainerInitializer
{
private static final Logger LOG = Log.getLogger(ContainerInitializer.class);
final protected ServletContainerInitializer _target;
final protected Class<?>[] _interestedTypes;
final protected Set<String> _applicableTypeNames = new ConcurrentHashSet<String>();
final protected Set<String> _annotatedTypeNames = new ConcurrentHashSet<String>();
public ContainerInitializer (ServletContainerInitializer target, Class<?>[] classes)
{
_target = target;
_interestedTypes = classes;
}
public ContainerInitializer (ClassLoader loader, String toString)
{
Matcher m = Pattern.compile("ContainerInitializer\\{(.*),interested=(.*),applicable=(.*),annotated=(.*)\\}").matcher(toString);
if (!m.matches())
throw new IllegalArgumentException(toString);
try
{
_target = (ServletContainerInitializer)loader.loadClass(m.group(1)).newInstance();
String[] interested = StringUtil.arrayFromString(m.group(2));
_interestedTypes = new Class<?>[interested.length];
for (int i=0;i<interested.length;i++)
_interestedTypes[i]=loader.loadClass(interested[i]);
for (String s:StringUtil.arrayFromString(m.group(3)))
_applicableTypeNames.add(s);
for (String s:StringUtil.arrayFromString(m.group(4)))
_annotatedTypeNames.add(s);
}
catch(Exception e)
{
throw new IllegalArgumentException(toString, e);
}
}
public ServletContainerInitializer getTarget ()
{
return _target;
}
public Class[] getInterestedTypes ()
{
return _interestedTypes;
}
/**
* A class has been found that has an annotation of interest
* to this initializer.
* @param className the class name to add
*/
public void addAnnotatedTypeName (String className)
{
_annotatedTypeNames.add(className);
}
public Set<String> getAnnotatedTypeNames ()
{
return Collections.unmodifiableSet(_annotatedTypeNames);
}
public void addApplicableTypeName (String className)
{
_applicableTypeNames.add(className);
}
public Set<String> getApplicableTypeNames ()
{
return Collections.unmodifiableSet(_applicableTypeNames);
}
public void callStartup(WebAppContext context)
throws Exception
{
if (_target != null)
{
Set<Class<?>> classes = new HashSet<Class<?>>();
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
try
{
for (String s : _applicableTypeNames)
classes.add(Loader.loadClass(context.getClass(), s));
context.getServletContext().setExtendedListenerTypes(true);
if (LOG.isDebugEnabled())
{
long start = System.nanoTime();
_target.onStartup(classes, context.getServletContext());
LOG.debug("ContainerInitializer {} called in {}ms", _target.getClass().getName(), TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS));
}
else
_target.onStartup(classes, context.getServletContext());
}
finally
{
context.getServletContext().setExtendedListenerTypes(false);
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
}
public String toString()
{
List<String> interested = Collections.emptyList();
if (_interestedTypes != null)
{
interested = new ArrayList<>(_interestedTypes.length);
for (Class<?> c : _interestedTypes)
interested.add(c.getName());
}
return String.format("ContainerInitializer{%s,interested=%s,applicable=%s,annotated=%s}",_target.getClass().getName(),interested,_applicableTypeNames,_annotatedTypeNames);
}
public void resolveClasses(WebAppContext context, Map<String, Set<String>> classMap)
{
//We have already found the classes that directly have an annotation that was in the HandlesTypes
//annotation of the ServletContainerInitializer. For each of those classes, walk the inheritance
//hierarchy to find classes that extend or implement them.
Set<String> annotatedClassNames = getAnnotatedTypeNames();
if (annotatedClassNames != null && !annotatedClassNames.isEmpty())
{
for (String name : annotatedClassNames)
{
//add the class that has the annotation
addApplicableTypeName(name);
//find and add the classes that inherit the annotation
addInheritedTypes(classMap, (Set<String>)classMap.get(name));
}
}
//Now we need to look at the HandlesTypes classes that were not annotations. We need to
//find all classes that extend or implement them.
if (getInterestedTypes() != null)
{
for (Class<?> c : getInterestedTypes())
{
if (!c.isAnnotation())
{
//find and add the classes that implement or extend the class.
//but not including the class itself
addInheritedTypes(classMap, (Set<String>)classMap.get(c.getName()));
}
}
}
}
private void addInheritedTypes(Map<String, Set<String>> classMap,Set<String> names)
{
if (names == null || names.isEmpty())
return;
for (String s : names)
{
//add the name of the class
addApplicableTypeName(s);
//walk the hierarchy and find all types that extend or implement the class
addInheritedTypes(classMap, (Set<String>)classMap.get(s));
}
}
}