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 // ========================================================================
13 package org.eclipse.jetty.osgi.boot.internal.webapp;
14
15 import java.net.URL;
16 import java.util.Dictionary;
17
18 import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
19 import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
20 import org.osgi.framework.Bundle;
21 import org.osgi.framework.BundleContext;
22 import org.osgi.framework.BundleEvent;
23 import org.osgi.framework.BundleListener;
24
25 /**
26 * Support bundles that declare the webapp directly through headers in their
27 * manifest.
28 * <p>
29 * Those headers will define a new WebApplication:
30 * <ul>
31 * <li>Web-ContextPath</li>
32 * <li>Jetty-WarFolderPath</li>
33 * </ul>
34 * </p>
35 * <p>
36 * Those headers will define a new app started via a jetty-context or a list of
37 * them. ',' column is the separator between the various context files.
38 * <ul>
39 * <li>Jetty-ContextFilePath</li>
40 * </ul>
41 * </p>
42 * And generate a jetty WebAppContext or another ContextHandler then registers
43 * it as service. Kind of simpler than declarative services and their xml files.
44 * Also avoid having the contributing bundle depend on jetty's package for
45 * WebApp.
46 *
47 * @author hmalphettes
48 */
49 public class JettyContextHandlerExtender implements BundleListener
50 {
51
52 /**
53 * Receives notification that a bundle has had a lifecycle change.
54 *
55 * @param event
56 * The <code>BundleEvent</code>.
57 */
58 public void bundleChanged(BundleEvent event)
59 {
60 switch (event.getType())
61 {
62 case BundleEvent.STARTED:
63 register(event.getBundle());
64 break;
65 case BundleEvent.STOPPING:
66 unregister(event.getBundle());
67 break;
68 }
69 }
70
71 /**
72 *
73 */
74 public void init(BundleContext context)
75 {
76 Bundle bundles[] = context.getBundles();
77 for (int i = 0; i < bundles.length; i++)
78 {
79 if ((bundles[i].getState() & (Bundle.STARTING | Bundle.ACTIVE)) != 0)
80 {
81 register(bundles[i]);
82 }
83 }
84 }
85
86 private void register(Bundle bundle)
87 {
88 Dictionary<?, ?> dic = bundle.getHeaders();
89 String warFolderRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
90 if (warFolderRelativePath != null)
91 {
92 String contextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
93 if (contextPath == null || !contextPath.startsWith("/"))
94 {
95 throw new IllegalArgumentException();
96 }
97 // create the corresponding service and publish it in the context of
98 // the contributor bundle.
99 try
100 {
101 JettyBootstrapActivator.registerWebapplication(bundle,warFolderRelativePath,contextPath);
102 }
103 catch (Throwable e)
104 {
105 // TODO Auto-generated catch block
106 e.printStackTrace();
107 }
108 }
109 else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null)
110 {
111 String contextFileRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
112 if (contextFileRelativePath == null)
113 {
114 // nothing to register here.
115 return;
116 }
117 // support for multiple webapps in the same bundle:
118 String[] pathes = contextFileRelativePath.split(",;");
119 for (String path : pathes)
120 {
121 try
122 {
123 JettyBootstrapActivator.registerContext(bundle,path.trim());
124 }
125 catch (Throwable e)
126 {
127 // TODO Auto-generated catch block
128 e.printStackTrace();
129 }
130 }
131 }
132 else
133 {
134 // support for OSGi-RFC66; disclaimer, no access to the actual
135 // (draft) of the spec: just a couple of posts on the
136 // world-wide-web.
137 URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
138 if (rfc66Webxml == null)
139 {
140 return;// no webapp in here
141 }
142 // this is risky: should we make sure that there is no classes and
143 // jars directly available
144 // at the root of the of the bundle: otherwise they are accessible
145 // through the browser. we should enforce that the whole classpath
146 // is
147 // pointing to files and folders inside WEB-INF. We should
148 // filter-out
149 // META-INF too
150 String rfc66ContextPath = getWebContextPath(bundle,dic);
151 try
152 {
153 JettyBootstrapActivator.registerWebapplication(bundle,".",rfc66ContextPath);
154 }
155 catch (Throwable e)
156 {
157 // TODO Auto-generated catch block
158 e.printStackTrace();
159 }
160 }
161 }
162
163 private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic)
164 {
165 String rfc66ContextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
166 if (rfc66ContextPath == null)
167 {
168 // extract from the last token of the bundle's location:
169 // (really ?
170 // could consider processing the symbolic name as an alternative
171 // the location will often reflect the version.
172 // maybe this is relevant when the file is a war)
173 String location = bundle.getLocation();
174 String toks[] = location.replace('\\','/').split("/");
175 rfc66ContextPath = toks[toks.length - 1];
176 // remove .jar, .war etc:
177 int lastDot = rfc66ContextPath.lastIndexOf('.');
178 if (lastDot != -1)
179 {
180 rfc66ContextPath = rfc66ContextPath.substring(0,lastDot);
181 }
182 }
183 if (!rfc66ContextPath.startsWith("/"))
184 {
185 rfc66ContextPath = "/" + rfc66ContextPath;
186 }
187 return rfc66ContextPath;
188 }
189
190 private void unregister(Bundle bundle)
191 {
192 // nothing to do: when the bundle is stopped, each one of its service
193 // reference is also stopped and that is what we use to stop the
194 // corresponding
195 // webapps registered in that bundle.
196 }
197
198 }