View Javadoc

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