View Javadoc

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