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                 Resource r = Resource.newResource(contextURL);              
380                 return registerContext(contributor,contextFileRelativePath,r.getInputStream(),extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
381             }
382             throw new IllegalArgumentException("Could not find the context " + "file " + contextFileRelativePath + " for the bundle "
383                     + contributor.getSymbolicName() + (overrideBundleInstallLocation != null?" using the install location " + overrideBundleInstallLocation:""));
384         }
385     }
386 
387     /**
388      * This type of registration relies on jetty's complete context xml file.
389      * Context encompasses jndi and all other things. This makes the definition
390      * of the webapp a lot more self-contained.
391      * 
392      * @param webapp
393      * @param contextPath
394      * @param classInBundle
395      * @throws Exception
396      */
397     private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile,
398             String extraClasspath, String overrideBundleInstallLocation,
399             String requireTldBundle, ContextHandler handler) throws Exception
400     {
401         InputStream contextFileInputStream = null;
402         try
403         {
404             contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile));
405             return registerContext(contributor, pathInBundle, contextFileInputStream,
406                     extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
407         }
408         finally
409         {
410             IO.close(contextFileInputStream);
411         }
412     }
413 
414     /**
415      * @param contributor
416      * @param contextFileInputStream
417      * @return The ContextHandler created and registered or null if it did not
418      *         happen.
419      * @throws Exception
420      */
421     private ContextHandler registerContext(Bundle contributor,
422             String pathInsideBundle, InputStream contextFileInputStream,
423             String extraClasspath, String overrideBundleInstallLocation,
424             String requireTldBundle, ContextHandler handler)
425             throws Exception
426     {
427         ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
428         String[] oldServerClasses = null;
429         WebAppContext webAppContext = null;
430         try
431         {
432             // make sure we provide access to all the jetty bundles by going
433             // through this bundle.
434             OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
435             // configure with access to all jetty classes and also all the
436             // classes
437             // that the contributor gives access to.
438             Thread.currentThread().setContextClassLoader(composite);
439             ContextHandler context = createContextHandler(handler, contributor,
440                     contextFileInputStream,extraClasspath,
441                     overrideBundleInstallLocation,requireTldBundle);
442             if (context == null)
443             {
444                 return null;// did not happen
445             }
446 
447             // ok now register this webapp. we checked when we started jetty
448             // that there
449             // was at least one such handler for webapps.
450             //the actual registration must happen via the new Deployment API.
451 //            _ctxtHandler.addHandler(context);
452 
453             configureWebappClassLoader(contributor,context,composite);
454             if (context instanceof WebAppContext)
455             {
456                 webAppContext = (WebAppContext)context;
457                 // @see
458                 // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
459                 oldServerClasses = webAppContext.getServerClasses();
460                 webAppContext.setServerClasses(null);
461             }
462             _wrapper.getOSGiAppProvider().addContext(contributor, pathInsideBundle, context);
463             return context;
464         }
465         finally
466         {
467             if (webAppContext != null)
468             {
469                 webAppContext.setServerClasses(oldServerClasses);
470             }
471             Thread.currentThread().setContextClassLoader(contextCl);
472         }
473 
474     }
475 
476     /**
477      * Applies the properties of WebAppDeployer as defined in jetty.xml.
478      * 
479      * @see {WebAppDeployer#scan} around the comment
480      *      <code>// configure it</code>
481      */
482     protected void configureWebAppContext(ContextHandler wah, Bundle contributor,
483             String requireTldBundle) throws IOException
484     {
485         // rfc66
486         wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
487 
488         //spring-dm-1.2.1 looks for the BundleContext as a different attribute.
489         //not a spec... but if we want to support 
490         //org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
491         //then we need to do this to:
492         wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
493                         contributor.getBundleContext());
494         
495         //also pass the bundle directly. sometimes a bundle does not have a bundlecontext.
496         //it is still useful to have access to the Bundle from the servlet context.
497         wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor);
498         
499         //pass the value of the require tld bundle so that the TagLibOSGiConfiguration
500         //can pick it up.
501         wah.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundle);
502 
503         
504         Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(contributor);
505         if (fragments != null && fragments.length != 0)
506         {
507             //sorted extra resource base found in the fragments.
508             //the resources are either overriding the resourcebase found in the web-bundle
509             //or appended.
510             //amongst each resource we sort them according to the alphabetical order
511             //of the name of the internal folder and the symbolic name of the fragment.
512             //this is useful to make sure that the lookup path of those
513             //resource base defined by fragments is always the same.
514             //This natural order could be abused to define the order in which the base resources are
515             //looked up.
516             TreeMap<String,Resource> patchResourcesPath = new TreeMap<String,Resource>();
517             TreeMap<String,Resource> appendedResourcesPath = new TreeMap<String,Resource>();
518             for (Bundle frag : fragments) {
519                 String fragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
520                 String patchFragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
521                 if (fragFolder != null)
522                 {
523                     URL fragUrl = frag.getEntry(fragFolder);
524                     if (fragUrl == null)
525                     {
526                         throw new IllegalArgumentException("Unable to locate " + fragFolder + " inside "
527                                 + " the fragment '" + frag.getSymbolicName() + "'");
528                     }
529                     fragUrl = DefaultFileLocatorHelper.getLocalURL(fragUrl);
530                     String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
531                     appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
532                 }
533                 if (patchFragFolder != null)
534                 {
535                     URL patchFragUrl = frag.getEntry(patchFragFolder);
536                     if (patchFragUrl == null)
537                     {
538                         throw new IllegalArgumentException("Unable to locate " + patchFragUrl + " inside "
539                                 + " the fragment '" + frag.getSymbolicName() + "'");
540                     }
541                     patchFragUrl = DefaultFileLocatorHelper.getLocalURL(patchFragUrl);
542                     String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
543                     patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
544                 }
545             }
546             if (!appendedResourcesPath.isEmpty())
547             {
548             	wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
549             }
550             if (!patchResourcesPath.isEmpty())
551             {
552             	wah.setAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch", new ArrayList<Resource>(patchResourcesPath.values()));
553             }
554             
555             if (wah instanceof WebAppContext)
556             {
557             	//This is the equivalent of what MetaInfConfiguration does. For OSGi bundles without the JarScanner
558             	WebAppContext webappCtxt = (WebAppContext)wah;
559 	            //take care of the web-fragments, meta-inf resources and tld resources:
560 	            //similar to what MetaInfConfiguration does.
561 	            List<Resource> frags = (List<Resource>)wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES);
562 	            List<Resource> resfrags = (List<Resource>)wah.getAttribute(WebInfConfiguration.RESOURCE_URLS);
563 	            List<Resource> tldfrags = (List<Resource>)wah.getAttribute(TagLibConfiguration.TLD_RESOURCES);
564 	            for (Bundle frag : fragments)
565 	            {
566 	            	URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
567 	            	Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
568 	            	Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false);
569 	            	if (webFrag != null || (resEnum != null && resEnum.hasMoreElements())
570 	            			|| (tldEnum != null && tldEnum.hasMoreElements()))
571 	                {
572 	                    try
573 	                    {
574 	                        File fragFile = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(frag);
575 	                        //add it to the webinf jars collection:
576 	                        //no need to check that it was not there yet: it was not there yet for sure.
577 	                        Resource fragFileAsResource = Resource.newResource(fragFile.toURI());
578 	                        webappCtxt.getMetaData().addWebInfJar(fragFileAsResource);
579 	                        
580 	                        if (webFrag != null)
581 	                        {
582 		                        if (frags == null)
583 		                        {
584 		                            frags = new ArrayList<Resource>();
585 		                            wah.setAttribute(FragmentConfiguration.FRAGMENT_RESOURCES, frags);
586 		                        }
587 		                        frags.add(fragFileAsResource);
588 	                        }
589 	                        if (resEnum != null && resEnum.hasMoreElements())
590 	                        {
591 	                        	URL resourcesEntry = frag.getEntry("/META-INF/resources/");
592 	                        	if (resourcesEntry == null)
593 	                        	{
594 	                        		//probably we found some fragments to a bundle.
595 			                        //those are already contributed.
596 	                        		//so we skip this.
597 	                        	}
598 	                        	else
599 	                        	{
600 			                        if (resfrags == null)
601 			                        {
602 			                        	resfrags = new ArrayList<Resource>();
603 			                            wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags);
604 			                        }
605 	                        		resfrags.add(Resource.newResource(
606 	                        				DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
607 	                        	}
608 	                        }
609 	                        if (tldEnum != null && tldEnum.hasMoreElements())
610 	                        {
611 		                        if (tldfrags == null)
612 		                        {
613 		                        	tldfrags = new ArrayList<Resource>();
614 		                            wah.setAttribute(TagLibConfiguration.TLD_RESOURCES, tldfrags);
615 		                        }
616 		                        while (tldEnum.hasMoreElements())
617 		                        {
618 		                        	tldfrags.add(Resource.newResource(
619 		                        			DefaultFileLocatorHelper.getLocalURL(tldEnum.nextElement())));
620 		                        }
621 	                        }
622 	                    }
623 	                    catch (Exception e)
624 	                    {
625 	                        __logger.warn("Unable to locate the bundle " + frag.getBundleId(),e);
626 	                    }
627 	                }
628 	            }
629             }
630         }
631         
632         
633     }
634 
635     /**
636      * @See {@link ContextDeployer#scan}
637      * @param contextFile
638      * @return
639      */
640     protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
641             Bundle bundle, File contextFile, String extraClasspath,
642             String overrideBundleInstallLocation, String requireTldBundle)
643     {
644         try
645         {
646             return createContextHandler(handlerToConfigure,bundle,
647                     new BufferedInputStream(new FileInputStream(contextFile)),
648                     extraClasspath,overrideBundleInstallLocation,requireTldBundle);
649         }
650         catch (FileNotFoundException e)
651         {
652             e.printStackTrace();
653         }
654         return null;
655     }
656 
657     /**
658      * @See {@link ContextDeployer#scan}
659      * @param contextFile
660      * @return
661      */
662     @SuppressWarnings("unchecked")
663     protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
664             Bundle bundle, InputStream contextInputStream, String extraClasspath,
665             String overrideBundleInstallLocation, String requireTldBundle)
666     {
667         /*
668          * Do something identical to what the ContextProvider would have done:
669          * XmlConfiguration xmlConfiguration=new
670          * XmlConfiguration(resource.getURL()); HashMap properties = new
671          * HashMap(); properties.put("Server", _contexts.getServer()); if
672          * (_configMgr!=null) properties.putAll(_configMgr.getProperties());
673          * 
674          * xmlConfiguration.setProperties(properties); ContextHandler
675          * context=(ContextHandler)xmlConfiguration.configure();
676          * context.setAttributes(new AttributesMap(_contextAttributes));
677          */
678         try
679         {
680             XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
681             HashMap properties = new HashMap();
682             properties.put("Server",_wrapper.getServer());
683             
684             // insert the bundle's location as a property.
685             setThisBundleHomeProperty(bundle,properties,overrideBundleInstallLocation);
686             xmlConfiguration.getProperties().putAll(properties);
687 
688             ContextHandler context = null;
689             if (handlerToConfigure == null)
690             {
691                 context = (ContextHandler)xmlConfiguration.configure();
692             }
693             else
694             {
695                 xmlConfiguration.configure(handlerToConfigure);
696                 context = handlerToConfigure;
697             }
698             
699             if (context instanceof WebAppContext)
700             {
701                 ((WebAppContext)context).setExtraClasspath(extraClasspath);
702                 ((WebAppContext)context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
703                 if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0)
704                 {
705                     ((WebAppContext)context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor());
706                 }
707             }
708 
709             configureWebAppContext(context, bundle, requireTldBundle);
710             return context;
711         }
712         catch (FileNotFoundException e)
713         {
714             return null;
715         }
716         catch (SAXException e)
717         {
718             // TODO Auto-generated catch block
719             e.printStackTrace();
720         }
721         catch (IOException e)
722         {
723             // TODO Auto-generated catch block
724             e.printStackTrace();
725         }
726         catch (Throwable e)
727         {
728             // TODO Auto-generated catch block
729             e.printStackTrace();
730         }
731         finally
732         {
733             IO.close(contextInputStream);
734         }
735         return null;
736     }
737 
738     /**
739      * Configure a classloader onto the context. If the context is a
740      * WebAppContext, build a WebAppClassLoader that has access to all the jetty
741      * classes thanks to the classloader of the JettyBootStrapper bundle and
742      * also has access to the classloader of the bundle that defines this
743      * context.
744      * <p>
745      * If the context is not a WebAppContext, same but with a simpler
746      * URLClassLoader. Note that the URLClassLoader is pretty much fake: it
747      * delegate all actual classloading to the parent classloaders.
748      * </p>
749      * <p>
750      * The URL[] returned by the URLClassLoader create contained specifically
751      * the jars that some j2ee tools expect and look into. For example the jars
752      * that contain tld files for jasper's jstl support.
753      * </p>
754      * <p>
755      * Also as the jars in the lib folder and the classes in the classes folder
756      * might already be in the OSGi classloader we filter them out of the
757      * WebAppClassLoader
758      * </p>
759      * 
760      * @param context
761      * @param contributor
762      * @param webapp
763      * @param contextPath
764      * @param classInBundle
765      * @throws Exception
766      */
767     protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader) throws Exception
768     {
769         if (context instanceof WebAppContext)
770         {
771             WebAppContext webappCtxt = (WebAppContext)context;
772             context.setClassLoader(webappClassLoader);
773             webappClassLoader.setWebappContext(webappCtxt);
774         }
775         else
776         {
777             context.setClassLoader(webappClassLoader);
778         }
779     }
780 
781     /**
782      * No matter what the type of webapp, we create a WebappClassLoader.
783      */
784     protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
785     {
786         // we use a temporary WebAppContext object.
787         // if this is a real webapp we will set it on it a bit later: once we
788         // know.
789         OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(
790             _wrapper.getParentClassLoaderForWebapps(),new WebAppContext(),contributor,BUNDLE_CLASS_LOADER_HELPER);
791         return webappClassLoader;
792     }
793 
794     /**
795      * Set the property &quot;this.bundle.install&quot; to point to the location
796      * of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
797      * used.
798      */
799     private void setThisBundleHomeProperty(Bundle bundle, HashMap<String, Object> properties, String overrideBundleInstallLocation)
800     {
801         try
802         {
803             File location = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation):BUNDLE_FILE_LOCATOR_HELPER
804                     .getBundleInstallLocation(bundle);
805             properties.put("this.bundle.install",location.getCanonicalPath());
806             properties.put("this.bundle.install.url",bundle.getEntry("/").toString());
807         }
808         catch (Throwable t)
809         {
810             __logger.warn("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName(), t);
811         }
812     }
813 
814 
815 }