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.osgi.annotations;
20  
21  import java.util.HashSet;
22  import java.util.Set;
23  
24  import org.eclipse.jetty.annotations.AnnotationParser.Handler;
25  import org.eclipse.jetty.annotations.ClassNameResolver;
26  import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
27  import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.log.Logger;
30  import org.eclipse.jetty.util.resource.Resource;
31  import org.eclipse.jetty.webapp.WebAppContext;
32  import org.osgi.framework.Bundle;
33  import org.osgi.framework.Constants;
34  
35  /**
36   * Extend the AnnotationConfiguration to support OSGi:
37   * Look for annotations inside WEB-INF/lib and also in the fragments and required bundles.
38   * Discover them using a scanner adapted to OSGi instead of the jarscanner. 
39   */
40  public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration
41  {
42      private static final Logger LOG = Log.getLogger(org.eclipse.jetty.annotations.AnnotationConfiguration.class);
43      
44      public class BundleParserTask extends ParserTask
45      {
46          
47          public BundleParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource, ClassNameResolver resolver)
48          {
49              super(parser, handlers, resource, resolver);
50          }
51  
52          public Void call() throws Exception
53          {
54              if (_parser != null)
55              {
56                  org.eclipse.jetty.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.osgi.annotations.AnnotationParser)_parser;
57                  Bundle bundle = osgiAnnotationParser.getBundle(_resource);
58                  if (_stat != null)
59                      _stat.start();
60                  osgiAnnotationParser.parse(_handlers, bundle,  _resolver); 
61                  if (_stat != null)
62                      _stat.end();
63              }
64              return null;
65          }
66      }
67      
68      
69      /**
70       * This parser scans the bundles using the OSGi APIs instead of assuming a jar.
71       */
72      @Override
73      protected org.eclipse.jetty.annotations.AnnotationParser createAnnotationParser()
74      {
75          return new AnnotationParser();
76      }
77      
78      /**
79       * Here is the order in which jars and osgi artifacts are scanned for discoverable annotations.
80       * <ol>
81       * <li>The container jars are scanned.</li>
82       * <li>The WEB-INF/classes are scanned</li>
83       * <li>The osgi fragment to the web bundle are parsed.</li>
84       * <li>The WEB-INF/lib are scanned</li>
85       * <li>The required bundles are parsed</li>
86       * </ol>
87       */
88      @Override
89      public void parseWebInfLib (WebAppContext context, org.eclipse.jetty.annotations.AnnotationParser parser)
90      throws Exception
91      {
92          AnnotationParser oparser = (AnnotationParser)parser;
93          
94          Bundle webbundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
95          Bundle[] fragAndRequiredBundles = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(webbundle);
96          if (fragAndRequiredBundles != null)
97          {
98              //index and scan fragments
99              for (Bundle bundle : fragAndRequiredBundles)
100             {
101                 Resource bundleRes = oparser.indexBundle(bundle);
102                 if (!context.getMetaData().getWebInfJars().contains(bundleRes))
103                 {
104                     context.getMetaData().addWebInfJar(bundleRes);
105                 }
106                 
107                 if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null)
108                 {
109                     //a fragment indeed:
110                     parseFragmentBundle(context,oparser,webbundle,bundle);
111                 }
112             }
113         }
114         //scan ourselves
115         oparser.indexBundle(webbundle);
116         parseWebBundle(context,oparser,webbundle);
117         
118         //scan the WEB-INF/lib
119         super.parseWebInfLib(context,parser);
120         if (fragAndRequiredBundles != null)
121         {
122             //scan the required bundles
123             for (Bundle requiredBundle : fragAndRequiredBundles)
124             {
125                 if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null)
126                 {
127                     //a bundle indeed:
128                     parseRequiredBundle(context,oparser,webbundle,requiredBundle);
129                 }
130             }
131         }
132     }
133     
134     /**
135      * Scan a fragment bundle for servlet annotations
136      * @param context The webapp context
137      * @param parser The parser
138      * @param webbundle The current webbundle
139      * @param fragmentBundle The OSGi fragment bundle to scan
140      * @throws Exception
141      */
142     protected void parseFragmentBundle(WebAppContext context, AnnotationParser parser,
143             Bundle webbundle, Bundle fragmentBundle) throws Exception
144     {
145         parseBundle(context,parser,webbundle,fragmentBundle);
146     }
147     
148     /**
149      * Scan a bundle required by the webbundle for servlet annotations
150      * @param context The webapp context
151      * @param parser The parser
152      * @param webbundle The current webbundle
153      * @param fragmentBundle The OSGi required bundle to scan
154      * @throws Exception
155      */
156     protected void parseWebBundle(WebAppContext context, AnnotationParser parser, Bundle webbundle)
157     throws Exception
158     {
159         parseBundle(context,parser,webbundle,webbundle);
160     }
161     
162     /**
163      * Scan a bundle required by the webbundle for servlet annotations
164      * @param context The webapp context
165      * @param parser The parser
166      * @param webbundle The current webbundle
167      * @param fragmentBundle The OSGi required bundle to scan
168      * @throws Exception
169      */
170     protected void parseRequiredBundle(WebAppContext context, AnnotationParser parser,
171             Bundle webbundle, Bundle requiredBundle) throws Exception
172     {
173         parseBundle(context,parser,webbundle,requiredBundle);
174     }
175     
176     protected void parseBundle(WebAppContext context, AnnotationParser parser,
177                                Bundle webbundle, Bundle bundle) throws Exception
178                                {
179 
180         Resource bundleRes = parser.getResource(bundle);  
181         Set<Handler> handlers = new HashSet<Handler>();
182         handlers.addAll(_discoverableAnnotationHandlers);
183         if (_classInheritanceHandler != null)
184             handlers.add(_classInheritanceHandler);
185         handlers.addAll(_containerInitializerAnnotationHandlers);
186 
187         ClassNameResolver classNameResolver = createClassNameResolver(context);
188         if (_parserTasks != null)
189         {
190             BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes, classNameResolver);
191             _parserTasks.add(task);
192             if (LOG.isDebugEnabled())
193                 task.setStatistic(new TimeStatistic());
194         }
195     }
196     
197     /**
198      * Returns the same classname resolver than for the webInfjar scanner
199      * @param context
200      * @return
201      */
202     protected ClassNameResolver createClassNameResolver(final WebAppContext context)
203     {
204         return createClassNameResolver(context,true,false,false,false);
205     }
206     
207     protected ClassNameResolver createClassNameResolver(final WebAppContext context,
208             final boolean excludeSysClass, final boolean excludeServerClass, final boolean excludeEverythingElse,
209             final boolean overrideIsParenLoaderIsPriority)
210     {
211         return new ClassNameResolver ()
212         {
213             public boolean isExcluded (String name)
214             {
215                 if (context.isSystemClass(name)) return excludeSysClass;
216                 if (context.isServerClass(name)) return excludeServerClass;
217                 return excludeEverythingElse;
218             }
219 
220             public boolean shouldOverride (String name)
221             { 
222                 //looking at system classpath
223                 if (context.isParentLoaderPriority())
224                     return overrideIsParenLoaderIsPriority;
225                 return !overrideIsParenLoaderIsPriority;
226             }
227         };
228     }
229 
230 }