1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.osgi.boot;
20
21 import java.io.File;
22 import java.net.URL;
23 import java.util.Dictionary;
24 import java.util.Enumeration;
25 import java.util.HashMap;
26
27 import org.eclipse.jetty.deploy.App;
28 import org.eclipse.jetty.deploy.AppProvider;
29 import org.eclipse.jetty.deploy.DeploymentManager;
30 import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
31 import org.eclipse.jetty.osgi.boot.internal.webapp.OSGiWebappClassLoader;
32 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
33 import org.eclipse.jetty.server.handler.ContextHandler;
34 import org.eclipse.jetty.util.Loader;
35 import org.eclipse.jetty.util.component.AbstractLifeCycle;
36 import org.eclipse.jetty.util.log.Log;
37 import org.eclipse.jetty.util.log.Logger;
38 import org.eclipse.jetty.util.resource.JarResource;
39 import org.eclipse.jetty.util.resource.Resource;
40 import org.eclipse.jetty.webapp.Configuration;
41 import org.eclipse.jetty.webapp.WebAppContext;
42 import org.eclipse.jetty.xml.XmlConfiguration;
43 import org.osgi.framework.Bundle;
44 import org.osgi.framework.BundleContext;
45 import org.osgi.framework.ServiceReference;
46 import org.osgi.service.packageadmin.PackageAdmin;
47
48
49
50
51
52
53
54 public abstract class AbstractWebAppProvider extends AbstractLifeCycle implements AppProvider
55 {
56 private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
57
58
59
60
61
62
63
64 private static boolean annotationsAvailable()
65 {
66 boolean result = false;
67 try
68 {
69 Loader.loadClass(AbstractWebAppProvider.class,"org.eclipse.jetty.annotations.AnnotationConfiguration");
70 result = true;
71 LOG.debug("Annotation support detected");
72 }
73 catch (ClassNotFoundException e)
74 {
75 result = false;
76 LOG.debug("No annotation support detected");
77 }
78
79 return result;
80 }
81
82
83
84
85
86
87
88 private static boolean jndiAvailable()
89 {
90 try
91 {
92 Loader.loadClass(AbstractWebAppProvider.class, "org.eclipse.jetty.plus.jndi.Resource");
93 Loader.loadClass(AbstractWebAppProvider.class, "org.eclipse.jetty.plus.webapp.EnvConfiguration");
94 LOG.debug("JNDI support detected");
95 return true;
96 }
97 catch (ClassNotFoundException e)
98 {
99 LOG.debug("No JNDI support detected");
100 return false;
101 }
102 }
103
104
105 private boolean _parentLoaderPriority;
106
107 private String _defaultsDescriptor;
108
109 private boolean _extractWars = true;
110
111 private String _tldBundles;
112
113 private DeploymentManager _deploymentManager;
114
115 private String[] _configurationClasses;
116
117 private ServerInstanceWrapper _serverWrapper;
118
119
120
121
122
123
124
125
126
127 public class OSGiApp extends AbstractOSGiApp
128 {
129 private String _contextPath;
130 private String _webAppPath;
131 private WebAppContext _webApp;
132
133 public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
134 {
135 super(manager, provider, bundle, originId);
136 }
137
138 public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
139 {
140 super(manager, provider, bundle, properties, originId);
141 }
142
143 public void setWebAppContext (WebAppContext webApp)
144 {
145 _webApp = webApp;
146 }
147
148 public String getContextPath()
149 {
150 return _contextPath;
151 }
152
153 public void setContextPath(String contextPath)
154 {
155 this._contextPath = contextPath;
156 }
157
158 public String getBundlePath()
159 {
160 return _webAppPath;
161 }
162
163 public void setWebAppPath(String path)
164 {
165 this._webAppPath = path;
166 }
167
168
169 public ContextHandler createContextHandler()
170 throws Exception
171 {
172 if (_webApp != null)
173 {
174 configureWebApp();
175 return _webApp;
176 }
177
178 createWebApp();
179 return _webApp;
180 }
181
182
183
184 protected void createWebApp ()
185 throws Exception
186 {
187 _webApp = newWebApp();
188 configureWebApp();
189 }
190
191 protected WebAppContext newWebApp ()
192 {
193 WebAppContext webApp = new WebAppContext();
194 webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
195
196
197 String[] targets = webApp.getProtectedTargets();
198 String[] updatedTargets = null;
199 if (targets != null)
200 {
201 updatedTargets = new String[targets.length+OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
202 System.arraycopy(targets, 0, updatedTargets, 0, targets.length);
203 }
204 else
205 updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
206 System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
207 webApp.setProtectedTargets(updatedTargets);
208
209 return webApp;
210 }
211
212
213 public void configureWebApp()
214 throws Exception
215 {
216
217 _webApp.setContextPath(_contextPath);
218
219
220 _webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
221
222 String overrideBundleInstallLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
223 File bundleInstallLocation =
224 (overrideBundleInstallLocation == null
225 ? BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle)
226 : new File(overrideBundleInstallLocation));
227
228 if (LOG.isDebugEnabled())
229 {
230 LOG.debug("Bundle location is {}, install location: {}", _bundle.getLocation(), bundleInstallLocation);
231 }
232
233 URL url = null;
234 Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(bundleInstallLocation.toURI().toURL()));
235
236 if (rootResource.exists()&& !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:"))
237 {
238 Resource jarResource = JarResource.newJarResource(rootResource);
239 if (jarResource.exists() && jarResource.isDirectory())
240 rootResource = jarResource;
241 }
242
243
244 if (_webAppPath == null || _webAppPath.length() == 0 || ".".equals(_webAppPath))
245 {
246 url = bundleInstallLocation.toURI().toURL();
247 if (LOG.isDebugEnabled())
248 LOG.debug("Webapp base using bundle install location: {}", url);
249 }
250 else
251 {
252
253 if (_webAppPath.startsWith("/") || _webAppPath.startsWith("file:"))
254 {
255 url = new File(_webAppPath).toURI().toURL();
256 if (LOG.isDebugEnabled())
257 LOG.debug("Webapp base using absolute location: {}", url);
258 }
259 else if (bundleInstallLocation != null && bundleInstallLocation.isDirectory())
260 {
261 url = new File(bundleInstallLocation, _webAppPath).toURI().toURL();
262 if (LOG.isDebugEnabled())
263 LOG.debug("Webapp base using path relative to bundle unpacked install location: {}", url);
264 }
265 else if (bundleInstallLocation != null)
266 {
267 Enumeration<URL> urls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(_bundle, _webAppPath);
268 if (urls != null && urls.hasMoreElements())
269 {
270 url = urls.nextElement();
271 if (LOG.isDebugEnabled())
272 LOG.debug("Webapp base using path relative to packed bundle location: {}", url);
273 }
274 }
275 }
276
277 if (url == null)
278 {
279 throw new IllegalArgumentException("Unable to locate " + _webAppPath
280 + " in "
281 + (bundleInstallLocation != null ? bundleInstallLocation.getAbsolutePath() : "unlocated bundle '" + _bundle.getSymbolicName()+ "'"));
282 }
283
284
285
286 _webApp.setWar(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url).toString());
287
288
289 _webApp.setParentLoaderPriority(isParentLoaderPriority());
290 _webApp.setExtractWAR(isExtract());
291 _webApp.setConfigurationClasses(getConfigurationClasses());
292
293
294 if (getDefaultsDescriptor() != null)
295 _webApp.setDefaultsDescriptor(getDefaultsDescriptor());
296
297
298
299 String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
300 if (tmp != null)
301 _webApp.setExtraClasspath(tmp);
302
303
304 tmp = (String)_properties.get(OSGiWebappConstants.JETTY_WEB_XML_PATH);
305 if (tmp != null && tmp.trim().length() != 0)
306 {
307 File webXml = getFile (tmp, bundleInstallLocation);
308 if (webXml != null && webXml.exists())
309 _webApp.setDescriptor(webXml.getAbsolutePath());
310 }
311
312
313 tmp = (String)_properties.get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
314 if (tmp != null)
315 {
316 File defaultWebXml = getFile (tmp, bundleInstallLocation);
317 if (defaultWebXml != null)
318 {
319 if (defaultWebXml.exists())
320 _webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
321 else
322 LOG.warn(defaultWebXml.getAbsolutePath()+" does not exist");
323 }
324 }
325
326
327
328
329 String requireTldBundles = (String)_properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
330 String pathsToTldBundles = getPathsToRequiredBundles(requireTldBundles);
331
332
333
334
335 OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(_serverWrapper.getParentClassLoaderForWebapps(), _webApp, _bundle);
336
337 if (pathsToTldBundles != null)
338 webAppLoader.addClassPath(pathsToTldBundles);
339 _webApp.setClassLoader(webAppLoader);
340
341
342
343
344 applyMetaInfContextXml(rootResource, overrideBundleInstallLocation);
345
346 _webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles);
347
348
349
350 _webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, _bundle.getBundleContext());
351
352
353
354
355
356 _webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), _bundle.getBundleContext());
357
358
359
360
361
362 _webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, _bundle);
363 }
364
365 protected String getPathsToRequiredBundles (String requireTldBundles)
366 throws Exception
367 {
368 if (requireTldBundles == null) return null;
369
370 ServiceReference ref = _bundle.getBundleContext().getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
371 PackageAdmin packageAdmin = (ref == null) ? null : (PackageAdmin)_bundle.getBundleContext().getService(ref);
372 if (packageAdmin == null)
373 throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles");
374
375 StringBuilder paths = new StringBuilder();
376 String[] symbNames = requireTldBundles.split("[, ]");
377
378 for (String symbName : symbNames)
379 {
380 Bundle[] bs = packageAdmin.getBundles(symbName, null);
381 if (bs == null || bs.length == 0)
382 {
383 throw new IllegalArgumentException("Unable to locate the bundle '" + symbName
384 + "' specified by "
385 + OSGiWebappConstants.REQUIRE_TLD_BUNDLE
386 + " in manifest of "
387 + (_bundle == null ? "unknown" : _bundle.getSymbolicName()));
388 }
389
390 File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]);
391 if (paths.length() > 0) paths.append(", ");
392 paths.append(f.toURI().toURL().toString());
393 LOG.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI());
394 }
395
396 return paths.toString();
397 }
398
399
400 protected void applyMetaInfContextXml(Resource rootResource, String overrideBundleInstallLocation)
401 throws Exception
402 {
403 if (_bundle == null) return;
404 if (_webApp == null) return;
405
406 ClassLoader cl = Thread.currentThread().getContextClassLoader();
407 LOG.debug("Context classloader = " + cl);
408 try
409 {
410
411 Thread.currentThread().setContextClassLoader(_webApp.getClassLoader());
412
413
414
415 URL contextXmlUrl = _bundle.getEntry("/META-INF/jetty-webapp-context.xml");
416
417 if (contextXmlUrl == null)
418 {
419
420 if (_properties != null)
421 {
422 String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
423 if (tmp != null)
424 {
425 String[] filenames = tmp.split("[,;]");
426 if (filenames != null && filenames.length > 0)
427 {
428 String filename = filenames[0];
429 String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
430 if (jettyHome == null)
431 jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME);
432 Resource res = findFile(filename, jettyHome, overrideBundleInstallLocation, _bundle);
433 if (res != null)
434 contextXmlUrl = res.getURL();
435 }
436 }
437 }
438 }
439 if (contextXmlUrl == null) return;
440
441
442 LOG.info("Applying " + contextXmlUrl + " to " + _webApp);
443
444 XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl);
445 HashMap properties = new HashMap();
446 properties.put("Server", getDeploymentManager().getServer());
447 properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString());
448 properties.put(OSGiServerConstants.JETTY_HOME, getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME));
449 xmlConfiguration.getProperties().putAll(properties);
450 xmlConfiguration.configure(_webApp);
451 }
452 finally
453 {
454 Thread.currentThread().setContextClassLoader(cl);
455 }
456 }
457
458 private File getFile (String file, File bundleInstall)
459 {
460 if (file == null)
461 return null;
462
463 if (file.startsWith("/") || file.startsWith("file:/"))
464 return new File(file);
465 else
466 {
467
468
469 File f = new File (bundleInstall, file);
470 if (f.exists()) return f;
471 String jettyHome = (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
472 if (jettyHome != null)
473 return new File(jettyHome, file);
474 }
475
476 return null;
477 }
478 }
479
480
481 public AbstractWebAppProvider (ServerInstanceWrapper wrapper)
482 {
483 _serverWrapper = wrapper;
484 }
485
486
487
488
489
490
491
492
493
494 public boolean isParentLoaderPriority()
495 {
496 return _parentLoaderPriority;
497 }
498
499
500
501
502
503
504
505 public void setParentLoaderPriority(boolean parentLoaderPriority)
506 {
507 _parentLoaderPriority = parentLoaderPriority;
508 }
509
510
511
512
513
514
515
516 public String getDefaultsDescriptor()
517 {
518 return _defaultsDescriptor;
519 }
520
521
522
523
524
525
526
527 public void setDefaultsDescriptor(String defaultsDescriptor)
528 {
529 _defaultsDescriptor = defaultsDescriptor;
530 }
531
532
533
534 public boolean isExtract()
535 {
536 return _extractWars;
537 }
538
539
540
541 public void setExtract(boolean extract)
542 {
543 _extractWars = extract;
544 }
545
546
547
548
549
550
551
552 public void setTldBundles(String tldBundles)
553 {
554 _tldBundles = tldBundles;
555 }
556
557
558
559
560
561
562
563 public String getTldBundles()
564 {
565 return _tldBundles;
566 }
567
568
569
570
571
572 public void setConfigurationClasses(String[] configurations)
573 {
574 _configurationClasses = configurations == null ? null : (String[]) configurations.clone();
575 }
576
577
578 public String[] getConfigurationClasses()
579 {
580 if (_configurationClasses != null)
581 return _configurationClasses;
582
583 Configuration.ClassList defaults = Configuration.ClassList.serverDefault(_serverWrapper.getServer());
584
585
586 if (annotationsAvailable())
587 defaults.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
588 "org.eclipse.jetty.osgi.annotations.AnnotationConfiguration");
589
590
591 if (jndiAvailable())
592 defaults.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
593 "org.eclipse.jetty.plus.webapp.EnvConfiguration",
594 "org.eclipse.jetty.plus.webapp.PlusConfiguration");
595 String[] asArray = new String[defaults.size()];
596 return defaults.toArray(asArray);
597 }
598
599
600
601 public void setServerInstanceWrapper(ServerInstanceWrapper wrapper)
602 {
603 _serverWrapper = wrapper;
604 }
605
606 public ServerInstanceWrapper getServerInstanceWrapper()
607 {
608 return _serverWrapper;
609 }
610
611
612 public DeploymentManager getDeploymentManager()
613 {
614 return _deploymentManager;
615 }
616
617
618
619
620
621 public void setDeploymentManager(DeploymentManager deploymentManager)
622 {
623 _deploymentManager = deploymentManager;
624 }
625
626
627
628 public ContextHandler createContextHandler(App app) throws Exception
629 {
630 if (app == null)
631 return null;
632 if (!(app instanceof OSGiApp))
633 throw new IllegalStateException(app+" is not a BundleApp");
634
635
636 ContextHandler ch = ((OSGiApp)app).createContextHandler();
637 return ch;
638 }
639
640
641
642 public static String getOriginId(Bundle contributor, String path)
643 {
644 return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (path.startsWith("/") ? path : "/" + path);
645 }
646
647 }