View Javadoc

1   // ========================================================================
2   // Copyright (c) 2009 Intalio, Inc.
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   // The Apache License v2.0 is available at
9   // http://www.opensource.org/licenses/apache2.0.php
10  // You may elect to redistribute this code under either of these licenses. 
11  // Contributors:
12  //    Hugues Malphettes - initial API and implementation
13  // ========================================================================
14  package org.eclipse.jetty.osgi.boot.internal.webapp;
15  
16  import java.io.BufferedInputStream;
17  import java.io.File;
18  import java.io.FileInputStream;
19  import java.io.FileNotFoundException;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.net.URL;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Enumeration;
26  import java.util.HashMap;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.TreeMap;
30  
31  import org.eclipse.jetty.deploy.ContextDeployer;
32  import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
33  import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
34  import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
35  import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
36  import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
37  import org.eclipse.jetty.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
38  import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
39  import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
40  import org.eclipse.jetty.server.handler.ContextHandler;
41  import org.eclipse.jetty.util.IO;
42  import org.eclipse.jetty.util.log.Log;
43  import org.eclipse.jetty.util.log.Logger;
44  import org.eclipse.jetty.util.resource.Resource;
45  import org.eclipse.jetty.util.resource.ResourceCollection;
46  import org.eclipse.jetty.webapp.FragmentConfiguration;
47  import org.eclipse.jetty.webapp.TagLibConfiguration;
48  import org.eclipse.jetty.webapp.WebAppContext;
49  import org.eclipse.jetty.webapp.WebInfConfiguration;
50  import org.eclipse.jetty.xml.XmlConfiguration;
51  import org.osgi.framework.Bundle;
52  import org.osgi.framework.BundleContext;
53  import org.xml.sax.SAXException;
54  
55  /**
56   * Bridges the jetty deployers with the OSGi lifecycle where applications are
57   * managed inside OSGi-bundles.
58   * <p>
59   * This class should be called as a consequence of the activation of a new
60   * service that is a ContextHandler.<br/>
61   * This way the new webapps are exposed as OSGi services.
62   * </p>
63   * <p>
64   * Helper methods to register a bundle that is a web-application or a context.
65   * </p>
66   * Limitations:
67   * <ul>
68   * <li>support for jarred webapps is somewhat limited.</li>
69   * </ul>
70   */
71  public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
72  {
73  
74      private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
75  
76      private static boolean INITIALIZED = false;
77      
78      /**
79       * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
80       * equinox and apache-felix fragment bundles that are specific to an OSGi
81       * implementation should set a different implementation.
82       */
83      public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null;
84      /**
85       * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
86       * equinox and apache-felix fragment bundles that are specific to an OSGi
87       * implementation should set a different implementation.
88       */
89      public static BundleFileLocatorHelper BUNDLE_FILE_LOCATOR_HELPER = null;
90  
91      /**
92       * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
93       * equinox and apache-felix fragment bundles that are specific to an OSGi
94       * implementation should set a different implementation.
95       * <p>
96       * Several of those objects can be added here: For example we could have an optional fragment that setups
97       * a specific implementation of JSF for the whole of jetty-osgi.
98       * </p>
99       */
100     public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
101 
102     /**
103      * this class loader loads the jars inside {$jetty.home}/lib/ext it is meant
104      * as a migration path and for jars that are not OSGi ready. also gives
105      * access to the jsp jars.
106      */
107     // private URLClassLoader _libExtClassLoader;
108 
109     private ServerInstanceWrapper _wrapper;
110 
111     public WebBundleDeployerHelper(ServerInstanceWrapper wrapper)
112     {
113         staticInit();
114         _wrapper = wrapper;
115     }
116 
117     // Inject the customizing classes that might be defined in fragment bundles.
118     public static synchronized void staticInit()
119     {
120         if (!INITIALIZED)
121         {
122             INITIALIZED = true;
123             // setup the custom BundleClassLoaderHelper
124             try
125             {
126                 BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper)Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
127             }
128             catch (Throwable t)
129             {
130                 // System.err.println("support for equinox and felix");
131                 BUNDLE_CLASS_LOADER_HELPER = new DefaultBundleClassLoaderHelper();
132             }
133             // setup the custom FileLocatorHelper
134             try
135             {
136                 BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper)Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
137             }
138             catch (Throwable t)
139             {
140                 // System.err.println("no jsp/jasper support");
141                 BUNDLE_FILE_LOCATOR_HELPER = new DefaultFileLocatorHelper();
142             }
143         }
144     }
145 
146     /**
147      * Deploy a new web application on the jetty server.
148      * 
149      * @param bundle
150      *            The bundle
151      * @param webappFolderPath
152      *            The path to the root of the webapp. Must be a path relative to
153      *            bundle; either an absolute path.
154      * @param contextPath
155      *            The context path. Must start with "/"
156      * @param extraClasspath
157      * @param overrideBundleInstallLocation
158      * @param requireTldBundle The list of bundles's symbolic names that contain
159      * tld files that are required by this WAB.
160      * @param webXmlPath
161      * @param defaultWebXmlPath
162      *            TODO: parameter description
163      * @return The contexthandler created and started
164      * @throws Exception
165      */
166     public WebAppContext registerWebapplication(Bundle bundle,
167             String webappFolderPath, String contextPath, String extraClasspath,
168             String overrideBundleInstallLocation,
169             String requireTldBundle, String webXmlPath,
170             String defaultWebXmlPath, WebAppContext webAppContext) throws Exception
171     {
172         File bundleInstall = overrideBundleInstallLocation == null?BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle):new File(
173                 overrideBundleInstallLocation);
174         File webapp = null;
175         URL baseWebappInstallURL = null;
176         if (webappFolderPath != null && webappFolderPath.length() != 0 && !webappFolderPath.equals("."))
177         {
178             if (webappFolderPath.startsWith("/") || webappFolderPath.startsWith("file:"))
179             {
180                 webapp = new File(webappFolderPath);
181             }
182             else if (bundleInstall != null && bundleInstall.isDirectory())
183             {
184                 webapp = new File(bundleInstall,webappFolderPath);
185             }
186             else if (bundleInstall != null)
187             {
188                 Enumeration<URL> urls = BUNDLE_FILE_LOCATOR_HELPER.findEntries(bundle, webappFolderPath);
189                 if (urls != null && urls.hasMoreElements())
190                 {
191                     baseWebappInstallURL = urls.nextElement();
192                 }
193             }
194         }
195         else
196         {
197             webapp = bundleInstall;
198         }
199         if (baseWebappInstallURL == null && (webapp == null || !webapp.exists()))
200         {
201             throw new IllegalArgumentException("Unable to locate " + webappFolderPath + " inside "
202                     + (bundleInstall != null?bundleInstall.getAbsolutePath():"unlocated bundle '" + bundle.getSymbolicName() + "'"));
203         }
204         if (baseWebappInstallURL == null && webapp != null)
205         {
206             baseWebappInstallURL = webapp.toURI().toURL();
207         }
208         return registerWebapplication(bundle,webappFolderPath,baseWebappInstallURL,contextPath,
209                 extraClasspath,bundleInstall,requireTldBundle,webXmlPath,defaultWebXmlPath,webAppContext);
210     }
211     
212     /* (non-Javadoc)
213      * @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerWebapplication(org.osgi.framework.Bundle, java.lang.String, java.io.File, java.lang.String, java.lang.String, java.io.File, java.lang.String, java.lang.String)
214      */
215     private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp,
216             URL baseWebappInstallURL, String contextPath, String extraClasspath, File bundleInstall,
217             String requireTldBundle, String webXmlPath, String defaultWebXmlPath, WebAppContext context)
218     throws Exception
219     {
220 
221         ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
222         String[] oldServerClasses = null;
223         
224         try
225         {
226             // make sure we provide access to all the jetty bundles by going
227             // through this bundle.
228             OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
229             // configure with access to all jetty classes and also all the classes
230             // that the contributor gives access to.
231             Thread.currentThread().setContextClassLoader(composite);
232 
233             context.setWar(baseWebappInstallURL.toString());
234             context.setContextPath(contextPath);
235             context.setExtraClasspath(extraClasspath);
236 
237             if (webXmlPath != null && webXmlPath.length() != 0)
238             {
239                 File webXml = null;
240                 if (webXmlPath.startsWith("/") || webXmlPath.startsWith("file:/"))
241                 {
242                     webXml = new File(webXmlPath);
243                 }
244                 else
245                 {
246                     webXml = new File(bundleInstall,webXmlPath);
247                 }
248                 if (webXml.exists())
249                 {
250                     context.setDescriptor(webXml.getAbsolutePath());
251                 }
252             }
253 
254             if (defaultWebXmlPath == null || defaultWebXmlPath.length() == 0)
255             {
256                 //use the one defined by the OSGiAppProvider.
257                 defaultWebXmlPath = _wrapper.getOSGiAppProvider().getDefaultsDescriptor();
258             }
259             if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0)
260             {
261                 File defaultWebXml = null;
262                 if (defaultWebXmlPath.startsWith("/") || defaultWebXmlPath.startsWith("file:/"))
263                 {
264                     defaultWebXml = new File(webXmlPath);
265                 }
266                 else
267                 {
268                     defaultWebXml = new File(bundleInstall,defaultWebXmlPath);
269                 }
270                 if (defaultWebXml.exists())
271                 {
272                     context.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
273                 }
274             }
275             
276             //other parameters that might be defines on the OSGiAppProvider:
277             context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
278 
279             configureWebAppContext(context,contributor,requireTldBundle);
280             configureWebappClassLoader(contributor,context,composite);
281 
282             // @see
283             // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
284             // during initialization of the webapp all the jetty packages are
285             // visible
286             // through the webapp classloader.
287             oldServerClasses = context.getServerClasses();
288             context.setServerClasses(null);
289             
290             _wrapper.getOSGiAppProvider().addContext(contributor,pathInBundleToWebApp,context);
291             
292             //support for patch resources. ideally this should be done inside a configurator.
293             List<Resource> patchResources =
294             		(List<Resource>)context.getAttribute(WebInfConfiguration.RESOURCE_URLS+".patch");
295             if (patchResources != null)
296             {
297 	            LinkedList<Resource> resourcesPath = new LinkedList<Resource>();
298 	            //place the patch resources at the beginning of the lookup path.
299 	            resourcesPath.addAll(patchResources);
300 	            //then place the ones from the host web bundle.
301 	            Resource hostResources = context.getBaseResource();
302 	            if (hostResources instanceof ResourceCollection)
303 	            {
304 	            	for (Resource re : ((ResourceCollection)hostResources).getResources())
305 	            	{
306 	            		resourcesPath.add(re);
307 	            	}
308 	            }
309 	            else
310 	            {
311 	            	resourcesPath.add(hostResources);
312 	            }
313 	            
314 	            ResourceCollection rc = new ResourceCollection(resourcesPath.toArray(
315 	                    new Resource[resourcesPath.size()]));
316 	            context.setBaseResource(rc);
317             }
318             
319             return context;
320         }
321         finally
322         {
323             if (context != null && oldServerClasses != null)
324             {
325                 context.setServerClasses(oldServerClasses);
326             }
327             Thread.currentThread().setContextClassLoader(contextCl);
328         }
329 
330     }
331 
332     /* (non-Javadoc)
333      * @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#unregister(org.eclipse.jetty.server.handler.ContextHandler)
334      */
335     public void unregister(ContextHandler contextHandler) throws Exception
336     {
337         _wrapper.getOSGiAppProvider().removeContext(contextHandler);
338     }
339 
340     /* (non-Javadoc)
341      * @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerContext(org.osgi.framework.Bundle, java.lang.String, java.lang.String, java.lang.String)
342      */
343     public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, 
344                 String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler)
345             throws Exception
346     {
347         File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile();
348         if (contextsHome != null)
349         {
350             File prodContextFile = new File(contextsHome,contributor.getSymbolicName() + "/" + contextFileRelativePath);
351             if (prodContextFile.exists())
352             {
353                 return registerContext(contributor,contextFileRelativePath,prodContextFile,extraClasspath,
354                         overrideBundleInstallLocation,requireTldBundle,handler);
355             }
356         }
357         File rootFolder = overrideBundleInstallLocation != null
358             ? Resource.newResource(overrideBundleInstallLocation).getFile()
359             : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor);
360         File contextFile = rootFolder != null?new File(rootFolder,contextFileRelativePath):null;
361         if (contextFile != null && contextFile.exists())
362         {
363             return registerContext(contributor,contextFileRelativePath,contextFile,extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
364         }
365         else
366         {
367             if (contextFileRelativePath.startsWith("./"))
368             {
369                 contextFileRelativePath = contextFileRelativePath.substring(1);
370             }
371             if (!contextFileRelativePath.startsWith("/"))
372             {
373                 contextFileRelativePath = "/" + contextFileRelativePath;
374             }
375             
376             URL contextURL = contributor.getEntry(contextFileRelativePath);
377             if (contextURL != null)
378             {
379                 return registerContext(contributor,contextFileRelativePath,contextURL.openStream(),extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
380             }
381             throw new IllegalArgumentException("Could not find the context " + "file " + contextFileRelativePath + " for the bundle "
382                     + contributor.getSymbolicName() + (overrideBundleInstallLocation != null?" using the install location " + overrideBundleInstallLocation:""));
383         }
384     }
385 
386     /**
387      * This type of registration relies on jetty's complete context xml file.
388      * Context encompasses jndi and all other things. This makes the definition
389      * of the webapp a lot more self-contained.
390      * 
391      * @param webapp
392      * @param contextPath
393      * @param classInBundle
394      * @throws Exception
395      */
396     private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile,
397             String extraClasspath, String overrideBundleInstallLocation,
398             String requireTldBundle, ContextHandler handler) throws Exception
399     {
400         InputStream contextFileInputStream = null;
401         try
402         {
403             contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile));
404             return registerContext(contributor, pathInBundle, contextFileInputStream,
405                     extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
406         }
407         finally
408         {
409             IO.close(contextFileInputStream);
410         }
411     }
412 
413     /**
414      * @param contributor
415      * @param contextFileInputStream
416      * @return The ContextHandler created and registered or null if it did not
417      *         happen.
418      * @throws Exception
419      */
420     private ContextHandler registerContext(Bundle contributor,
421             String pathInsideBundle, InputStream contextFileInputStream,
422             String extraClasspath, String overrideBundleInstallLocation,
423             String requireTldBundle, ContextHandler handler)
424             throws Exception
425     {
426         ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
427         String[] oldServerClasses = null;
428         WebAppContext webAppContext = null;
429         try
430         {
431             // make sure we provide access to all the jetty bundles by going
432             // through this bundle.
433             OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
434             // configure with access to all jetty classes and also all the
435             // classes
436             // that the contributor gives access to.
437             Thread.currentThread().setContextClassLoader(composite);
438             ContextHandler context = createContextHandler(handler, contributor,
439                     contextFileInputStream,extraClasspath,
440                     overrideBundleInstallLocation,requireTldBundle);
441             if (context == null)
442             {
443                 return null;// did not happen
444             }
445 
446             // ok now register this webapp. we checked when we started jetty
447             // that there
448             // was at least one such handler for webapps.
449             //the actual registration must happen via the new Deployment API.
450 //            _ctxtHandler.addHandler(context);
451 
452             configureWebappClassLoader(contributor,context,composite);
453             if (context instanceof WebAppContext)
454             {
455                 webAppContext = (WebAppContext)context;
456                 // @see
457                 // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
458                 oldServerClasses = webAppContext.getServerClasses();
459                 webAppContext.setServerClasses(null);
460             }
461             _wrapper.getOSGiAppProvider().addContext(contributor, pathInsideBundle, context);
462             return context;
463         }
464         finally
465         {
466             if (webAppContext != null)
467             {
468                 webAppContext.setServerClasses(oldServerClasses);
469             }
470             Thread.currentThread().setContextClassLoader(contextCl);
471         }
472 
473     }
474 
475     /**
476      * Applies the properties of WebAppDeployer as defined in jetty.xml.
477      * 
478      * @see {WebAppDeployer#scan} around the comment
479      *      <code>// configure it</code>
480      */
481     protected void configureWebAppContext(ContextHandler wah, Bundle contributor,
482             String requireTldBundle) throws IOException
483     {
484         // rfc66
485         wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
486 
487         //spring-dm-1.2.1 looks for the BundleContext as a different attribute.
488         //not a spec... but if we want to support 
489         //org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
490         //then we need to do this to:
491         wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
492                         contributor.getBundleContext());
493         
494         //also pass the bundle directly. sometimes a bundle does not have a bundlecontext.
495         //it is still useful to have access to the Bundle from the servlet context.
496         wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor);
497         
498         //pass the value of the require tld bundle so that the TagLibOSGiConfiguration
499         //can pick it up.
500         wah.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundle);
501 
502         if (wah instanceof WebAppContext)
503         {
504             if (_wrapper.getOSGiAppProvider().getConfigurationClasses() != null)
505             {
506                 ((WebAppContext)wah).setConfigurationClasses(_wrapper.getOSGiAppProvider().getConfigurationClasses());
507             }
508         }
509         
510         Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(contributor);
511         if (fragments != null && fragments.length != 0)
512         {
513             //sorted extra resource base found in the fragments.
514             //the resources are either overriding the resourcebase found in the web-bundle
515             //or appended.
516             //amongst each resource we sort them according to the alphabetical order
517             //of the name of the internal folder and the symbolic name of the fragment.
518             //this is useful to make sure that the lookup path of those
519             //resource base defined by fragments is always the same.
520             //This natural order could be abused to define the order in which the base resources are
521             //looked up.
522             TreeMap<String,Resource> patchResourcesPath = new TreeMap<String,Resource>();
523             TreeMap<String,Resource> appendedResourcesPath = new TreeMap<String,Resource>();
524             for (Bundle frag : fragments) {
525                 String fragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
526                 String patchFragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
527                 if (fragFolder != null)
528                 {
529                     URL fragUrl = frag.getEntry(fragFolder);
530                     if (fragUrl == null)
531                     {
532                         throw new IllegalArgumentException("Unable to locate " + fragFolder + " inside "
533                                 + " the fragment '" + frag.getSymbolicName() + "'");
534                     }
535                     fragUrl = DefaultFileLocatorHelper.getLocalURL(fragUrl);
536                     String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
537                     appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
538                 }
539                 if (patchFragFolder != null)
540                 {
541                     URL patchFragUrl = frag.getEntry(patchFragFolder);
542                     if (patchFragUrl == null)
543                     {
544                         throw new IllegalArgumentException("Unable to locate " + patchFragUrl + " inside "
545                                 + " the fragment '" + frag.getSymbolicName() + "'");
546                     }
547                     patchFragUrl = DefaultFileLocatorHelper.getLocalURL(patchFragUrl);
548                     String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
549                     patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
550                 }
551             }
552             if (!appendedResourcesPath.isEmpty())
553             {
554             	wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
555             }
556             if (!patchResourcesPath.isEmpty())
557             {
558             	wah.setAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch", new ArrayList<Resource>(patchResourcesPath.values()));
559             }
560             
561             if (wah instanceof WebAppContext)
562             {
563             	//This is the equivalent of what MetaInfConfiguration does. For OSGi bundles without the JarScanner
564             	WebAppContext webappCtxt = (WebAppContext)wah;
565 	            //take care of the web-fragments, meta-inf resources and tld resources:
566 	            //similar to what MetaInfConfiguration does.
567 	            List<Resource> frags = (List<Resource>)wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES);
568 	            List<Resource> resfrags = (List<Resource>)wah.getAttribute(WebInfConfiguration.RESOURCE_URLS);
569 	            List<Resource> tldfrags = (List<Resource>)wah.getAttribute(TagLibConfiguration.TLD_RESOURCES);
570 	            for (Bundle frag : fragments)
571 	            {
572 	            	URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
573 	            	Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
574 	            	Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false);
575 	            	if (webFrag != null || (resEnum != null && resEnum.hasMoreElements())
576 	            			|| (tldEnum != null && tldEnum.hasMoreElements()))
577 	                {
578 	                    try
579 	                    {
580 	                        File fragFile = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(frag);
581 	                        //add it to the webinf jars collection:
582 	                        //no need to check that it was not there yet: it was not there yet for sure.
583 	                        Resource fragFileAsResource = Resource.newResource(fragFile.toURI());
584 	                        webappCtxt.getMetaData().addWebInfJar(fragFileAsResource);
585 	                        
586 	                        if (webFrag != null)
587 	                        {
588 		                        if (frags == null)
589 		                        {
590 		                            frags = new ArrayList<Resource>();
591 		                            wah.setAttribute(FragmentConfiguration.FRAGMENT_RESOURCES, frags);
592 		                        }
593 		                        frags.add(fragFileAsResource);
594 	                        }
595 	                        if (resEnum != null && resEnum.hasMoreElements())
596 	                        {
597 	                        	URL resourcesEntry = frag.getEntry("/META-INF/resources/");
598 	                        	if (resourcesEntry == null)
599 	                        	{
600 	                        		//probably we found some fragments to a bundle.
601 			                        //those are already contributed.
602 	                        		//so we skip this.
603 	                        	}
604 	                        	else
605 	                        	{
606 			                        if (resfrags == null)
607 			                        {
608 			                        	resfrags = new ArrayList<Resource>();
609 			                            wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags);
610 			                        }
611 	                        		resfrags.add(Resource.newResource(
612 	                        				DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
613 	                        	}
614 	                        }
615 	                        if (tldEnum != null && tldEnum.hasMoreElements())
616 	                        {
617 		                        if (tldfrags == null)
618 		                        {
619 		                        	tldfrags = new ArrayList<Resource>();
620 		                            wah.setAttribute(TagLibConfiguration.TLD_RESOURCES, tldfrags);
621 		                        }
622 		                        while (tldEnum.hasMoreElements())
623 		                        {
624 		                        	tldfrags.add(Resource.newResource(
625 		                        			DefaultFileLocatorHelper.getLocalURL(tldEnum.nextElement())));
626 		                        }
627 	                        }
628 	                    }
629 	                    catch (Exception e)
630 	                    {
631 	                        __logger.warn("Unable to locate the bundle " + frag.getBundleId(),e);
632 	                    }
633 	                }
634 	            }
635             }
636         }
637         
638         
639     }
640 
641     /**
642      * @See {@link ContextDeployer#scan}
643      * @param contextFile
644      * @return
645      */
646     protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
647             Bundle bundle, File contextFile, String extraClasspath,
648             String overrideBundleInstallLocation, String requireTldBundle)
649     {
650         try
651         {
652             return createContextHandler(handlerToConfigure,bundle,
653                     new BufferedInputStream(new FileInputStream(contextFile)),
654                     extraClasspath,overrideBundleInstallLocation,requireTldBundle);
655         }
656         catch (FileNotFoundException e)
657         {
658             e.printStackTrace();
659         }
660         return null;
661     }
662 
663     /**
664      * @See {@link ContextDeployer#scan}
665      * @param contextFile
666      * @return
667      */
668     @SuppressWarnings("unchecked")
669     protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
670             Bundle bundle, InputStream contextInputStream, String extraClasspath,
671             String overrideBundleInstallLocation, String requireTldBundle)
672     {
673         /*
674          * Do something identical to what the ContextProvider would have done:
675          * XmlConfiguration xmlConfiguration=new
676          * XmlConfiguration(resource.getURL()); HashMap properties = new
677          * HashMap(); properties.put("Server", _contexts.getServer()); if
678          * (_configMgr!=null) properties.putAll(_configMgr.getProperties());
679          * 
680          * xmlConfiguration.setProperties(properties); ContextHandler
681          * context=(ContextHandler)xmlConfiguration.configure();
682          * context.setAttributes(new AttributesMap(_contextAttributes));
683          */
684         try
685         {
686             XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
687             HashMap properties = new HashMap();
688             properties.put("Server",_wrapper.getServer());
689             
690             // insert the bundle's location as a property.
691             setThisBundleHomeProperty(bundle,properties,overrideBundleInstallLocation);
692             xmlConfiguration.getProperties().putAll(properties);
693 
694             ContextHandler context = null;
695             if (handlerToConfigure == null)
696             {
697                 context = (ContextHandler)xmlConfiguration.configure();
698             }
699             else
700             {
701                 xmlConfiguration.configure(handlerToConfigure);
702                 context = handlerToConfigure;
703             }
704             
705             if (context instanceof WebAppContext)
706             {
707                 ((WebAppContext)context).setExtraClasspath(extraClasspath);
708                 ((WebAppContext)context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
709                 if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0)
710                 {
711                     ((WebAppContext)context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor());
712                 }
713             }
714 
715             configureWebAppContext(context, bundle, requireTldBundle);
716             return context;
717         }
718         catch (FileNotFoundException e)
719         {
720             return null;
721         }
722         catch (SAXException e)
723         {
724             // TODO Auto-generated catch block
725             e.printStackTrace();
726         }
727         catch (IOException e)
728         {
729             // TODO Auto-generated catch block
730             e.printStackTrace();
731         }
732         catch (Throwable e)
733         {
734             // TODO Auto-generated catch block
735             e.printStackTrace();
736         }
737         finally
738         {
739             IO.close(contextInputStream);
740         }
741         return null;
742     }
743 
744     /**
745      * Configure a classloader onto the context. If the context is a
746      * WebAppContext, build a WebAppClassLoader that has access to all the jetty
747      * classes thanks to the classloader of the JettyBootStrapper bundle and
748      * also has access to the classloader of the bundle that defines this
749      * context.
750      * <p>
751      * If the context is not a WebAppContext, same but with a simpler
752      * URLClassLoader. Note that the URLClassLoader is pretty much fake: it
753      * delegate all actual classloading to the parent classloaders.
754      * </p>
755      * <p>
756      * The URL[] returned by the URLClassLoader create contained specifically
757      * the jars that some j2ee tools expect and look into. For example the jars
758      * that contain tld files for jasper's jstl support.
759      * </p>
760      * <p>
761      * Also as the jars in the lib folder and the classes in the classes folder
762      * might already be in the OSGi classloader we filter them out of the
763      * WebAppClassLoader
764      * </p>
765      * 
766      * @param context
767      * @param contributor
768      * @param webapp
769      * @param contextPath
770      * @param classInBundle
771      * @throws Exception
772      */
773     protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader) throws Exception
774     {
775         if (context instanceof WebAppContext)
776         {
777             WebAppContext webappCtxt = (WebAppContext)context;
778             context.setClassLoader(webappClassLoader);
779             webappClassLoader.setWebappContext(webappCtxt);
780         }
781         else
782         {
783             context.setClassLoader(webappClassLoader);
784         }
785     }
786 
787     /**
788      * No matter what the type of webapp, we create a WebappClassLoader.
789      */
790     protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
791     {
792         // we use a temporary WebAppContext object.
793         // if this is a real webapp we will set it on it a bit later: once we
794         // know.
795         OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(
796             _wrapper.getParentClassLoaderForWebapps(),new WebAppContext(),contributor,BUNDLE_CLASS_LOADER_HELPER);
797         return webappClassLoader;
798     }
799 
800     /**
801      * Set the property &quot;this.bundle.install&quot; to point to the location
802      * of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
803      * used.
804      */
805     private void setThisBundleHomeProperty(Bundle bundle, HashMap<String, Object> properties, String overrideBundleInstallLocation)
806     {
807         try
808         {
809             File location = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation):BUNDLE_FILE_LOCATOR_HELPER
810                     .getBundleInstallLocation(bundle);
811             properties.put("this.bundle.install",location.getCanonicalPath());
812             properties.put("this.bundle.install.url",bundle.getEntry("/").toString());
813         }
814         catch (Throwable t)
815         {
816             __logger.warn("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName(), t);
817         }
818     }
819 
820 
821 }
822