View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.io.File;
22  import java.net.URI;
23  import java.net.URL;
24  import java.util.Comparator;
25  import java.util.Enumeration;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.StringTokenizer;
31  import java.util.TreeSet;
32  
33  import org.eclipse.jetty.annotations.ClassNameResolver;
34  import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
35  import org.eclipse.jetty.util.resource.Resource;
36  import org.osgi.framework.Bundle;
37  import org.osgi.framework.Constants;
38  
39  /**
40   * 
41   */
42  public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationParser
43  {
44      private Set<URI> _alreadyParsed = new HashSet<URI>();
45      
46      private Map<URI,Bundle> _uriToBundle = new HashMap<URI, Bundle>();
47      private Map<Bundle,Resource> _resourceToBundle = new HashMap<Bundle,Resource>();
48      private Map<Bundle,URI> _bundleToUri = new HashMap<Bundle, URI>();
49      
50      /**
51       * Keep track of a jetty URI Resource and its associated OSGi bundle.
52       * @param uri
53       * @param bundle
54       * @throws Exception 
55       */
56      protected Resource indexBundle(Bundle bundle) throws Exception
57      {
58          File bundleFile = BundleFileLocatorHelper.DEFAULT.getBundleInstallLocation(bundle);
59          Resource resource = Resource.newResource(bundleFile.toURI());
60          URI uri = resource.getURI();
61          _uriToBundle.put(uri,bundle);
62          _bundleToUri.put(bundle,uri);
63          _resourceToBundle.put(bundle,resource);
64          return resource;
65      }
66      protected URI getURI(Bundle bundle)
67      {
68          return _bundleToUri.get(bundle);
69      }
70      protected Resource getResource(Bundle bundle)
71      {
72          return _resourceToBundle.get(bundle);
73      }
74      /**
75       * 
76       */
77      @Override
78      public void parse (URI[] uris, ClassNameResolver resolver)
79      throws Exception
80      {
81          for (URI uri : uris)
82          {
83              Bundle associatedBundle = _uriToBundle.get(uri);
84              if (associatedBundle == null)
85              {
86                  if (!_alreadyParsed.add(uri))
87                  {
88                      continue;
89                  }
90                  //a jar in WEB-INF/lib or the WEB-INF/classes
91                  //use the behavior of the super class for a standard jar.
92                  super.parse(new URI[] {uri},resolver);
93              }
94              else
95              {
96                  parse(associatedBundle,resolver);
97              }
98          }
99      }
100     
101     protected void parse(Bundle bundle, ClassNameResolver resolver)
102     throws Exception
103     {
104         URI uri = _bundleToUri.get(bundle);
105         if (!_alreadyParsed.add(uri))
106         {
107             return;
108         }
109         
110         String bundleClasspath = (String)bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);
111         if (bundleClasspath == null)
112         {
113             bundleClasspath = ".";
114         }
115         //order the paths first by the number of tokens in the path second alphabetically.
116         TreeSet<String> paths = new TreeSet<String>(
117                 new Comparator<String>()
118                 {
119                     public int compare(String o1, String o2)
120                     {
121                         int paths1 = new StringTokenizer(o1,"/",false).countTokens();
122                         int paths2 = new StringTokenizer(o2,"/",false).countTokens();
123                         if (paths1 == paths2)
124                         {
125                             return o1.compareTo(o2);
126                         }
127                         return paths2 - paths1;
128                     }
129                 });
130         boolean hasDotPath = false;
131         StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, ",;", false);
132         while (tokenizer.hasMoreTokens())
133         {
134             String token = tokenizer.nextToken().trim();
135             if (!token.startsWith("/"))
136             {
137                 token = "/" + token;
138             }
139             if (token.equals("/."))
140             {
141                 hasDotPath = true;
142             }
143             else if (!token.endsWith(".jar") && !token.endsWith("/"))
144             {
145                 paths.add(token+"/");
146             }
147             else
148             {
149                 paths.add(token);
150             }
151         }
152         //support the development environment: maybe the classes are inside bin or target/classes
153         //this is certainly not useful in production.
154         //however it makes our life so much easier during development.
155         if (bundle.getEntry("/.classpath") != null)
156         {
157             if (bundle.getEntry("/bin/") != null)
158             {
159                 paths.add("/bin/");
160             }
161             else if (bundle.getEntry("/target/classes/") != null)
162             {
163                 paths.add("/target/classes/");
164             }
165         }
166         Enumeration classes = bundle.findEntries("/","*.class",true);
167         if (classes == null)
168         {
169             return;
170         }
171         while (classes.hasMoreElements())
172         {
173             URL classUrl = (URL) classes.nextElement();
174             String path = classUrl.getPath();
175             //remove the longest path possible:
176             String name = null;
177             for (String prefixPath : paths)
178             {
179                 if (path.startsWith(prefixPath))
180                 {
181                     name = path.substring(prefixPath.length());
182                     break;
183                 }
184             }
185             if (name == null && hasDotPath)
186             {
187                 //remove the starting '/'
188                 name = path.substring(1);
189             }
190             //transform into a classname to pass to the resolver
191             String shortName =  name.replace('/', '.').substring(0,name.length()-6);
192             if ((resolver == null)|| (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
193                 scanClass(classUrl.openStream());
194         }
195     }
196     
197 }