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         
503         Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(contributor);
504         if (fragments != null && fragments.length != 0)
505         {
506             //sorted extra resource base found in the fragments.
507             //the resources are either overriding the resourcebase found in the web-bundle
508             //or appended.
509             //amongst each resource we sort them according to the alphabetical order
510             //of the name of the internal folder and the symbolic name of the fragment.
511             //this is useful to make sure that the lookup path of those
512             //resource base defined by fragments is always the same.
513             //This natural order could be abused to define the order in which the base resources are
514             //looked up.
515             TreeMap<String,Resource> patchResourcesPath = new TreeMap<String,Resource>();
516             TreeMap<String,Resource> appendedResourcesPath = new TreeMap<String,Resource>();
517             for (Bundle frag : fragments) {
518                 String fragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
519                 String patchFragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
520                 if (fragFolder != null)
521                 {
522                     URL fragUrl = frag.getEntry(fragFolder);
523                     if (fragUrl == null)
524                     {
525                         throw new IllegalArgumentException("Unable to locate " + fragFolder + " inside "
526                                 + " the fragment '" + frag.getSymbolicName() + "'");
527                     }
528                     fragUrl = DefaultFileLocatorHelper.getLocalURL(fragUrl);
529                     String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
530                     appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
531                 }
532                 if (patchFragFolder != null)
533                 {
534                     URL patchFragUrl = frag.getEntry(patchFragFolder);
535                     if (patchFragUrl == null)
536                     {
537                         throw new IllegalArgumentException("Unable to locate " + patchFragUrl + " inside "
538                                 + " the fragment '" + frag.getSymbolicName() + "'");
539                     }
540                     patchFragUrl = DefaultFileLocatorHelper.getLocalURL(patchFragUrl);
541                     String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
542                     patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
543                 }
544             }
545             if (!appendedResourcesPath.isEmpty())
546             {
547             	wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
548             }
549             if (!patchResourcesPath.isEmpty())
550             {
551             	wah.setAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch", new ArrayList<Resource>(patchResourcesPath.values()));
552             }
553             
554             if (wah instanceof WebAppContext)
555             {
556             	//This is the equivalent of what MetaInfConfiguration does. For OSGi bundles without the JarScanner
557             	WebAppContext webappCtxt = (WebAppContext)wah;
558 	            //take care of the web-fragments, meta-inf resources and tld resources:
559 	            //similar to what MetaInfConfiguration does.
560 	            List<Resource> frags = (List<Resource>)wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES);
561 	            List<Resource> resfrags = (List<Resource>)wah.getAttribute(WebInfConfiguration.RESOURCE_URLS);
562 	            List<Resource> tldfrags = (List<Resource>)wah.getAttribute(TagLibConfiguration.TLD_RESOURCES);
563 	            for (Bundle frag : fragments)
564 	            {
565 	            	URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
566 	            	Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
567 	            	Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false);
568 	            	if (webFrag != null || (resEnum != null && resEnum.hasMoreElements())
569 	            			|| (tldEnum != null && tldEnum.hasMoreElements()))
570 	                {
571 	                    try
572 	                    {
573 	                        File fragFile = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(frag);
574 	                        //add it to the webinf jars collection:
575 	                        //no need to check that it was not there yet: it was not there yet for sure.
576 	                        Resource fragFileAsResource = Resource.newResource(fragFile.toURI());
577 	                        webappCtxt.getMetaData().addWebInfJar(fragFileAsResource);
578 	                        
579 	                        if (webFrag != null)
580 	                        {
581 		                        if (frags == null)
582 		                        {
583 		                            frags = new ArrayList<Resource>();
584 		                            wah.setAttribute(FragmentConfiguration.FRAGMENT_RESOURCES, frags);
585 		                        }
586 		                        frags.add(fragFileAsResource);
587 	                        }
588 	                        if (resEnum != null && resEnum.hasMoreElements())
589 	                        {
590 	                        	URL resourcesEntry = frag.getEntry("/META-INF/resources/");
591 	                        	if (resourcesEntry == null)
592 	                        	{
593 	                        		//probably we found some fragments to a bundle.
594 			                        //those are already contributed.
595 	                        		//so we skip this.
596 	                        	}
597 	                        	else
598 	                        	{
599 			                        if (resfrags == null)
600 			                        {
601 			                        	resfrags = new ArrayList<Resource>();
602 			                            wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags);
603 			                        }
604 	                        		resfrags.add(Resource.newResource(
605 	                        				DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
606 	                        	}
607 	                        }
608 	                        if (tldEnum != null && tldEnum.hasMoreElements())
609 	                        {
610 		                        if (tldfrags == null)
611 		                        {
612 		                        	tldfrags = new ArrayList<Resource>();
613 		                            wah.setAttribute(TagLibConfiguration.TLD_RESOURCES, tldfrags);
614 		                        }
615 		                        while (tldEnum.hasMoreElements())
616 		                        {
617 		                        	tldfrags.add(Resource.newResource(
618 		                        			DefaultFileLocatorHelper.getLocalURL(tldEnum.nextElement())));
619 		                        }
620 	                        }
621 	                    }
622 	                    catch (Exception e)
623 	                    {
624 	                        __logger.warn("Unable to locate the bundle " + frag.getBundleId(),e);
625 	                    }
626 	                }
627 	            }
628             }
629         }
630         
631         
632     }
633 
634     /**
635      * @See {@link ContextDeployer#scan}
636      * @param contextFile
637      * @return
638      */
639     protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
640             Bundle bundle, File contextFile, String extraClasspath,
641             String overrideBundleInstallLocation, String requireTldBundle)
642     {
643         try
644         {
645             return createContextHandler(handlerToConfigure,bundle,
646                     new BufferedInputStream(new FileInputStream(contextFile)),
647                     extraClasspath,overrideBundleInstallLocation,requireTldBundle);
648         }
649         catch (FileNotFoundException e)
650         {
651             e.printStackTrace();
652         }
653         return null;
654     }
655 
656     /**
657      * @See {@link ContextDeployer#scan}
658      * @param contextFile
659      * @return
660      */
661     @SuppressWarnings("unchecked")
662     protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
663             Bundle bundle, InputStream contextInputStream, String extraClasspath,
664             String overrideBundleInstallLocation, String requireTldBundle)
665     {
666         /*
667          * Do something identical to what the ContextProvider would have done:
668          * XmlConfiguration xmlConfiguration=new
669          * XmlConfiguration(resource.getURL()); HashMap properties = new
670          * HashMap(); properties.put("Server", _contexts.getServer()); if
671          * (_configMgr!=null) properties.putAll(_configMgr.getProperties());
672          * 
673          * xmlConfiguration.setProperties(properties); ContextHandler
674          * context=(ContextHandler)xmlConfiguration.configure();
675          * context.setAttributes(new AttributesMap(_contextAttributes));
676          */
677         try
678         {
679             XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
680             HashMap properties = new HashMap();
681             properties.put("Server",_wrapper.getServer());
682             
683             // insert the bundle's location as a property.
684             setThisBundleHomeProperty(bundle,properties,overrideBundleInstallLocation);
685             xmlConfiguration.getProperties().putAll(properties);
686 
687             ContextHandler context = null;
688             if (handlerToConfigure == null)
689             {
690                 context = (ContextHandler)xmlConfiguration.configure();
691             }
692             else
693             {
694                 xmlConfiguration.configure(handlerToConfigure);
695                 context = handlerToConfigure;
696             }
697             
698             if (context instanceof WebAppContext)
699             {
700                 ((WebAppContext)context).setExtraClasspath(extraClasspath);
701                 ((WebAppContext)context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
702                 if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0)
703                 {
704                     ((WebAppContext)context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor());
705                 }
706             }
707 
708             configureWebAppContext(context, bundle, requireTldBundle);
709             return context;
710         }
711         catch (FileNotFoundException e)
712         {
713             return null;
714         }
715         catch (SAXException e)
716         {
717             // TODO Auto-generated catch block
718             e.printStackTrace();
719         }
720         catch (IOException e)
721         {
722             // TODO Auto-generated catch block
723             e.printStackTrace();
724         }
725         catch (Throwable e)
726         {
727             // TODO Auto-generated catch block
728             e.printStackTrace();
729         }
730         finally
731         {
732             IO.close(contextInputStream);
733         }
734         return null;
735     }
736 
737     /**
738      * Configure a classloader onto the context. If the context is a
739      * WebAppContext, build a WebAppClassLoader that has access to all the jetty
740      * classes thanks to the classloader of the JettyBootStrapper bundle and
741      * also has access to the classloader of the bundle that defines this
742      * context.
743      * <p>
744      * If the context is not a WebAppContext, same but with a simpler
745      * URLClassLoader. Note that the URLClassLoader is pretty much fake: it
746      * delegate all actual classloading to the parent classloaders.
747      * </p>
748      * <p>
749      * The URL[] returned by the URLClassLoader create contained specifically
750      * the jars that some j2ee tools expect and look into. For example the jars
751      * that contain tld files for jasper's jstl support.
752      * </p>
753      * <p>
754      * Also as the jars in the lib folder and the classes in the classes folder
755      * might already be in the OSGi classloader we filter them out of the
756      * WebAppClassLoader
757      * </p>
758      * 
759      * @param context
760      * @param contributor
761      * @param webapp
762      * @param contextPath
763      * @param classInBundle
764      * @throws Exception
765      */
766     protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader) throws Exception
767     {
768         if (context instanceof WebAppContext)
769         {
770             WebAppContext webappCtxt = (WebAppContext)context;
771             context.setClassLoader(webappClassLoader);
772             webappClassLoader.setWebappContext(webappCtxt);
773         }
774         else
775         {
776             context.setClassLoader(webappClassLoader);
777         }
778     }
779 
780     /**
781      * No matter what the type of webapp, we create a WebappClassLoader.
782      */
783     protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
784     {
785         // we use a temporary WebAppContext object.
786         // if this is a real webapp we will set it on it a bit later: once we
787         // know.
788         OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(
789             _wrapper.getParentClassLoaderForWebapps(),new WebAppContext(),contributor,BUNDLE_CLASS_LOADER_HELPER);
790         return webappClassLoader;
791     }
792 
793     /**
794      * Set the property &quot;this.bundle.install&quot; to point to the location
795      * of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
796      * used.
797      */
798     private void setThisBundleHomeProperty(Bundle bundle, HashMap<String, Object> properties, String overrideBundleInstallLocation)
799     {
800         try
801         {
802             File location = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation):BUNDLE_FILE_LOCATOR_HELPER
803                     .getBundleInstallLocation(bundle);
804             properties.put("this.bundle.install",location.getCanonicalPath());
805             properties.put("this.bundle.install.url",bundle.getEntry("/").toString());
806         }
807         catch (Throwable t)
808         {
809             __logger.warn("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName(), t);
810         }
811     }
812 
813 
814 }