View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.plus.annotation;
20  
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  import java.util.concurrent.TimeUnit;
28  import java.util.regex.Matcher;
29  import java.util.regex.Pattern;
30  
31  import javax.servlet.ServletContainerInitializer;
32  
33  import org.eclipse.jetty.util.ConcurrentHashSet;
34  import org.eclipse.jetty.util.Loader;
35  import org.eclipse.jetty.util.StringUtil;
36  import org.eclipse.jetty.util.log.Log;
37  import org.eclipse.jetty.util.log.Logger;
38  import org.eclipse.jetty.webapp.WebAppContext;
39  
40  public class ContainerInitializer
41  {
42      private static final Logger LOG = Log.getLogger(ContainerInitializer.class);
43      
44      final protected ServletContainerInitializer _target;
45      final protected Class<?>[] _interestedTypes;
46      final protected Set<String> _applicableTypeNames = new ConcurrentHashSet<String>();
47      final protected Set<String> _annotatedTypeNames = new ConcurrentHashSet<String>();
48  
49  
50      public ContainerInitializer (ServletContainerInitializer target, Class<?>[] classes)
51      {
52          _target = target;
53          _interestedTypes = classes;
54      }
55      
56      public ContainerInitializer (ClassLoader loader, String toString)
57      {
58          Matcher m = Pattern.compile("ContainerInitializer\\{(.*),interested=(.*),applicable=(.*),annotated=(.*)\\}").matcher(toString);
59          if (!m.matches())
60              throw new IllegalArgumentException(toString);
61  
62          try
63          {
64              _target = (ServletContainerInitializer)loader.loadClass(m.group(1)).newInstance();
65              String[] interested = StringUtil.arrayFromString(m.group(2));
66              _interestedTypes = new Class<?>[interested.length];
67              for (int i=0;i<interested.length;i++)
68                  _interestedTypes[i]=loader.loadClass(interested[i]);
69              for (String s:StringUtil.arrayFromString(m.group(3)))
70                  _applicableTypeNames.add(s);
71              for (String s:StringUtil.arrayFromString(m.group(4)))
72                  _annotatedTypeNames.add(s);
73          }
74          catch(Exception e)
75          {
76              throw new IllegalArgumentException(toString, e);
77          }
78      }
79      
80      public ServletContainerInitializer getTarget ()
81      {
82          return _target;
83      }
84  
85      public Class[] getInterestedTypes ()
86      {
87          return _interestedTypes;
88      }
89  
90  
91      /**
92       * A class has been found that has an annotation of interest
93       * to this initializer.
94       * @param className the class name to add
95       */
96      public void addAnnotatedTypeName (String className)
97      {
98          _annotatedTypeNames.add(className);
99      }
100 
101     public Set<String> getAnnotatedTypeNames ()
102     {
103         return Collections.unmodifiableSet(_annotatedTypeNames);
104     }
105 
106     public void addApplicableTypeName (String className)
107     {
108         _applicableTypeNames.add(className);
109     }
110 
111     public Set<String> getApplicableTypeNames ()
112     {
113         return Collections.unmodifiableSet(_applicableTypeNames);
114     }
115 
116 
117     public void callStartup(WebAppContext context)
118     throws Exception
119     {
120         if (_target != null)
121         {
122             Set<Class<?>> classes = new HashSet<Class<?>>();
123 
124             ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
125             Thread.currentThread().setContextClassLoader(context.getClassLoader());
126 
127             try
128             {
129                 for (String s : _applicableTypeNames)
130                     classes.add(Loader.loadClass(context.getClass(), s));
131 
132                 context.getServletContext().setExtendedListenerTypes(true);
133                 if (LOG.isDebugEnabled())
134                 {
135                     long start = System.nanoTime();
136                     _target.onStartup(classes, context.getServletContext());
137                     LOG.debug("ContainerInitializer {} called in {}ms", _target.getClass().getName(), TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS));
138                 }
139                 else
140                     _target.onStartup(classes, context.getServletContext());
141             }
142             finally
143             { 
144                 context.getServletContext().setExtendedListenerTypes(false);
145                 Thread.currentThread().setContextClassLoader(oldLoader);
146             }
147         }
148     }
149 
150     public String toString()
151     {
152         List<String> interested = Collections.emptyList();
153         if (_interestedTypes != null)
154         {
155             interested = new ArrayList<>(_interestedTypes.length);
156             for (Class<?> c : _interestedTypes)
157                 interested.add(c.getName());
158         }
159 
160         return String.format("ContainerInitializer{%s,interested=%s,applicable=%s,annotated=%s}",_target.getClass().getName(),interested,_applicableTypeNames,_annotatedTypeNames);
161     }
162 
163     public void resolveClasses(WebAppContext context, Map<String, Set<String>> classMap) 
164     {
165         //We have already found the classes that directly have an annotation that was in the HandlesTypes
166         //annotation of the ServletContainerInitializer. For each of those classes, walk the inheritance
167         //hierarchy to find classes that extend or implement them.
168         Set<String> annotatedClassNames = getAnnotatedTypeNames();
169         if (annotatedClassNames != null && !annotatedClassNames.isEmpty())
170         {
171             for (String name : annotatedClassNames)
172             {
173                 //add the class that has the annotation
174                 addApplicableTypeName(name);
175 
176                 //find and add the classes that inherit the annotation               
177                 addInheritedTypes(classMap, (Set<String>)classMap.get(name));
178             }
179         }
180 
181 
182         //Now we need to look at the HandlesTypes classes that were not annotations. We need to
183         //find all classes that extend or implement them.
184         if (getInterestedTypes() != null)
185         {
186             for (Class<?> c : getInterestedTypes())
187             {
188                 if (!c.isAnnotation())
189                 {
190                     //find and add the classes that implement or extend the class.
191                     //but not including the class itself
192                     addInheritedTypes(classMap, (Set<String>)classMap.get(c.getName()));
193                 }
194             }
195         }
196     }
197 
198     private void addInheritedTypes(Map<String, Set<String>> classMap,Set<String> names)
199     {
200         if (names == null || names.isEmpty())
201             return;
202 
203         for (String s : names)
204         {
205             //add the name of the class
206             addApplicableTypeName(s);
207 
208             //walk the hierarchy and find all types that extend or implement the class
209             addInheritedTypes(classMap, (Set<String>)classMap.get(s));
210         }
211     }
212 }