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.boot;
20  
21  import java.io.File;
22  import java.net.URL;
23  import java.util.ArrayList;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.StringTokenizer;
27  import java.util.TreeMap;
28  import java.util.regex.Pattern;
29  
30  import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
31  import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  import org.eclipse.jetty.util.resource.Resource;
35  import org.eclipse.jetty.util.resource.ResourceCollection;
36  import org.eclipse.jetty.webapp.WebAppContext;
37  import org.eclipse.jetty.webapp.WebInfConfiguration;
38  import org.osgi.framework.Bundle;
39  import org.osgi.framework.FrameworkUtil;
40  
41  
42  
43  /**
44   * OSGiWebInfConfiguration
45   *
46   * Handle adding resources found in bundle fragments, and add them into the 
47   */
48  public class OSGiWebInfConfiguration extends WebInfConfiguration
49  {
50      private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
51      
52      
53      public static final String CONTAINER_BUNDLE_PATTERN = "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern";
54      
55      /* ------------------------------------------------------------ */
56      /** 
57       * Check to see if there have been any bundle symbolic names added of bundles that should be
58       * regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
59       * This can be defined in:
60       * <ol>
61       *  <li>SystemProperty SYS_PROP_TLD_BUNDLES</li>
62       *  <li>DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN</li>
63       *  </ol>
64       *  
65       *  We also allow individual bundles to specify particular bundles that might include TLDs via the Require-Tlds
66       *  MANIFEST.MF header. This is processed in the TagLibOSGiConfiguration class.
67       *  
68       * @see org.eclipse.jetty.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
69       */
70      @Override
71      public void preConfigure(final WebAppContext context) throws Exception
72      {
73          super.preConfigure(context);
74          
75          //Check to see if there have been any bundle symbolic names added of bundles that should be
76          //regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
77          //This can be defined in:
78          // 1. SystemProperty SYS_PROP_TLD_BUNDLES
79          // 2. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN
80          String tmp = (String)context.getAttribute(CONTAINER_BUNDLE_PATTERN);
81          Pattern pattern = (tmp==null?null:Pattern.compile(tmp));
82          List<String> names = new ArrayList<String>();
83          tmp = System.getProperty("org.eclipse.jetty.osgi.tldbundles");
84          if (tmp != null)
85          {
86              StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false);
87              while (tokenizer.hasMoreTokens())
88                  names.add(tokenizer.nextToken());
89          }
90  
91          HashSet<Resource> matchingResources = new HashSet<Resource>();
92          if ( !names.isEmpty() || pattern != null)
93          {
94              Bundle[] bundles = FrameworkUtil.getBundle(OSGiWebInfConfiguration.class).getBundleContext().getBundles();
95             
96              for (Bundle bundle : bundles)
97              {
98                  if (pattern != null)
99                  {
100                     // if bundle symbolic name matches the pattern
101                     if (pattern.matcher(bundle.getSymbolicName()).matches())
102                     {
103                         //get the file location of the jar and put it into the list of container jars that will be scanned for stuff (including tlds)
104                         matchingResources.addAll(getBundleAsResource(bundle));
105                     }
106                 }               
107                 if (names != null)
108                 {
109                     //if there is an explicit bundle name, then check if it matches
110                     if (names.contains(bundle.getSymbolicName()))
111                         matchingResources.addAll(getBundleAsResource(bundle));
112                 }
113             }
114         }
115         
116         for (Resource r:matchingResources)
117         {
118             context.getMetaData().addContainerResource(r);
119         }
120     }
121     
122     
123     /* ------------------------------------------------------------ */
124     /** 
125      * Consider the fragment bundles associated with the bundle of the webapp being deployed.
126      * 
127      * 
128      * @see org.eclipse.jetty.webapp.WebInfConfiguration#findJars(org.eclipse.jetty.webapp.WebAppContext)
129      */
130     @Override
131     protected List<Resource> findJars (WebAppContext context) 
132     throws Exception
133     {
134         List<Resource> mergedResources = new ArrayList<Resource>();
135         //get jars from WEB-INF/lib if there are any
136         List<Resource> webInfJars = super.findJars(context);
137         if (webInfJars != null)
138             mergedResources.addAll(webInfJars);
139         
140         //add fragment jars as if in WEB-INF/lib of the associated webapp
141         Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
142         for (Bundle frag : fragments)
143         {
144             File fragFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag);
145             mergedResources.add(Resource.newResource(fragFile.toURI()));  
146         }
147         
148         return mergedResources;
149     }
150     
151     /* ------------------------------------------------------------ */
152     /** 
153      * Allow fragments to supply some resources that are added to the baseResource of the webapp.
154      * 
155      * The resources can be either prepended or appended to the baseResource.
156      * 
157      * @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
158      */
159     @Override
160     public void configure(WebAppContext context) throws Exception
161     {
162         TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
163         TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
164              
165         Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
166         if (bundle != null)
167         {
168             //TODO anything we need to do to improve PackageAdminServiceTracker?
169             Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(bundle);
170             if (fragments != null && fragments.length != 0)
171             {
172                 // sorted extra resource base found in the fragments.
173                 // the resources are either overriding the resourcebase found in the
174                 // web-bundle
175                 // or appended.
176                 // amongst each resource we sort them according to the alphabetical
177                 // order
178                 // of the name of the internal folder and the symbolic name of the
179                 // fragment.
180                 // this is useful to make sure that the lookup path of those
181                 // resource base defined by fragments is always the same.
182                 // This natural order could be abused to define the order in which
183                 // the base resources are
184                 // looked up.
185                 for (Bundle frag : fragments)
186                 {
187                     String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
188                     String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
189                     if (fragFolder != null)
190                     {
191                         URL fragUrl = frag.getEntry(fragFolder);
192                         if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder
193                                                                                   + " inside "
194                                                                                   + " the fragment '"
195                                                                                   + frag.getSymbolicName()
196                                                                                   + "'"); }
197                         fragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(fragUrl);
198                         String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
199                         appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
200                     }
201                     if (patchFragFolder != null)
202                     {
203                         URL patchFragUrl = frag.getEntry(patchFragFolder);
204                         if (patchFragUrl == null)
205                         { 
206                             throw new IllegalArgumentException("Unable to locate " + patchFragUrl
207                                                                + " inside fragment '"+frag.getSymbolicName()+ "'"); 
208                         }
209                         patchFragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(patchFragUrl);
210                         String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
211                         patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
212                     }
213                 }
214                 if (!appendedResourcesPath.isEmpty())
215                     context.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
216             }
217         }
218         
219         super.configure(context);
220 
221         // place the patch resources at the beginning of the contexts's resource base
222         if (!patchResourcesPath.isEmpty())
223         {
224             Resource[] resources = new Resource[1+patchResourcesPath.size()];
225             ResourceCollection mergedResources = new ResourceCollection (patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]));
226             System.arraycopy(patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]), 0, resources, 0, patchResourcesPath.size());
227             resources[resources.length-1] = context.getBaseResource();
228             context.setBaseResource(new ResourceCollection(resources));
229         }
230     }
231 
232     
233     /* ------------------------------------------------------------ */
234     /**
235     * Resolves the bundle. Usually that would be a single URL per bundle. But we do some more work if there are jars
236     * embedded in the bundle.
237     */
238     private  List<Resource> getBundleAsResource(Bundle bundle)
239     throws Exception
240     {
241         List<Resource> resources = new ArrayList<Resource>();
242 
243         File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
244         if (file.isDirectory())
245         {
246             for (File f : file.listFiles())
247             {
248                 if (f.getName().endsWith(".jar") && f.isFile())
249                 {
250                     resources.add(Resource.newResource(f));
251                 }
252                 else if (f.isDirectory() && f.getName().equals("lib"))
253                 {
254                     for (File f2 : file.listFiles())
255                     {
256                         if (f2.getName().endsWith(".jar") && f2.isFile())
257                         {
258                             resources.add(Resource.newResource(f));
259                         }
260                     }
261                 }
262             }
263             resources.add(Resource.newResource(file)); //TODO really???
264         }
265         else
266         {
267             resources.add(Resource.newResource(file));
268         }
269         
270         return resources;
271     }
272 }