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