001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.geronimo.jetty6.deployment;
019
020 import java.io.File;
021 import java.io.FileNotFoundException;
022 import java.io.IOException;
023 import static java.lang.Boolean.FALSE;
024 import static java.lang.Boolean.TRUE;
025 import java.net.URL;
026 import java.security.PermissionCollection;
027 import java.util.ArrayList;
028 import java.util.Arrays;
029 import java.util.Collection;
030 import java.util.Collections;
031 import java.util.Comparator;
032 import java.util.HashMap;
033 import java.util.HashSet;
034 import java.util.List;
035 import java.util.Map;
036 import java.util.Set;
037 import java.util.TreeSet;
038 import java.util.jar.JarFile;
039
040 import javax.management.ObjectName;
041 import javax.servlet.Servlet;
042
043 import org.apache.commons.logging.Log;
044 import org.apache.commons.logging.LogFactory;
045 import org.apache.geronimo.common.DeploymentException;
046 import org.apache.geronimo.deployment.ModuleIDBuilder;
047 import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
048 import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
049 import org.apache.geronimo.deployment.service.EnvironmentBuilder;
050 import org.apache.geronimo.deployment.util.DeploymentUtil;
051 import org.apache.geronimo.deployment.xbeans.EnvironmentType;
052 import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
053 import org.apache.geronimo.gbean.AbstractName;
054 import org.apache.geronimo.gbean.AbstractNameQuery;
055 import org.apache.geronimo.gbean.GBeanData;
056 import org.apache.geronimo.gbean.GBeanInfo;
057 import org.apache.geronimo.gbean.GBeanInfoBuilder;
058 import org.apache.geronimo.gbean.GBeanLifecycle;
059 import org.apache.geronimo.j2ee.deployment.EARContext;
060 import org.apache.geronimo.j2ee.deployment.Module;
061 import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
062 import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
063 import org.apache.geronimo.j2ee.deployment.NamingBuilder;
064 import org.apache.geronimo.j2ee.deployment.WebModule;
065 import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
066 import org.apache.geronimo.j2ee.deployment.annotation.AnnotatedWebApp;
067 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
068 import org.apache.geronimo.jetty6.Host;
069 import org.apache.geronimo.jetty6.JettyDefaultServletHolder;
070 import org.apache.geronimo.jetty6.JettyFilterHolder;
071 import org.apache.geronimo.jetty6.JettyFilterMapping;
072 import org.apache.geronimo.jetty6.JettyServletHolder;
073 import org.apache.geronimo.jetty6.JettyWebAppContext;
074 import org.apache.geronimo.jetty6.NonAuthenticator;
075 import org.apache.geronimo.jetty6.JettyJspServletHolder;
076 import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
077 import org.apache.geronimo.kernel.GBeanNotFoundException;
078 import org.apache.geronimo.kernel.Kernel;
079 import org.apache.geronimo.kernel.Naming;
080 import org.apache.geronimo.kernel.config.ConfigurationData;
081 import org.apache.geronimo.kernel.repository.Environment;
082 import org.apache.geronimo.naming.deployment.ENCConfigBuilder;
083 import org.apache.geronimo.naming.deployment.GBeanResourceEnvironmentBuilder;
084 import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter;
085 import org.apache.geronimo.security.jacc.ComponentPermissions;
086 import org.apache.geronimo.web.deployment.GenericToSpecificPlanConverter;
087 import org.apache.geronimo.web25.deployment.AbstractWebModuleBuilder;
088 import org.apache.geronimo.xbeans.geronimo.j2ee.GerClusteringDocument;
089 import org.apache.geronimo.xbeans.geronimo.web.jetty.JettyWebAppDocument;
090 import org.apache.geronimo.xbeans.geronimo.web.jetty.JettyWebAppType;
091 import org.apache.geronimo.xbeans.geronimo.web.jetty.config.GerJettyDocument;
092 import org.apache.geronimo.xbeans.javaee.DispatcherType;
093 import org.apache.geronimo.xbeans.javaee.ErrorPageType;
094 import org.apache.geronimo.xbeans.javaee.FilterMappingType;
095 import org.apache.geronimo.xbeans.javaee.FilterType;
096 import org.apache.geronimo.xbeans.javaee.FormLoginConfigType;
097 import org.apache.geronimo.xbeans.javaee.JspConfigType;
098 import org.apache.geronimo.xbeans.javaee.ListenerType;
099 import org.apache.geronimo.xbeans.javaee.LocaleEncodingMappingListType;
100 import org.apache.geronimo.xbeans.javaee.LocaleEncodingMappingType;
101 import org.apache.geronimo.xbeans.javaee.LoginConfigType;
102 import org.apache.geronimo.xbeans.javaee.MimeMappingType;
103 import org.apache.geronimo.xbeans.javaee.ParamValueType;
104 import org.apache.geronimo.xbeans.javaee.ServletMappingType;
105 import org.apache.geronimo.xbeans.javaee.ServletType;
106 import org.apache.geronimo.xbeans.javaee.TaglibType;
107 import org.apache.geronimo.xbeans.javaee.UrlPatternType;
108 import org.apache.geronimo.xbeans.javaee.WebAppDocument;
109 import org.apache.geronimo.xbeans.javaee.WebAppType;
110 import org.apache.geronimo.xbeans.javaee.WelcomeFileListType;
111 import org.apache.geronimo.xbeans.javaee.JspPropertyGroupType;
112 import org.apache.xmlbeans.XmlException;
113 import org.apache.xmlbeans.XmlObject;
114 import org.mortbay.jetty.security.BasicAuthenticator;
115 import org.mortbay.jetty.security.ClientCertAuthenticator;
116 import org.mortbay.jetty.security.DigestAuthenticator;
117 import org.mortbay.jetty.security.FormAuthenticator;
118
119
120 /**
121 * @version $Rev:385659 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
122 */
123 public class JettyModuleBuilder extends AbstractWebModuleBuilder implements GBeanLifecycle {
124 private final static Log log = LogFactory.getLog(JettyModuleBuilder.class);
125 private static final Map<String, String> NAMESPACE_UPDATES = new HashMap<String, String>();
126 static {
127 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
128 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web-1.1", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
129 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web-1.2", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
130 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web-2.0", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
131 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web/jetty", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.1");
132 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-1.1", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
133 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-1.2", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
134 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
135 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.1", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
136 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web/jetty/config", "http://geronimo.apache.org/xml/ns/web/jetty/config-1.0.1");
137 NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web/jetty/config-1.0", "http://geronimo.apache.org/xml/ns/web/jetty/config-1.0.1");
138 }
139
140 private final Environment defaultEnvironment;
141 private final AbstractNameQuery jettyContainerObjectName;
142 private final JettyJspServletHolder jspServlet;
143 private final Collection defaultServlets;
144 private final Collection defaultFilters;
145 private final Collection defaultFilterMappings;
146 private final GBeanData pojoWebServiceTemplate;
147
148 protected final NamespaceDrivenBuilderCollection clusteringBuilders;
149
150 private final List<String> defaultWelcomeFiles;
151 private final Integer defaultSessionTimeoutSeconds;
152 private final Map<String, String> defaultLocaleEncodingMappings;
153 private final Map<String, String> defaultMimeTypeMappings;
154
155 private static final String JETTY_NAMESPACE = JettyWebAppDocument.type.getDocumentElementName().getNamespaceURI();
156
157 public JettyModuleBuilder(Environment defaultEnvironment,
158 Integer defaultSessionTimeoutSeconds,
159 List<String> defaultWelcomeFiles,
160 AbstractNameQuery jettyContainerName,
161 JettyJspServletHolder jspServlet,
162 Collection defaultServlets,
163 Collection defaultFilters,
164 Collection defaultFilterMappings,
165 Map<String, String> defaultLocaleEncodingMappings,
166 Map<String, String> defaultMimeTypeMappings,
167 Object pojoWebServiceTemplate,
168 Collection<WebServiceBuilder> webServiceBuilder,
169 Collection clusteringBuilders,
170 Collection securityBuilders,
171 Collection serviceBuilders,
172 NamingBuilder namingBuilders,
173 Collection<ModuleBuilderExtension> moduleBuilderExtensions,
174 ResourceEnvironmentSetter resourceEnvironmentSetter,
175 Kernel kernel) throws GBeanNotFoundException {
176 super(kernel, securityBuilders, serviceBuilders, namingBuilders, resourceEnvironmentSetter, webServiceBuilder, moduleBuilderExtensions);
177 this.defaultEnvironment = defaultEnvironment;
178 this.defaultSessionTimeoutSeconds = (defaultSessionTimeoutSeconds == null) ? 30 * 60 : defaultSessionTimeoutSeconds;
179 this.jettyContainerObjectName = jettyContainerName;
180 this.jspServlet = jspServlet;
181 this.defaultServlets = defaultServlets;
182 this.defaultFilters = defaultFilters;
183 this.defaultFilterMappings = defaultFilterMappings;
184 this.pojoWebServiceTemplate = getGBeanData(kernel, pojoWebServiceTemplate);
185 this.clusteringBuilders = new NamespaceDrivenBuilderCollection(clusteringBuilders, GerClusteringDocument.type.getDocumentElementName());
186
187 this.defaultWelcomeFiles = defaultWelcomeFiles == null ? new ArrayList<String>() : defaultWelcomeFiles;
188 this.defaultLocaleEncodingMappings = defaultLocaleEncodingMappings == null ? new HashMap<String, String>() : defaultLocaleEncodingMappings;
189 this.defaultMimeTypeMappings = defaultMimeTypeMappings == null ? new HashMap<String, String>() : defaultMimeTypeMappings;
190 }
191
192 public void doStart() throws Exception {
193 XmlBeansUtil.registerNamespaceUpdates(NAMESPACE_UPDATES);
194 }
195
196 public void doStop() {
197 XmlBeansUtil.unregisterNamespaceUpdates(NAMESPACE_UPDATES);
198 }
199
200 public void doFail() {
201 doStop();
202 }
203
204 private static GBeanData getGBeanData(Kernel kernel, Object template) throws GBeanNotFoundException {
205 if (template == null) {
206 return null;
207 }
208 AbstractName templateName = kernel.getAbstractNameFor(template);
209 return kernel.getGBeanData(templateName);
210 }
211
212 protected Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, boolean standAlone, String contextRoot, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
213 assert moduleFile != null : "moduleFile is null";
214 assert targetPath != null : "targetPath is null";
215 assert !targetPath.endsWith("/") : "targetPath must not end with a '/'";
216
217 // parse the spec dd
218 String specDD = null;
219 WebAppType webApp = null;
220 try {
221 if (specDDUrl == null) {
222 specDDUrl = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/web.xml");
223 }
224
225 // read in the entire specDD as a string, we need this for getDeploymentDescriptor
226 // on the J2ee management object
227 specDD = DeploymentUtil.readAll(specDDUrl);
228
229 // we found web.xml, if it won't parse that's an error.
230 XmlObject parsed = XmlBeansUtil.parse(specDD);
231 WebAppDocument webAppDoc = convertToServletSchema(parsed);
232 webApp = webAppDoc.getWebApp();
233 check(webApp);
234 } catch (XmlException e) {
235 // Output the target path in the error to make it clearer to the user which webapp
236 // has the problem. The targetPath is used, as moduleFile may have an unhelpful
237 // value such as C:\geronimo-1.1\var\temp\geronimo-deploymentUtil22826.tmpdir
238 throw new DeploymentException("Error parsing web.xml for " + targetPath, e);
239 } catch (Exception e) {
240 if (!moduleFile.getName().endsWith(".war")) {
241 //not for us
242 return null;
243 }
244 //else ignore as jee5 allows optional spec dd for .war's
245 }
246
247 if (webApp == null) {
248 webApp = WebAppType.Factory.newInstance();
249 }
250
251 // parse vendor dd
252 JettyWebAppType jettyWebApp = getJettyWebApp(plan, moduleFile, standAlone, targetPath, webApp);
253 contextRoot = getContextRoot(jettyWebApp, contextRoot, webApp, standAlone, moduleFile, targetPath);
254
255 EnvironmentType environmentType = jettyWebApp.getEnvironment();
256 Environment environment = EnvironmentBuilder.buildEnvironment(environmentType, defaultEnvironment);
257
258 Boolean distributable = webApp.getDistributableArray().length == 1 ? TRUE : FALSE;
259 if (TRUE == distributable) {
260 clusteringBuilders.buildEnvironment(jettyWebApp, environment);
261 }
262
263 // Note: logic elsewhere depends on the default artifact ID being the file name less extension (ConfigIDExtractor)
264 String warName = new File(moduleFile.getName()).getName();
265 if (warName.lastIndexOf('.') > -1) {
266 warName = warName.substring(0, warName.lastIndexOf('.'));
267 }
268 idBuilder.resolve(environment, warName, "war");
269
270 AbstractName moduleName;
271 if (earName == null) {
272 earName = naming.createRootName(environment.getConfigId(), NameFactory.NULL, NameFactory.J2EE_APPLICATION);
273 moduleName = naming.createChildName(earName, environment.getConfigId().toString(), NameFactory.WEB_MODULE);
274 } else {
275 moduleName = naming.createChildName(earName, targetPath, NameFactory.WEB_MODULE);
276 }
277
278 // Create the AnnotatedApp interface for the WebModule
279 AnnotatedWebApp annotatedWebApp = new AnnotatedWebApp(webApp);
280
281 WebModule module = new WebModule(standAlone, moduleName, environment, moduleFile, targetPath, webApp, jettyWebApp, specDD, contextRoot, JETTY_NAMESPACE, annotatedWebApp);
282
283 for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
284 mbe.createModule(module, plan, moduleFile, targetPath, specDDUrl, environment, contextRoot, earName, naming, idBuilder);
285 }
286 return module;
287 }
288
289 String getContextRoot(JettyWebAppType jettyWebApp, String contextRoot, WebAppType webApp, boolean standAlone, JarFile moduleFile, String targetPath) {
290 if (jettyWebApp.isSetContextRoot()) {
291 contextRoot = jettyWebApp.getContextRoot();
292 } else if (contextRoot == null || contextRoot.trim().equals("")) {
293 contextRoot = determineDefaultContextRoot(webApp, standAlone, moduleFile, targetPath);
294 }
295
296 contextRoot = contextRoot.trim();
297 return contextRoot;
298 }
299
300 JettyWebAppType getJettyWebApp(Object plan, JarFile moduleFile, boolean standAlone, String targetPath, WebAppType webApp) throws DeploymentException {
301 XmlObject rawPlan = null;
302 try {
303 // load the geronimo-web.xml from either the supplied plan or from the earFile
304 try {
305 if (plan instanceof XmlObject) {
306 rawPlan = (XmlObject) plan;
307 } else {
308 if (plan != null) {
309 rawPlan = XmlBeansUtil.parse(((File) plan).toURL(), getClass().getClassLoader());
310 } else {
311 URL path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-web.xml");
312 try {
313 rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
314 } catch (FileNotFoundException e) {
315 path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-jetty.xml");
316 try {
317 rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
318 } catch (FileNotFoundException e1) {
319 log.warn("Web application " + targetPath + " does not contain a WEB-INF/geronimo-web.xml deployment plan. This may or may not be a problem, depending on whether you have things like resource references that need to be resolved. You can also give the deployer a separate deployment plan file on the command line.");
320 }
321 }
322 }
323 }
324 } catch (IOException e) {
325 log.warn(e);
326 }
327
328 JettyWebAppType jettyWebApp;
329 if (rawPlan != null) {
330 XmlObject webPlan = new GenericToSpecificPlanConverter(GerJettyDocument.type.getDocumentElementName().getNamespaceURI(),
331 JettyWebAppDocument.type.getDocumentElementName().getNamespaceURI(), "jetty").convertToSpecificPlan(rawPlan);
332 jettyWebApp = (JettyWebAppType) webPlan.changeType(JettyWebAppType.type);
333 XmlBeansUtil.validateDD(jettyWebApp);
334 } else {
335 jettyWebApp = createDefaultPlan();
336 }
337 return jettyWebApp;
338 } catch (XmlException e) {
339 throw new DeploymentException("xml problem for web app " + targetPath, e);
340 }
341 }
342
343 private JettyWebAppType createDefaultPlan() {
344 return JettyWebAppType.Factory.newInstance();
345 }
346
347 public void initContext(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
348 JettyWebAppType gerWebApp = (JettyWebAppType) module.getVendorDD();
349 boolean hasSecurityRealmName = gerWebApp.isSetSecurityRealmName();
350 basicInitContext(earContext, module, gerWebApp, hasSecurityRealmName);
351 for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
352 mbe.initContext(earContext, module, cl);
353 }
354 }
355
356 public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repository) throws DeploymentException {
357 EARContext moduleContext = module.getEarContext();
358 AbstractName moduleName = moduleContext.getModuleName();
359 WebModule webModule = (WebModule) module;
360
361 WebAppType webApp = (WebAppType) webModule.getSpecDD();
362 JettyWebAppType jettyWebApp = (JettyWebAppType) webModule.getVendorDD();
363 GBeanData webModuleData = new GBeanData(moduleName, JettyWebAppContext.GBEAN_INFO);
364
365 configureBasicWebModuleAttributes(webApp, jettyWebApp, moduleContext, earContext, webModule, webModuleData);
366
367 // unsharableResources, applicationManagedSecurityResources
368 GBeanResourceEnvironmentBuilder rebuilder = new GBeanResourceEnvironmentBuilder(webModuleData);
369 //N.B. use earContext not moduleContext
370 //TODO fix this for javaee 5 !!!
371 resourceEnvironmentSetter.setResourceEnvironment(rebuilder, webApp.getResourceRefArray(), jettyWebApp.getResourceRefArray());
372 try {
373 moduleContext.addGBean(webModuleData);
374 Set<String> securityRoles = collectRoleNames(webApp);
375 Map<String, PermissionCollection> rolePermissions = new HashMap<String, PermissionCollection>();
376
377 // configure hosts and virtual-hosts
378 configureHosts(earContext, jettyWebApp, webModuleData);
379
380
381 String contextPath = webModule.getContextRoot();
382 if (contextPath == null) {
383 throw new DeploymentException("null contextPath");
384 }
385 if (!contextPath.startsWith("/")) {
386 contextPath = "/" + contextPath;
387 }
388 webModuleData.setAttribute("contextPath", contextPath);
389
390 if (jettyWebApp.isSetWorkDir()) {
391 String workDir = jettyWebApp.getWorkDir();
392 webModuleData.setAttribute("workDir", workDir);
393 }
394
395 if (jettyWebApp.isSetWebContainer()) {
396 AbstractNameQuery webContainerName = ENCConfigBuilder.getGBeanQuery(NameFactory.GERONIMO_SERVICE, jettyWebApp.getWebContainer());
397 webModuleData.setReferencePattern("JettyContainer", webContainerName);
398 } else {
399 webModuleData.setReferencePattern("JettyContainer", jettyContainerObjectName);
400 }
401 //stuff that jetty used to do
402 if (webApp.getDisplayNameArray().length > 0) {
403 webModuleData.setAttribute("displayName", webApp.getDisplayNameArray()[0].getStringValue());
404 }
405
406 // configure context parameters.
407 configureContextParams(webApp, webModuleData);
408
409 // configure listeners.
410 configureListeners(webApp, webModuleData);
411
412 webModuleData.setAttribute(JettyWebAppContext.GBEAN_ATTR_SESSION_TIMEOUT,
413 (webApp.getSessionConfigArray().length == 1 && webApp.getSessionConfigArray(0).getSessionTimeout() != null) ?
414 webApp.getSessionConfigArray(0).getSessionTimeout().getBigIntegerValue().intValue() * 60 :
415 defaultSessionTimeoutSeconds);
416
417 Boolean distributable = webApp.getDistributableArray().length == 1 ? TRUE : FALSE;
418 webModuleData.setAttribute("distributable", distributable);
419 if (TRUE == distributable) {
420 clusteringBuilders.build(jettyWebApp, earContext, moduleContext);
421 if (webModuleData.getReferencePatterns(JettyWebAppContext.GBEAN_REF_SESSION_HANDLER_FACTORY) == null) {
422 log.warn("No clustering builders configured: app will not be clustered");
423 configureNoClustering(moduleContext, webModuleData);
424 }
425 } else {
426 configureNoClustering(moduleContext, webModuleData);
427 }
428
429 // configure mime mappings.
430 configureMimeMappings(webApp, webModuleData);
431
432 // configure welcome file lists.
433 configureWelcomeFileLists(webApp, webModuleData);
434
435 // configure local encoding mapping lists.
436 configureLocaleEncodingMappingLists(webApp, webModuleData);
437
438 // configure error pages.
439 configureErrorPages(webApp, webModuleData);
440
441 // configure tag libs.
442 Set<String> knownServletMappings = new HashSet<String>();
443 Map<String, Set<String>> servletMappings = new HashMap<String, Set<String>>();
444 if (jspServlet != null) {
445 configureTagLibs(module, webApp, webModuleData, servletMappings, knownServletMappings, jspServlet.getServletName());
446 GBeanData jspServletData = configureDefaultServlet(jspServlet, earContext, moduleName, knownServletMappings, moduleContext);
447 Set<String> jspMappings = (Set<String>) jspServletData.getAttribute("servletMappings");
448 jspMappings.addAll(knownServletMappings);
449 jspServletData.setAttribute("servletMappings", jspMappings);
450 }
451
452 // configure login configs.
453 configureLoginConfigs(module, webApp, jettyWebApp, webModuleData);
454
455 // Make sure that servlet mappings point to available servlets and never add a duplicate pattern.
456
457 buildServletMappings(module, webApp, servletMappings, knownServletMappings);
458
459 //"previous" filter mapping for linked list to keep dd's ordering.
460 AbstractName previous = null;
461
462 //add default filters
463 if (defaultFilters != null) {
464 previous = addDefaultFiltersGBeans(earContext, moduleContext, moduleName, previous);
465 }
466
467 //add default filtermappings
468 // if (defaultFilterMappings != null) {
469 // for (Iterator iterator = defaultFilterMappings.iterator(); iterator.hasNext();) {
470 // Object defaultFilterMapping = iterator.next();
471 // GBeanData filterMappingGBeanData = getGBeanData(kernel, defaultFilterMapping);
472 // String filterName = (String) filterMappingGBeanData.getAttribute("filterName");
473 // ObjectName defaultFilterMappingObjectName;
474 // if (filterMappingGBeanData.getAttribute("urlPattern") != null) {
475 // String urlPattern = (String) filterMappingGBeanData.getAttribute("urlPattern");
476 // defaultFilterMappingObjectName = NameFactory.getWebFilterMappingName(null, null, null, null, filterName, null, urlPattern, moduleName);
477 // } else {
478 // Set servletNames = filterMappingGBeanData.getReferencePatterns("Servlet");
479 // if (servletNames == null || servletNames.size() != 1) {
480 // throw new DeploymentException("Exactly one servlet name must be supplied");
481 // }
482 // ObjectName servletObjectName = (ObjectName) servletNames.iterator().next();
483 // String servletName = servletObjectName.getKeyProperty("name");
484 // defaultFilterMappingObjectName = NameFactory.getWebFilterMappingName(null, null, null, null, filterName, servletName, null, moduleName);
485 // }
486 // filterMappingGBeanData.setName(defaultFilterMappingObjectName);
487 // filterMappingGBeanData.setReferencePattern("JettyFilterMappingRegistration", webModuleName);
488 // moduleContext.addGBean(filterMappingGBeanData);
489 // }
490 // }
491
492 // add filter mapping GBeans.
493 addFilterMappingsGBeans(earContext, moduleContext, moduleName, webApp, previous);
494
495 // add filter GBeans.
496 addFiltersGBeans(earContext, moduleContext, moduleName, webApp);
497
498 //add default servlets
499 if (defaultServlets != null) {
500 addDefaultServletsGBeans(earContext, moduleContext, moduleName, knownServletMappings);
501 }
502
503 //set up servlet gbeans.
504 ServletType[] servletTypes = webApp.getServletArray();
505 addServlets(moduleName, webModule, servletTypes, servletMappings, securityRoles, rolePermissions, moduleContext);
506
507 if (jettyWebApp.isSetSecurityRealmName()) {
508 configureSecurityRealm(earContext, webApp, jettyWebApp, webModuleData, securityRoles, rolePermissions);
509 }
510
511 //See Jetty-386, GERONIMO-3738
512 if (jettyWebApp.getCompactPath()) {
513 webModuleData.setAttribute("compactPath", Boolean.TRUE);
514 }
515
516 //TODO this may definitely not be the best place for this!
517 for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
518 mbe.addGBeans(earContext, module, cl, repository);
519 }
520
521 //not truly metadata complete until MBEs have run
522 if (!webApp.getMetadataComplete()) {
523 webApp.setMetadataComplete(true);
524 module.setOriginalSpecDD(module.getSpecDD().toString());
525 }
526 webModuleData.setAttribute("deploymentDescriptor", module.getOriginalSpecDD());
527
528 if (!module.isStandAlone()) {
529 ConfigurationData moduleConfigurationData = moduleContext.getConfigurationData();
530 earContext.addChildConfiguration(module.getTargetPath(), moduleConfigurationData);
531 }
532 } catch (DeploymentException de) {
533 throw de;
534 } catch (Exception e) {
535 throw new DeploymentException("Unable to initialize webapp GBean for " + module.getName(), e);
536 }
537
538 }
539
540 private void configureNoClustering(EARContext moduleContext, GBeanData webModuleData) throws GBeanAlreadyExistsException {
541 // AbstractName name = moduleContext.getNaming().createChildName(moduleContext.getModuleName(),
542 // "DefaultWebApplicationHandlerFactory",
543 // NameFactory.GERONIMO_SERVICE);
544 // GBeanData beanData = new GBeanData(name, DefaultWebApplicationHandlerFactory.GBEAN_INFO);
545 // webModuleData.setReferencePattern(JettyWebAppContext.GBEAN_REF_WEB_APPLICATION_HANDLER_FACTORY, name);
546 // moduleContext.addGBean(beanData);
547 }
548
549 private void configureSecurityRealm(EARContext earContext, WebAppType webApp, JettyWebAppType jettyWebApp, GBeanData webModuleData, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) throws DeploymentException {
550 AbstractName moduleName = webModuleData.getAbstractName();
551 if (earContext.getSecurityConfiguration() == null) {
552 throw new DeploymentException("You have specified a <security-realm-name> for the webapp " + moduleName + " but no <security> configuration (role mapping) is supplied in the Geronimo plan for the web application (or the Geronimo plan for the EAR if the web app is in an EAR)");
553 }
554 String securityRealmName = jettyWebApp.getSecurityRealmName().trim();
555 webModuleData.setAttribute("securityRealmName", securityRealmName);
556 webModuleData.setReferencePattern("RunAsSource", earContext.getJaccManagerName());
557
558 /**
559 * TODO - go back to commented version when possible.
560 */
561 String policyContextID = moduleName.toString().replaceAll("[, :]", "_");
562 //String policyContextID = webModuleName.getCanonicalName();
563 webModuleData.setAttribute("policyContextID", policyContextID);
564
565 ComponentPermissions componentPermissions = buildSpecSecurityConfig(webApp, securityRoles, rolePermissions);
566
567 earContext.addSecurityContext(policyContextID, componentPermissions);
568 }
569
570 private void addDefaultServletsGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, Set knownServletMappings) throws GBeanNotFoundException, GBeanAlreadyExistsException {
571 for (Object defaultServlet : defaultServlets) {
572 configureDefaultServlet(defaultServlet, earContext, moduleName, knownServletMappings, moduleContext);
573 }
574 }
575
576 private GBeanData configureDefaultServlet(Object defaultServlet, EARContext earContext, AbstractName moduleName, Set knownServletMappings, EARContext moduleContext) throws GBeanNotFoundException, GBeanAlreadyExistsException {
577 GBeanData servletGBeanData = getGBeanData(kernel, defaultServlet);
578 AbstractName defaultServletObjectName = earContext.getNaming().createChildName(moduleName, (String) servletGBeanData.getAttribute("servletName"), NameFactory.SERVLET);
579 servletGBeanData.setAbstractName(defaultServletObjectName);
580 servletGBeanData.setReferencePattern("JettyServletRegistration", moduleName);
581 Set<String> defaultServletMappings = new HashSet<String>((Collection<String>) servletGBeanData.getAttribute("servletMappings"));
582 defaultServletMappings.removeAll(knownServletMappings);
583 servletGBeanData.setAttribute("servletMappings", defaultServletMappings);
584 moduleContext.addGBean(servletGBeanData);
585 return servletGBeanData;
586 }
587
588 private void addFiltersGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, WebAppType webApp) throws GBeanAlreadyExistsException {
589 FilterType[] filterArray = webApp.getFilterArray();
590 for (FilterType filterType : filterArray) {
591 String filterName = filterType.getFilterName().getStringValue().trim();
592 AbstractName filterAbstractName = earContext.getNaming().createChildName(moduleName, filterName, NameFactory.WEB_FILTER);
593 GBeanData filterData = new GBeanData(filterAbstractName, JettyFilterHolder.GBEAN_INFO);
594 filterData.setAttribute("filterName", filterName);
595 filterData.setAttribute("filterClass", filterType.getFilterClass().getStringValue().trim());
596 Map<String, String> initParams = new HashMap<String, String>();
597 ParamValueType[] initParamArray = filterType.getInitParamArray();
598 for (ParamValueType paramValueType : initParamArray) {
599 initParams.put(paramValueType.getParamName().getStringValue().trim(), paramValueType.getParamValue().getStringValue().trim());
600 }
601 filterData.setAttribute("initParams", initParams);
602 filterData.setReferencePattern("JettyServletRegistration", moduleName);
603 moduleContext.addGBean(filterData);
604 }
605 }
606
607 private void addFilterMappingsGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, WebAppType webApp, AbstractName previous) throws GBeanAlreadyExistsException {
608 FilterMappingType[] filterMappingArray = webApp.getFilterMappingArray();
609 for (FilterMappingType filterMappingType : filterMappingArray) {
610 String filterName = filterMappingType.getFilterName().getStringValue().trim();
611 GBeanData filterMappingData = new GBeanData(JettyFilterMapping.GBEAN_INFO);
612 if (previous != null) {
613 filterMappingData.addDependency(previous);
614 }
615 filterMappingData.setReferencePattern("JettyServletRegistration", moduleName);
616 AbstractName filterAbstractName = earContext.getNaming().createChildName(moduleName, filterName, NameFactory.WEB_FILTER);
617
618 AbstractName filterMappingName = null;
619 if (filterMappingType.sizeOfUrlPatternArray() > 0) {
620 String[] urlPatterns = new String[filterMappingType.sizeOfUrlPatternArray()];
621 for (int j = 0; j < urlPatterns.length; j++) {
622 urlPatterns[j] = filterMappingType.getUrlPatternArray(j).getStringValue().trim();
623 }
624
625 filterMappingData.setAttribute("urlPatterns", urlPatterns);
626 filterMappingName = earContext.getNaming().createChildName(filterAbstractName, ObjectName.quote(Arrays.deepToString(urlPatterns)), NameFactory.URL_WEB_FILTER_MAPPING);
627 }
628 if (filterMappingType.sizeOfServletNameArray() > 0) {
629 Set<AbstractName> servletNameSet = new HashSet<AbstractName>();
630 for (int j = 0; j < filterMappingType.sizeOfServletNameArray(); j++) {
631 String servletName = filterMappingType.getServletNameArray(j).getStringValue().trim();
632 AbstractName abstractServletName = earContext.getNaming().createChildName(moduleName, servletName, NameFactory.SERVLET);
633 servletNameSet.add(abstractServletName);
634 filterMappingData.addDependency(abstractServletName);
635 }
636
637 filterMappingData.setReferencePatterns("Servlets", servletNameSet);
638 filterMappingName = earContext.getNaming().createChildName(filterAbstractName, ObjectName.quote(Arrays.deepToString(servletNameSet.toArray())), NameFactory.SERVLET_WEB_FILTER_MAPPING);
639
640 }
641 filterMappingData.setAbstractName(filterMappingName);
642 previous = filterMappingName;
643
644 boolean request = filterMappingType.getDispatcherArray().length == 0;
645 boolean forward = false;
646 boolean include = false;
647 boolean error = false;
648 for (int j = 0; j < filterMappingType.getDispatcherArray().length; j++) {
649 DispatcherType dispatcherType = filterMappingType.getDispatcherArray()[j];
650 if (dispatcherType.getStringValue().equals("REQUEST")) {
651 request = true;
652 } else if (dispatcherType.getStringValue().equals("FORWARD")) {
653 forward = true;
654 } else if (dispatcherType.getStringValue().equals("INCLUDE")) {
655 include = true;
656 } else if (dispatcherType.getStringValue().equals("ERROR")) {
657 error = true;
658 }
659 }
660 filterMappingData.setAttribute("requestDispatch", request);
661 filterMappingData.setAttribute("forwardDispatch", forward);
662 filterMappingData.setAttribute("includeDispatch", include);
663 filterMappingData.setAttribute("errorDispatch", error);
664 filterMappingData.setReferencePattern("Filter", filterAbstractName);
665 moduleContext.addGBean(filterMappingData);
666 }
667 }
668
669 private AbstractName addDefaultFiltersGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, AbstractName previous) throws GBeanNotFoundException, GBeanAlreadyExistsException {
670 for (Object defaultFilter : defaultFilters) {
671 GBeanData filterGBeanData = getGBeanData(kernel, defaultFilter);
672 String filterName = (String) filterGBeanData.getAttribute("filterName");
673 AbstractName defaultFilterAbstractName = earContext.getNaming().createChildName(moduleName, filterName, NameFactory.WEB_FILTER);
674 filterGBeanData.setAbstractName(defaultFilterAbstractName);
675 filterGBeanData.setReferencePattern("JettyServletRegistration", moduleName);
676 moduleContext.addGBean(filterGBeanData);
677 //add a mapping to /*
678
679 GBeanData filterMappingGBeanData = new GBeanData(JettyFilterMapping.GBEAN_INFO);
680 if (previous != null) {
681 filterMappingGBeanData.addDependency(previous);
682 }
683 filterMappingGBeanData.setReferencePattern("JettyServletRegistration", moduleName);
684 String urlPattern = "/*";
685 filterMappingGBeanData.setAttribute("urlPattern", urlPattern);
686 AbstractName filterMappingName = earContext.getNaming().createChildName(defaultFilterAbstractName, urlPattern, NameFactory.URL_WEB_FILTER_MAPPING);
687 filterMappingGBeanData.setAbstractName(filterMappingName);
688 previous = filterMappingName;
689
690
691 filterMappingGBeanData.setAttribute("requestDispatch", TRUE);
692 filterMappingGBeanData.setAttribute("forwardDispatch", TRUE);
693 filterMappingGBeanData.setAttribute("includeDispatch", TRUE);
694 filterMappingGBeanData.setAttribute("errorDispatch", FALSE);
695 filterMappingGBeanData.setReferencePattern("Filter", defaultFilterAbstractName);
696 moduleContext.addGBean(filterMappingGBeanData);
697 }
698 return previous;
699 }
700
701 private Map buildServletMappings(Module module, WebAppType webApp, Map<String, Set<String>> servletMappings, Set<String> knownServletMappings) throws DeploymentException {
702 ServletType[] servletTypes = webApp.getServletArray();
703 Set<String> knownServlets = new HashSet<String>();
704 for (ServletType type : servletTypes) {
705 knownServlets.add(type.getServletName().getStringValue().trim());
706 }
707
708 ServletMappingType[] servletMappingArray = webApp.getServletMappingArray();
709 for (ServletMappingType servletMappingType : servletMappingArray) {
710 String servletName = servletMappingType.getServletName().getStringValue().trim();
711 if (!knownServlets.contains(servletName)) {
712 throw new DeploymentException("Web app " + module.getName() +
713 " contains a servlet mapping that refers to servlet '" + servletName +
714 "' but no such servlet was found!");
715 }
716 UrlPatternType[] urlPatterns = servletMappingType.getUrlPatternArray();
717 addMappingsForServlet(servletName, urlPatterns, knownServletMappings, servletMappings);
718 }
719
720 return servletMappings;
721 }
722
723 private void addMappingsForServlet(String servletName, UrlPatternType[] urlPatterns, Set<String> knownServletMappings, Map<String, Set<String>> servletMappings) throws DeploymentException {
724 for (UrlPatternType patternType : urlPatterns) {
725 String urlPattern = patternType.getStringValue().trim();
726 if (!urlPattern.startsWith("*") && !urlPattern.startsWith("/")) {
727 urlPattern = "/" + urlPattern;
728 }
729 if (!knownServletMappings.contains(urlPattern)) {
730 knownServletMappings.add(urlPattern);
731 checkString(urlPattern);
732 Set<String> urlsForServlet = servletMappings.get(servletName);
733 if (urlsForServlet == null) {
734 urlsForServlet = new HashSet<String>();
735 servletMappings.put(servletName, urlsForServlet);
736 }
737 urlsForServlet.add(urlPattern);
738 }
739 }
740 }
741
742 private void configureLoginConfigs(Module module, WebAppType webApp, JettyWebAppType jettyWebApp, GBeanData webModuleData) throws DeploymentException {
743 LoginConfigType[] loginConfigArray = webApp.getLoginConfigArray();
744 if (loginConfigArray.length > 1) {
745 throw new DeploymentException("Web app " + module.getName() + " cannot have more than one login-config element. Currently has " + loginConfigArray.length + " login-config elements.");
746 }
747 if (loginConfigArray.length == 1) {
748 LoginConfigType loginConfig = loginConfigArray[0];
749 if (loginConfig.isSetAuthMethod()) {
750 String authMethod = loginConfig.getAuthMethod().getStringValue();
751 if ("BASIC".equals(authMethod)) {
752 webModuleData.setAttribute("authenticator", new BasicAuthenticator());
753 } else if ("DIGEST".equals(authMethod)) {
754 webModuleData.setAttribute("authenticator", new DigestAuthenticator());
755 } else if ("FORM".equals(authMethod)) {
756
757 FormAuthenticator formAuthenticator = new FormAuthenticator();
758 webModuleData.setAttribute("authenticator", formAuthenticator);
759 if (loginConfig.isSetFormLoginConfig()) {
760 FormLoginConfigType formLoginConfig = loginConfig.getFormLoginConfig();
761 formAuthenticator.setLoginPage(formLoginConfig.getFormLoginPage().getStringValue());
762 formAuthenticator.setErrorPage(formLoginConfig.getFormErrorPage().getStringValue());
763 }
764 } else if ("CLIENT-CERT".equals(authMethod)) {
765 webModuleData.setAttribute("authenticator", new ClientCertAuthenticator());
766 }
767 }
768 if (loginConfig.isSetRealmName()) {
769 webModuleData.setAttribute("realmName", loginConfig.getRealmName().getStringValue());
770 }
771
772 } else if (jettyWebApp.isSetSecurityRealmName()) {
773 webModuleData.setAttribute("authenticator", new NonAuthenticator());
774 }
775 }
776
777 private void configureTagLibs(Module module, WebAppType webApp, GBeanData webModuleData, Map<String, Set<String>> servletMappings, Set<String> knownServletMappings, String jspServletName) throws DeploymentException {
778 JspConfigType[] jspConfigArray = webApp.getJspConfigArray();
779 if (jspConfigArray.length > 1) {
780 throw new DeploymentException("Web app " + module.getName() + " cannot have more than one jsp-config element. Currently has " + jspConfigArray.length + " jsp-config elements.");
781 }
782 Map<String, String> tagLibMap = new HashMap<String, String>();
783 for (JspConfigType aJspConfigArray : jspConfigArray) {
784 TaglibType[] tagLibArray = aJspConfigArray.getTaglibArray();
785 for (TaglibType taglib : tagLibArray) {
786 tagLibMap.put(taglib.getTaglibUri().getStringValue().trim(), taglib.getTaglibLocation().getStringValue().trim());
787 }
788 for (JspPropertyGroupType propertyGroup: aJspConfigArray.getJspPropertyGroupArray()) {
789 UrlPatternType[] urlPatterns = propertyGroup.getUrlPatternArray();
790 addMappingsForServlet(jspServletName, urlPatterns, knownServletMappings, servletMappings);
791 }
792 }
793 webModuleData.setAttribute("tagLibMap", tagLibMap);
794 }
795
796 private void configureErrorPages(WebAppType webApp, GBeanData webModuleData) {
797 ErrorPageType[] errorPageArray = webApp.getErrorPageArray();
798 Map<String, String> errorPageMap = new HashMap<String, String>();
799 for (ErrorPageType errorPageType : errorPageArray) {
800 if (errorPageType.isSetErrorCode()) {
801 errorPageMap.put(errorPageType.getErrorCode().getStringValue().trim(), errorPageType.getLocation().getStringValue().trim());
802 } else {
803 errorPageMap.put(errorPageType.getExceptionType().getStringValue().trim(), errorPageType.getLocation().getStringValue().trim());
804 }
805 }
806 webModuleData.setAttribute("errorPages", errorPageMap);
807 }
808
809 private void configureLocaleEncodingMappingLists(WebAppType webApp, GBeanData webModuleData) {
810 LocaleEncodingMappingListType[] localeEncodingMappingListArray = webApp.getLocaleEncodingMappingListArray();
811 Map<String, String> localeEncodingMappingMap = new HashMap<String, String>(defaultLocaleEncodingMappings);
812 for (LocaleEncodingMappingListType aLocaleEncodingMappingListArray : localeEncodingMappingListArray) {
813 LocaleEncodingMappingType[] localeEncodingMappingArray = aLocaleEncodingMappingListArray.getLocaleEncodingMappingArray();
814 for (LocaleEncodingMappingType localeEncodingMapping : localeEncodingMappingArray) {
815 localeEncodingMappingMap.put(localeEncodingMapping.getLocale().trim(), localeEncodingMapping.getEncoding().trim());
816 }
817 }
818 webModuleData.setAttribute("localeEncodingMapping", localeEncodingMappingMap);
819 }
820
821 private void configureWelcomeFileLists(WebAppType webApp, GBeanData webModuleData) {
822 WelcomeFileListType[] welcomeFileArray = webApp.getWelcomeFileListArray();
823 List<String> welcomeFiles;
824 if (welcomeFileArray.length > 0) {
825 welcomeFiles = new ArrayList<String>();
826 for (WelcomeFileListType aWelcomeFileArray : welcomeFileArray) {
827 String[] welcomeFileListType = aWelcomeFileArray.getWelcomeFileArray();
828 for (String welcomeFile : welcomeFileListType) {
829 welcomeFiles.add(welcomeFile.trim());
830 }
831 }
832 } else {
833 welcomeFiles = new ArrayList<String>(defaultWelcomeFiles);
834 }
835 webModuleData.setAttribute("welcomeFiles", welcomeFiles.toArray(new String[welcomeFiles.size()]));
836 }
837
838 private void configureMimeMappings(WebAppType webApp, GBeanData webModuleData) {
839 MimeMappingType[] mimeMappingArray = webApp.getMimeMappingArray();
840 Map<String, String> mimeMappingMap = new HashMap<String, String>(defaultMimeTypeMappings);
841 for (MimeMappingType mimeMappingType : mimeMappingArray) {
842 mimeMappingMap.put(mimeMappingType.getExtension().getStringValue().trim(), mimeMappingType.getMimeType().getStringValue().trim());
843 }
844 webModuleData.setAttribute("mimeMap", mimeMappingMap);
845 }
846
847 private void configureListeners(WebAppType webApp, GBeanData webModuleData) {
848 ListenerType[] listenerArray = webApp.getListenerArray();
849 Collection<String> listeners = new ArrayList<String>();
850 for (ListenerType listenerType : listenerArray) {
851 listeners.add(listenerType.getListenerClass().getStringValue().trim());
852 }
853 webModuleData.setAttribute("listenerClassNames", listeners);
854 }
855
856 private void configureContextParams(WebAppType webApp, GBeanData webModuleData) {
857 ParamValueType[] contextParamArray = webApp.getContextParamArray();
858 Map<String, String> contextParams = new HashMap<String, String>();
859 for (ParamValueType contextParam : contextParamArray) {
860 contextParams.put(contextParam.getParamName().getStringValue().trim(), contextParam.getParamValue().getStringValue().trim());
861 }
862 webModuleData.setAttribute("contextParamMap", contextParams);
863 }
864
865 private void configureHosts(EARContext earContext, JettyWebAppType jettyWebApp, GBeanData webModuleData) throws GBeanAlreadyExistsException {
866 String[] hosts = jettyWebApp.getHostArray();
867 for (int i = 0; i < hosts.length; i++) {
868 hosts[i] = hosts[i].trim();
869 }
870 String[] virtualHosts = jettyWebApp.getVirtualHostArray();
871 for (int i = 0; i < virtualHosts.length; i++) {
872 virtualHosts[i] = virtualHosts[i].trim();
873 }
874 if (hosts.length > 0 || virtualHosts.length > 0) {
875 //use name same as module
876 AbstractName hostName = earContext.getNaming().createChildName(webModuleData.getAbstractName(), "Host", "Host");
877 GBeanData hostData = new GBeanData(hostName, Host.GBEAN_INFO);
878 hostData.setAttribute("hosts", hosts);
879 hostData.setAttribute("virtualHosts", virtualHosts);
880 earContext.addGBean(hostData);
881 webModuleData.setReferencePattern("Host", hostName);
882 }
883 }
884
885 public String getSchemaNamespace() {
886 return JETTY_NAMESPACE;
887 }
888
889 /**
890 * Adds the provided servlets, taking into account the load-on-startup ordering.
891 *
892 * @param webModuleName an <code>ObjectName</code> value
893 * @param module a <code>Module</code> value
894 * @param servletTypes a <code>ServletType[]</code> value, contains the <code>servlet</code> entries from <code>web.xml</code>.
895 * @param servletMappings a <code>Map</code> value
896 * @param securityRoles a <code>Set</code> value
897 * @param rolePermissions a <code>Map</code> value
898 * @param moduleContext an <code>EARContext</code> value
899 * @throws DeploymentException if an error occurs
900 */
901 private void addServlets(AbstractName webModuleName,
902 Module module,
903 ServletType[] servletTypes,
904 Map<String, Set<String>> servletMappings,
905 Set<String> securityRoles,
906 Map<String, PermissionCollection> rolePermissions,
907 EARContext moduleContext) throws DeploymentException {
908
909 // this TreeSet will order the ServletTypes based on whether
910 // they have a load-on-startup element and what its value is
911 TreeSet<ServletType> loadOrder = new TreeSet<ServletType>(new StartupOrderComparator());
912
913 // add all of the servlets to the sorted set
914 for (ServletType servletType1 : servletTypes) {
915 loadOrder.add(servletType1);
916 }
917
918 // now that they're sorted, read them in order and add them to
919 // the context. we'll use a GBean reference to enforce the
920 // load order. Each servlet GBean (except the first) has a
921 // reference to the previous GBean. The kernel will ensure
922 // that each "previous" GBean is running before it starts any
923 // other GBeans that reference it. See also
924 // o.a.g.jetty6.JettyFilterMapping which provided the example
925 // of how to do this.
926 // http://issues.apache.org/jira/browse/GERONIMO-645
927 AbstractName previousServlet = null;
928 for (Object aLoadOrder : loadOrder) {
929 ServletType servletType = (ServletType) aLoadOrder;
930 previousServlet = addServlet(webModuleName, module, previousServlet, servletType, servletMappings, securityRoles, rolePermissions, moduleContext);
931 }
932
933 // JACC v1.0 secion B.19
934 addUnmappedJSPPermissions(securityRoles, rolePermissions);
935 }
936
937 /**
938 * @param webModuleName AbstractName of the web module
939 * @param module the web module being added
940 * @param previousServlet the servlet to start before this one in init order
941 * @param servletType XMLObject specifying the servlet configuration
942 * @param servletMappings Map of servlet name to set of ServletMapping strings for this web app
943 * @param securityRoles security roles in the web app
944 * @param rolePermissions RolePermissions for the roles this servlet needs to access
945 * @param moduleContext deployment context for this module
946 * @return AbstractName of servlet gbean added
947 * @throws DeploymentException if something goes wrong
948 */
949 private AbstractName addServlet(AbstractName webModuleName,
950 Module module,
951 AbstractName previousServlet,
952 ServletType servletType,
953 Map<String, Set<String>> servletMappings,
954 Set<String> securityRoles,
955 Map<String, PermissionCollection> rolePermissions,
956 EARContext moduleContext) throws DeploymentException {
957 String servletName = servletType.getServletName().getStringValue().trim();
958 AbstractName servletAbstractName = moduleContext.getNaming().createChildName(webModuleName, servletName, NameFactory.SERVLET);
959 GBeanData servletData;
960 Map<String, String> initParams = new HashMap<String, String>();
961 if (servletType.isSetServletClass()) {
962 ClassLoader webClassLoader = moduleContext.getClassLoader();
963 String servletClassName = servletType.getServletClass().getStringValue().trim();
964 Class servletClass;
965 try {
966 servletClass = webClassLoader.loadClass(servletClassName);
967 } catch (ClassNotFoundException e) {
968 throw new DeploymentException("Could not load servlet class " + servletClassName, e); // TODO identify web app in message
969 }
970 Class baseServletClass;
971 try {
972 baseServletClass = webClassLoader.loadClass(Servlet.class.getName());
973 } catch (ClassNotFoundException e) {
974 throw new DeploymentException("Could not load javax.servlet.Servlet in web classloader", e); // TODO identify web app in message
975 }
976 if (baseServletClass.isAssignableFrom(servletClass)) {
977 servletData = new GBeanData(servletAbstractName, JettyServletHolder.GBEAN_INFO);
978 servletData.setAttribute("servletClass", servletClassName);
979 } else {
980 servletData = new GBeanData(pojoWebServiceTemplate);
981 servletData.setAbstractName(servletAbstractName);
982 //let the web service builder deal with configuring the gbean with the web service stack
983 // Object portInfo = portMap.get(servletName);
984 // if (portInfo == null) {
985 // throw new DeploymentException("No web service deployment info for servlet name " + servletName); // TODO identify web app in message
986 // }
987 boolean configured = false;
988 for (Object aWebServiceBuilder : webServiceBuilder) {
989 WebServiceBuilder serviceBuilder = (WebServiceBuilder) aWebServiceBuilder;
990 if (serviceBuilder.configurePOJO(servletData, servletName, module, servletClassName, moduleContext)) {
991 configured = true;
992 break;
993 }
994 }
995 if (!configured) {
996 throw new DeploymentException("POJO web service: " + servletName + " not configured by any web service builder");
997 }
998 }
999 } else if (servletType.isSetJspFile()) {
1000 servletData = new GBeanData(servletAbstractName, JettyServletHolder.GBEAN_INFO);
1001 servletData.setAttribute("jspFile", servletType.getJspFile().getStringValue().trim());
1002 servletData.setAttribute("servletClass", jspServlet.getServletClassName());
1003 initParams.put("development", "false");
1004 } else {
1005 throw new DeploymentException("Neither servlet class nor jsp file is set for " + servletName); // TODO identify web app in message
1006 }
1007
1008 // link to previous servlet, if there is one, so that we
1009 // preserve the <load-on-startup> ordering.
1010 // http://issues.apache.org/jira/browse/GERONIMO-645
1011 if (null != previousServlet) {
1012 servletData.addDependency(previousServlet);
1013 }
1014
1015 //TODO in init param setter, add classpath if jspFile is not null.
1016 servletData.setReferencePattern("JettyServletRegistration", webModuleName);
1017 servletData.setAttribute("servletName", servletName);
1018 ParamValueType[] initParamArray = servletType.getInitParamArray();
1019 for (ParamValueType paramValueType : initParamArray) {
1020 initParams.put(paramValueType.getParamName().getStringValue().trim(), paramValueType.getParamValue().getStringValue().trim());
1021 }
1022 servletData.setAttribute("initParams", initParams);
1023 if (servletType.isSetLoadOnStartup()) {
1024 Integer loadOnStartup = new Integer(servletType.xgetLoadOnStartup().getStringValue());
1025 servletData.setAttribute("loadOnStartup", loadOnStartup);
1026 }
1027
1028 Set mappings = servletMappings.get(servletName);
1029 servletData.setAttribute("servletMappings", mappings == null ? Collections.EMPTY_SET : mappings);
1030
1031 //run-as
1032 if (servletType.isSetRunAs()) {
1033 String runAsRole = servletType.getRunAs().getRoleName().getStringValue().trim();
1034 servletData.setAttribute("runAsRole", runAsRole);
1035 }
1036
1037 processRoleRefPermissions(servletType, securityRoles, rolePermissions);
1038
1039 try {
1040 moduleContext.addGBean(servletData);
1041 } catch (GBeanAlreadyExistsException e) {
1042 throw new DeploymentException("Could not add servlet gbean to context", e); // TODO identify web app in message
1043 }
1044 return servletAbstractName;
1045 }
1046
1047 public static final GBeanInfo GBEAN_INFO;
1048
1049 static {
1050 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(JettyModuleBuilder.class, NameFactory.MODULE_BUILDER);
1051 infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
1052 infoBuilder.addAttribute("defaultSessionTimeoutSeconds", Integer.class, true, true);
1053 infoBuilder.addAttribute("defaultWelcomeFiles", List.class, true, true);
1054 infoBuilder.addAttribute("defaultLocaleEncodingMappings", Map.class, true, true);
1055 infoBuilder.addAttribute("defaultMimeTypeMappings", Map.class, true, true);
1056 infoBuilder.addAttribute("jettyContainerObjectName", AbstractNameQuery.class, true, true);
1057 infoBuilder.addReference("JspServlet", JettyJspServletHolder.class, NameFactory.SERVLET_TEMPLATE);
1058 infoBuilder.addReference("DefaultServlets", JettyDefaultServletHolder.class, NameFactory.SERVLET_TEMPLATE);
1059 infoBuilder.addReference("DefaultFilters", Object.class);
1060 infoBuilder.addReference("DefaultFilterMappings", Object.class);
1061 infoBuilder.addReference("PojoWebServiceTemplate", Object.class, NameFactory.SERVLET_WEB_SERVICE_TEMPLATE);
1062 infoBuilder.addReference("WebServiceBuilder", WebServiceBuilder.class, NameFactory.MODULE_BUILDER);
1063 infoBuilder.addReference("ClusteringBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
1064 infoBuilder.addReference("SecurityBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
1065 infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
1066 infoBuilder.addReference("NamingBuilders", NamingBuilder.class, NameFactory.MODULE_BUILDER);
1067 infoBuilder.addReference("ModuleBuilderExtensions", ModuleBuilderExtension.class, NameFactory.MODULE_BUILDER);
1068 infoBuilder.addReference("ResourceEnvironmentSetter", ResourceEnvironmentSetter.class, NameFactory.MODULE_BUILDER);
1069 infoBuilder.addAttribute("kernel", Kernel.class, false);
1070 infoBuilder.addInterface(ModuleBuilder.class);
1071
1072 infoBuilder.setConstructor(new String[]{
1073 "defaultEnvironment",
1074 "defaultSessionTimeoutSeconds",
1075 "defaultWelcomeFiles",
1076 "jettyContainerObjectName",
1077 "JspServlet",
1078 "DefaultServlets",
1079 "DefaultFilters",
1080 "DefaultFilterMappings",
1081 "defaultLocaleEncodingMappings",
1082 "defaultMimeTypeMappings",
1083 "PojoWebServiceTemplate",
1084 "WebServiceBuilder",
1085 "ClusteringBuilders",
1086 "SecurityBuilders",
1087 "ServiceBuilders",
1088 "NamingBuilders",
1089 "ModuleBuilderExtensions",
1090 "ResourceEnvironmentSetter",
1091 "kernel"});
1092 GBEAN_INFO = infoBuilder.getBeanInfo();
1093 }
1094
1095 public static GBeanInfo getGBeanInfo() {
1096 return GBEAN_INFO;
1097 }
1098
1099 static class StartupOrderComparator implements Comparator<ServletType> {
1100 /**
1101 * comparator that compares first on the basis of startup order, and then on the lexicographical
1102 * ordering of servlet name. Since the servlet names have a uniqueness constraint, this should
1103 * provide a total ordering consistent with equals. All servlets with no startup order are after
1104 * all servlets with a startup order.
1105 *
1106 * @param s1 first ServletType object
1107 * @param s2 second ServletType object
1108 * @return an int < 0 if o1 precedes o2, 0 if they are equal, and > 0 if o2 preceeds o1.
1109 */
1110 public int compare(ServletType s1, ServletType s2) {
1111
1112 // load-on-startup is set for neither. the
1113 // ordering at this point doesn't matter, but we
1114 // should return "0" only if the two objects say
1115 // they are equal
1116 if (!s1.isSetLoadOnStartup() && !s2.isSetLoadOnStartup()) {
1117 return s1.equals(s2) ? 0 : s1.getServletName().getStringValue().trim().compareTo(s2.getServletName().getStringValue().trim());
1118 }
1119
1120 // load-on-startup is set for one but not the
1121 // other. whichever one is set will be "less
1122 // than", i.e. it will be loaded first
1123 if (s1.isSetLoadOnStartup() && !s2.isSetLoadOnStartup()) {
1124 return -1;
1125 }
1126 if (!s1.isSetLoadOnStartup() && s2.isSetLoadOnStartup()) {
1127 return 1;
1128 }
1129
1130 // load-on-startup is set for both. whichever one
1131 // has a smaller value is "less than"
1132 int comp = new Integer(s1.xgetLoadOnStartup().getStringValue()).compareTo(new Integer(s2.xgetLoadOnStartup().getStringValue()));
1133 if (comp == 0) {
1134 return s1.getServletName().getStringValue().trim().compareTo(s2.getServletName().getStringValue().trim());
1135 }
1136 return comp;
1137 }
1138 }
1139 }