1 /**
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.apache.geronimo.tomcat.deployment;
20
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.net.URL;
25 import java.security.Permission;
26 import java.security.PermissionCollection;
27 import java.security.Permissions;
28 import java.util.Collection;
29 import java.util.Enumeration;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.jar.JarFile;
35
36 import javax.servlet.Servlet;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.geronimo.common.DeploymentException;
41 import org.apache.geronimo.deployment.ModuleIDBuilder;
42 import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
43 import org.apache.geronimo.deployment.service.EnvironmentBuilder;
44 import org.apache.geronimo.deployment.util.DeploymentUtil;
45 import org.apache.geronimo.deployment.xbeans.EnvironmentType;
46 import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
47 import org.apache.geronimo.gbean.AbstractName;
48 import org.apache.geronimo.gbean.AbstractNameQuery;
49 import org.apache.geronimo.gbean.GBeanData;
50 import org.apache.geronimo.gbean.GBeanInfo;
51 import org.apache.geronimo.gbean.GBeanInfoBuilder;
52 import org.apache.geronimo.gbean.SingleElementCollection;
53 import org.apache.geronimo.gbean.ReferencePatterns;
54 import org.apache.geronimo.j2ee.deployment.EARContext;
55 import org.apache.geronimo.j2ee.deployment.Module;
56 import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
57 import org.apache.geronimo.j2ee.deployment.NamingBuilder;
58 import org.apache.geronimo.j2ee.deployment.WebModule;
59 import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
60 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
61 import org.apache.geronimo.kernel.Kernel;
62 import org.apache.geronimo.kernel.Naming;
63 import org.apache.geronimo.kernel.config.Configuration;
64 import org.apache.geronimo.kernel.config.ConfigurationData;
65 import org.apache.geronimo.kernel.repository.Environment;
66 import org.apache.geronimo.naming.deployment.ENCConfigBuilder;
67 import org.apache.geronimo.naming.deployment.GBeanResourceEnvironmentBuilder;
68 import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter;
69 import org.apache.geronimo.security.deploy.DefaultPrincipal;
70 import org.apache.geronimo.security.deployment.SecurityConfiguration;
71 import org.apache.geronimo.security.jacc.ComponentPermissions;
72 import org.apache.geronimo.tomcat.ManagerGBean;
73 import org.apache.geronimo.tomcat.RealmGBean;
74 import org.apache.geronimo.tomcat.TomcatWebAppContext;
75 import org.apache.geronimo.tomcat.ValveGBean;
76 import org.apache.geronimo.tomcat.cluster.CatalinaClusterGBean;
77 import org.apache.geronimo.tomcat.util.SecurityHolder;
78 import org.apache.geronimo.web.deployment.AbstractWebModuleBuilder;
79 import org.apache.geronimo.web.deployment.GenericToSpecificPlanConverter;
80 import org.apache.geronimo.xbeans.geronimo.web.tomcat.TomcatWebAppDocument;
81 import org.apache.geronimo.xbeans.geronimo.web.tomcat.TomcatWebAppType;
82 import org.apache.geronimo.xbeans.geronimo.web.tomcat.config.GerTomcatDocument;
83 import org.apache.geronimo.xbeans.j2ee.ServletType;
84 import org.apache.geronimo.xbeans.j2ee.WebAppDocument;
85 import org.apache.geronimo.xbeans.j2ee.WebAppType;
86 import org.apache.xmlbeans.XmlException;
87 import org.apache.xmlbeans.XmlObject;
88
89
90 /**
91 * @version $Rev:385659 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
92 */
93 public class TomcatModuleBuilder extends AbstractWebModuleBuilder {
94
95 private static final Log log = LogFactory.getLog(TomcatModuleBuilder.class);
96
97 private final Environment defaultEnvironment;
98 private final AbstractNameQuery tomcatContainerName;
99
100 private final SingleElementCollection webServiceBuilder;
101
102 private static final String TOMCAT_NAMESPACE = TomcatWebAppDocument.type.getDocumentElementName().getNamespaceURI();
103
104 public TomcatModuleBuilder(Environment defaultEnvironment,
105 AbstractNameQuery tomcatContainerName,
106 Collection webServiceBuilder,
107 Collection securityBuilders,
108 Collection serviceBuilders,
109 NamingBuilder namingBuilders,
110 ResourceEnvironmentSetter resourceEnvironmentSetter,
111 Kernel kernel) {
112 super(kernel, securityBuilders, serviceBuilders, namingBuilders, resourceEnvironmentSetter);
113 this.defaultEnvironment = defaultEnvironment;
114
115 this.tomcatContainerName = tomcatContainerName;
116 this.webServiceBuilder = new SingleElementCollection(webServiceBuilder);
117 }
118
119 private WebServiceBuilder getWebServiceBuilder() {
120 return (WebServiceBuilder) webServiceBuilder.getElement();
121 }
122
123 protected Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, boolean standAlone, String contextRoot, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
124 assert moduleFile != null: "moduleFile is null";
125 assert targetPath != null: "targetPath is null";
126 assert !targetPath.endsWith("/"): "targetPath must not end with a '/'";
127
128
129 String specDD;
130 WebAppType webApp;
131 try {
132 if (specDDUrl == null) {
133 specDDUrl = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/web.xml");
134 }
135
136
137
138 specDD = DeploymentUtil.readAll(specDDUrl);
139 } catch (Exception e) {
140
141 return null;
142 }
143
144 try {
145
146 XmlObject parsed = XmlBeansUtil.parse(specDD);
147 WebAppDocument webAppDoc = convertToServletSchema(parsed);
148 webApp = webAppDoc.getWebApp();
149 } catch (XmlException xmle) {
150
151
152
153 throw new DeploymentException("Error parsing web.xml for " + targetPath, xmle);
154 }
155 check(webApp);
156
157
158 TomcatWebAppType tomcatWebApp = getTomcatWebApp(plan, moduleFile, standAlone, targetPath, webApp);
159
160 if (contextRoot == null || contextRoot.trim().equals("")) {
161 if (tomcatWebApp.isSetContextRoot()) {
162 contextRoot = tomcatWebApp.getContextRoot();
163 } else {
164 contextRoot = determineDefaultContextRoot(webApp, standAlone, moduleFile, targetPath);
165 }
166 }
167
168 contextRoot = contextRoot.trim();
169
170 EnvironmentType environmentType = tomcatWebApp.getEnvironment();
171 Environment environment = EnvironmentBuilder.buildEnvironment(environmentType, defaultEnvironment);
172
173 getNamingBuilders().buildEnvironment(webApp, tomcatWebApp, environment);
174
175
176 String warName = new File(moduleFile.getName()).getName();
177 if (warName.lastIndexOf('.') > -1) {
178 warName = warName.substring(0, warName.lastIndexOf('.'));
179 }
180 idBuilder.resolve(environment, warName, "war");
181
182 Map servletNameToPathMap = buildServletNameToPathMap(webApp, contextRoot);
183
184 Map portMap = getWebServiceBuilder().findWebServices(moduleFile, false, servletNameToPathMap, environment);
185 AbstractName moduleName;
186 if (earName == null) {
187 earName = naming.createRootName(environment.getConfigId(), NameFactory.NULL, NameFactory.J2EE_APPLICATION);
188 moduleName = naming.createChildName(earName, environment.getConfigId().toString(), NameFactory.WEB_MODULE);
189 } else {
190 moduleName = naming.createChildName(earName, targetPath, NameFactory.WEB_MODULE);
191 }
192
193 return new WebModule(standAlone, moduleName, environment, moduleFile, targetPath, webApp, tomcatWebApp, specDD, contextRoot, portMap, TOMCAT_NAMESPACE);
194 }
195
196
197 TomcatWebAppType getTomcatWebApp(Object plan, JarFile moduleFile, boolean standAlone, String targetPath, WebAppType webApp) throws DeploymentException {
198 XmlObject rawPlan = null;
199 try {
200
201 try {
202 if (plan instanceof XmlObject) {
203 rawPlan = (XmlObject) plan;
204 } else {
205 if (plan != null) {
206 rawPlan = XmlBeansUtil.parse(((File) plan).toURL(), getClass().getClassLoader());
207 } else {
208 URL path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-web.xml");
209 try {
210 rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
211 } catch (FileNotFoundException e) {
212 path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-tomcat.xml");
213 try {
214 rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
215 } catch (FileNotFoundException e1) {
216 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.");
217 }
218 }
219 }
220 }
221 } catch (IOException e) {
222 log.warn(e);
223 }
224
225 TomcatWebAppType tomcatWebApp;
226 if (rawPlan != null) {
227 XmlObject webPlan = new GenericToSpecificPlanConverter(GerTomcatDocument.type.getDocumentElementName().getNamespaceURI(),
228 TomcatWebAppDocument.type.getDocumentElementName().getNamespaceURI(), "tomcat").convertToSpecificPlan(rawPlan);
229 tomcatWebApp = (TomcatWebAppType) webPlan.changeType(TomcatWebAppType.type);
230 XmlBeansUtil.validateDD(tomcatWebApp);
231 } else {
232 String defaultContextRoot = determineDefaultContextRoot(webApp, standAlone, moduleFile, targetPath);
233 tomcatWebApp = createDefaultPlan(defaultContextRoot);
234 }
235 return tomcatWebApp;
236 } catch (XmlException e) {
237 throw new DeploymentException("xml problem for web app " + targetPath, e);
238 }
239 }
240
241 private TomcatWebAppType createDefaultPlan(String path) {
242 TomcatWebAppType tomcatWebApp = TomcatWebAppType.Factory.newInstance();
243 tomcatWebApp.setContextRoot("/" + path);
244 return tomcatWebApp;
245 }
246
247
248 public void initContext(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
249 WebAppType webApp = (WebAppType) module.getSpecDD();
250
251 TomcatWebAppType gerWebApp = (TomcatWebAppType) module.getVendorDD();
252
253
254
255 getNamingBuilders().initContext(webApp, gerWebApp, module.getEarContext().getConfiguration(), earContext.getConfiguration(), module);
256 if ((webApp.getSecurityConstraintArray().length > 0 || webApp.getSecurityRoleArray().length > 0) &&
257 !gerWebApp.isSetSecurityRealmName()) {
258 throw new DeploymentException("web.xml for web app " + module.getName() + " includes security elements but Geronimo deployment plan is not provided or does not contain <security-realm-name> element necessary to configure security accordingly.");
259 }
260 boolean hasSecurityRealmName = gerWebApp.isSetSecurityRealmName();
261 buildSubstitutionGroups(gerWebApp, hasSecurityRealmName, module, earContext);
262 }
263
264 public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repository) throws DeploymentException {
265 EARContext moduleContext = module.getEarContext();
266 ClassLoader webClassLoader = moduleContext.getClassLoader();
267 AbstractName moduleName = moduleContext.getModuleName();
268 WebModule webModule = (WebModule) module;
269
270 WebAppType webApp = (WebAppType) webModule.getSpecDD();
271 TomcatWebAppType tomcatWebApp = (TomcatWebAppType) webModule.getVendorDD();
272
273 GBeanData webModuleData = new GBeanData(moduleName, TomcatWebAppContext.GBEAN_INFO);
274 try {
275 webModuleData.setReferencePattern("J2EEServer", moduleContext.getServerName());
276 if (!module.isStandAlone()) {
277 webModuleData.setReferencePattern("J2EEApplication", earContext.getModuleName());
278 }
279
280 webModuleData.setAttribute("deploymentDescriptor", module.getOriginalSpecDD());
281 Set securityRoles = collectRoleNames(webApp);
282 Map rolePermissions = new HashMap();
283
284 webModuleData.setAttribute("contextPath", webModule.getContextRoot());
285
286
287
288
289 Set dependencies = findGBeanDependencies(earContext);
290 webModuleData.addDependencies(dependencies);
291
292
293 Map buildingContext = new HashMap();
294 buildingContext.put(NamingBuilder.JNDI_KEY, new HashMap());
295 buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, moduleName);
296 Configuration earConfiguration = earContext.getConfiguration();
297 getNamingBuilders().buildNaming(webApp, tomcatWebApp, earConfiguration, earConfiguration, webModule, buildingContext);
298 Map compContext = (Map) buildingContext.get(NamingBuilder.JNDI_KEY);
299
300 webModuleData.setAttribute("componentContext", compContext);
301
302 GBeanResourceEnvironmentBuilder rebuilder = new GBeanResourceEnvironmentBuilder(webModuleData);
303
304 resourceEnvironmentSetter.setResourceEnvironment(rebuilder, webApp.getResourceRefArray(), tomcatWebApp.getResourceRefArray());
305
306 webModuleData.setReferencePattern("TransactionManager", earContext.getTransactionManagerName());
307 webModuleData.setReferencePattern("TrackedConnectionAssociator", earContext.getConnectionTrackerName());
308
309 if (tomcatWebApp.isSetWebContainer()) {
310 AbstractNameQuery webContainerName = ENCConfigBuilder.getGBeanQuery(NameFactory.GERONIMO_SERVICE, tomcatWebApp.getWebContainer());
311 webModuleData.setReferencePattern("Container", webContainerName);
312 } else {
313 webModuleData.setReferencePattern("Container", tomcatContainerName);
314 }
315
316 if (tomcatWebApp.isSetHost()) {
317 String virtualServer = tomcatWebApp.getHost().trim();
318 webModuleData.setAttribute("virtualServer", virtualServer);
319 }
320 if (tomcatWebApp.isSetCrossContext()) {
321 webModuleData.setAttribute("crossContext", Boolean.TRUE);
322 }
323 if (tomcatWebApp.isSetDisableCookies()) {
324 webModuleData.setAttribute("disableCookies", Boolean.TRUE);
325 }
326 if (tomcatWebApp.isSetTomcatRealm()) {
327 String tomcatRealm = tomcatWebApp.getTomcatRealm().trim();
328 AbstractName realmName = earContext.getNaming().createChildName(moduleName, tomcatRealm, RealmGBean.GBEAN_INFO.getJ2eeType());
329 webModuleData.setReferencePattern("TomcatRealm", realmName);
330 }
331 if (tomcatWebApp.isSetValveChain()) {
332 String valveChain = tomcatWebApp.getValveChain().trim();
333 AbstractName valveName = earContext.getNaming().createChildName(moduleName, valveChain, ValveGBean.J2EE_TYPE);
334 webModuleData.setReferencePattern("TomcatValveChain", valveName);
335 }
336
337 if (tomcatWebApp.isSetCluster()) {
338 String cluster = tomcatWebApp.getCluster().trim();
339 AbstractName clusterName = earContext.getNaming().createChildName(moduleName, cluster, CatalinaClusterGBean.J2EE_TYPE);
340 webModuleData.setReferencePattern("Cluster", clusterName);
341 }
342
343 if (tomcatWebApp.isSetManager()) {
344 String manager = tomcatWebApp.getManager().trim();
345 AbstractName managerName = earContext.getNaming().createChildName(moduleName, manager, ManagerGBean.J2EE_TYPE);
346 webModuleData.setReferencePattern("Manager", managerName);
347 }
348 Map portMap = webModule.getPortMap();
349
350
351 ServletType[] servletTypes = webApp.getServletArray();
352 Map webServices = new HashMap();
353 Class baseServletClass;
354 try {
355 baseServletClass = webClassLoader.loadClass(Servlet.class.getName());
356 } catch (ClassNotFoundException e) {
357 throw new DeploymentException("Could not load javax.servlet.Servlet in web classloader", e);
358 }
359 for (int i = 0; i < servletTypes.length; i++) {
360 ServletType servletType = servletTypes[i];
361
362
363 processRoleRefPermissions(servletType, securityRoles, rolePermissions);
364
365 if (servletType.isSetServletClass()) {
366 String servletName = servletType.getServletName().getStringValue().trim();
367 String servletClassName = servletType.getServletClass().getStringValue().trim();
368 Class servletClass;
369 try {
370 servletClass = webClassLoader.loadClass(servletClassName);
371 } catch (ClassNotFoundException e) {
372 throw new DeploymentException("Could not load servlet class " + servletClassName, e);
373 }
374 if (!baseServletClass.isAssignableFrom(servletClass)) {
375
376 AbstractName servletAbstractName = moduleContext.getNaming().createChildName(moduleName, servletName, NameFactory.SERVLET);
377 GBeanData servletData = new GBeanData();
378 servletData.setAbstractName(servletAbstractName);
379
380
381 Object portInfo = portMap.get(servletName);
382 getWebServiceBuilder().configurePOJO(servletData, module, portInfo, servletClassName, moduleContext);
383 ReferencePatterns patterns = servletData.getReferencePatterns("WebServiceContainerFactory");
384 AbstractName wsContainerFactoryName = patterns.getAbstractName();
385 webServices.put(servletName, wsContainerFactoryName);
386
387 webModuleData.addDependency(wsContainerFactoryName);
388 }
389 }
390 }
391
392
393 addUnmappedJSPPermissions(securityRoles, rolePermissions);
394
395 webModuleData.setAttribute("webServices", webServices);
396
397 if (tomcatWebApp.isSetSecurityRealmName()) {
398 if (earContext.getSecurityConfiguration() == null) {
399 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)");
400 }
401
402 SecurityHolder securityHolder = new SecurityHolder();
403 securityHolder.setSecurityRealm(tomcatWebApp.getSecurityRealmName().trim());
404
405 /**
406 * TODO - go back to commented version when possible.
407 */
408 String policyContextID = moduleName.toString().replaceAll("[, :]", "_");
409 securityHolder.setPolicyContextID(policyContextID);
410
411 ComponentPermissions componentPermissions = buildSpecSecurityConfig(webApp, securityRoles, rolePermissions);
412 securityHolder.setExcluded(componentPermissions.getExcludedPermissions());
413 PermissionCollection checkedPermissions = new Permissions();
414 for (Iterator iterator = rolePermissions.values().iterator(); iterator.hasNext();) {
415 PermissionCollection permissionsForRole = (PermissionCollection) iterator.next();
416 for (Enumeration iterator2 = permissionsForRole.elements(); iterator2.hasMoreElements();) {
417 Permission permission = (Permission) iterator2.nextElement();
418 checkedPermissions.add(permission);
419 }
420 }
421 securityHolder.setChecked(checkedPermissions);
422 earContext.addSecurityContext(policyContextID, componentPermissions);
423 DefaultPrincipal defaultPrincipal = ((SecurityConfiguration) earContext.getSecurityConfiguration()).getDefaultPrincipal();
424 securityHolder.setDefaultPrincipal(defaultPrincipal);
425 if (defaultPrincipal != null) {
426 securityHolder.setSecurity(true);
427 }
428
429 webModuleData.setAttribute("securityHolder", securityHolder);
430 }
431
432 moduleContext.addGBean(webModuleData);
433
434 if (!module.isStandAlone()) {
435 ConfigurationData moduleConfigurationData = moduleContext.getConfigurationData();
436 earContext.addChildConfiguration(module.getTargetPath(), moduleConfigurationData);
437 }
438 } catch (DeploymentException de) {
439 throw de;
440 } catch (Exception e) {
441 throw new DeploymentException("Unable to initialize GBean for web app " + module.getName(), e);
442 }
443 }
444
445 public String getSchemaNamespace() {
446 return TOMCAT_NAMESPACE;
447 }
448
449
450 public static final GBeanInfo GBEAN_INFO;
451
452 static {
453 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(TomcatModuleBuilder.class, NameFactory.MODULE_BUILDER);
454 infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
455 infoBuilder.addAttribute("tomcatContainerName", AbstractNameQuery.class, true, true);
456 infoBuilder.addReference("WebServiceBuilder", WebServiceBuilder.class, NameFactory.MODULE_BUILDER);
457 infoBuilder.addReference("SecurityBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
458 infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
459 infoBuilder.addReference("NamingBuilders", NamingBuilder.class, NameFactory.MODULE_BUILDER);
460 infoBuilder.addReference("ResourceEnvironmentSetter", ResourceEnvironmentSetter.class, NameFactory.MODULE_BUILDER);
461 infoBuilder.addAttribute("kernel", Kernel.class, false);
462 infoBuilder.addInterface(ModuleBuilder.class);
463
464 infoBuilder.setConstructor(new String[]{
465 "defaultEnvironment",
466 "tomcatContainerName",
467 "WebServiceBuilder",
468 "SecurityBuilders",
469 "ServiceBuilders",
470 "NamingBuilders",
471 "ResourceEnvironmentSetter",
472 "kernel"});
473 GBEAN_INFO = infoBuilder.getBeanInfo();
474 }
475
476 public static GBeanInfo getGBeanInfo() {
477 return GBEAN_INFO;
478 }
479
480 }