1
2
3
4
5
6
7
8
9
10
11
12
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
57
58
59
60
61
62
63
64
65
66
67
68
69
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
80
81
82
83 public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null;
84
85
86
87
88
89 public static BundleFileLocatorHelper BUNDLE_FILE_LOCATOR_HELPER = null;
90
91
92
93
94
95
96
97
98
99
100 public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
101
102
103
104
105
106
107
108
109 private ServerInstanceWrapper _wrapper;
110
111 public WebBundleDeployerHelper(ServerInstanceWrapper wrapper)
112 {
113 staticInit();
114 _wrapper = wrapper;
115 }
116
117
118 public static synchronized void staticInit()
119 {
120 if (!INITIALIZED)
121 {
122 INITIALIZED = true;
123
124 try
125 {
126 BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper)Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
127 }
128 catch (Throwable t)
129 {
130
131 BUNDLE_CLASS_LOADER_HELPER = new DefaultBundleClassLoaderHelper();
132 }
133
134 try
135 {
136 BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper)Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
137 }
138 catch (Throwable t)
139 {
140
141 BUNDLE_FILE_LOCATOR_HELPER = new DefaultFileLocatorHelper();
142 }
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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
213
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
227
228 OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
229
230
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
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
277 context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
278
279 configureWebAppContext(context,contributor,requireTldBundle);
280 configureWebappClassLoader(contributor,context,composite);
281
282
283
284
285
286
287 oldServerClasses = context.getServerClasses();
288 context.setServerClasses(null);
289
290 _wrapper.getOSGiAppProvider().addContext(contributor,pathInBundleToWebApp,context);
291
292
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
299 resourcesPath.addAll(patchResources);
300
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
333
334
335 public void unregister(ContextHandler contextHandler) throws Exception
336 {
337 _wrapper.getOSGiAppProvider().removeContext(contextHandler);
338 }
339
340
341
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
389
390
391
392
393
394
395
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
416
417
418
419
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
433
434 OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
435
436
437
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;
445 }
446
447
448
449
450
451
452
453 configureWebappClassLoader(contributor,context,composite);
454 if (context instanceof WebAppContext)
455 {
456 webAppContext = (WebAppContext)context;
457
458
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
478
479
480
481
482 protected void configureWebAppContext(ContextHandler wah, Bundle contributor,
483 String requireTldBundle) throws IOException
484 {
485
486 wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
487
488
489
490
491
492 wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
493 contributor.getBundleContext());
494
495
496
497 wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor);
498
499
500
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
508
509
510
511
512
513
514
515
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
558 WebAppContext webappCtxt = (WebAppContext)wah;
559
560
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
576
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
595
596
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
637
638
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
659
660
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
669
670
671
672
673
674
675
676
677
678 try
679 {
680 XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
681 HashMap properties = new HashMap();
682 properties.put("Server",_wrapper.getServer());
683
684
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
719 e.printStackTrace();
720 }
721 catch (IOException e)
722 {
723
724 e.printStackTrace();
725 }
726 catch (Throwable e)
727 {
728
729 e.printStackTrace();
730 }
731 finally
732 {
733 IO.close(contextInputStream);
734 }
735 return null;
736 }
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
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
783
784 protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
785 {
786
787
788
789 OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(
790 _wrapper.getParentClassLoaderForWebapps(),new WebAppContext(),contributor,BUNDLE_CLASS_LOADER_HELPER);
791 return webappClassLoader;
792 }
793
794
795
796
797
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 }