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 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
388
389
390
391
392
393
394
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
415
416
417
418
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
432
433 OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
434
435
436
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;
444 }
445
446
447
448
449
450
451
452 configureWebappClassLoader(contributor,context,composite);
453 if (context instanceof WebAppContext)
454 {
455 webAppContext = (WebAppContext)context;
456
457
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
477
478
479
480
481 protected void configureWebAppContext(ContextHandler wah, Bundle contributor,
482 String requireTldBundle) throws IOException
483 {
484
485 wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
486
487
488
489
490
491 wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
492 contributor.getBundleContext());
493
494
495
496 wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor);
497
498
499
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
507
508
509
510
511
512
513
514
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
557 WebAppContext webappCtxt = (WebAppContext)wah;
558
559
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
575
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
594
595
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
636
637
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
658
659
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
668
669
670
671
672
673
674
675
676
677 try
678 {
679 XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
680 HashMap properties = new HashMap();
681 properties.put("Server",_wrapper.getServer());
682
683
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
718 e.printStackTrace();
719 }
720 catch (IOException e)
721 {
722
723 e.printStackTrace();
724 }
725 catch (Throwable e)
726 {
727
728 e.printStackTrace();
729 }
730 finally
731 {
732 IO.close(contextInputStream);
733 }
734 return null;
735 }
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 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
782
783 protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
784 {
785
786
787
788 OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(
789 _wrapper.getParentClassLoaderForWebapps(),new WebAppContext(),contributor,BUNDLE_CLASS_LOADER_HELPER);
790 return webappClassLoader;
791 }
792
793
794
795
796
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 }