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.web25.deployment;
019
020 import java.io.File;
021 import java.io.IOException;
022 import java.net.URI;
023 import java.net.URISyntaxException;
024 import java.net.URL;
025 import java.security.Permission;
026 import java.security.PermissionCollection;
027 import java.security.Permissions;
028 import java.util.ArrayList;
029 import java.util.Collection;
030 import java.util.Collections;
031 import java.util.Enumeration;
032 import java.util.HashMap;
033 import java.util.HashSet;
034 import java.util.LinkedList;
035 import java.util.List;
036 import java.util.Map;
037 import java.util.Set;
038 import java.util.LinkedHashSet;
039 import java.util.jar.JarEntry;
040 import java.util.jar.JarFile;
041 import java.util.zip.ZipEntry;
042
043 import javax.security.jacc.WebResourcePermission;
044 import javax.security.jacc.WebRoleRefPermission;
045 import javax.security.jacc.WebUserDataPermission;
046 import javax.xml.namespace.QName;
047
048 import org.apache.commons.logging.Log;
049 import org.apache.commons.logging.LogFactory;
050 import org.apache.geronimo.common.DeploymentException;
051 import org.apache.geronimo.deployment.ModuleIDBuilder;
052 import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
053 import org.apache.geronimo.deployment.ClassPathList;
054 import org.apache.geronimo.deployment.ModuleList;
055 import org.apache.geronimo.deployment.util.DeploymentUtil;
056 import org.apache.geronimo.deployment.xbeans.ServiceDocument;
057 import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
058 import org.apache.geronimo.gbean.AbstractName;
059 import org.apache.geronimo.gbean.AbstractNameQuery;
060 import org.apache.geronimo.gbean.GBeanData;
061 import org.apache.geronimo.j2ee.annotation.Holder;
062 import org.apache.geronimo.j2ee.deployment.EARContext;
063 import org.apache.geronimo.j2ee.deployment.Module;
064 import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
065 import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
066 import org.apache.geronimo.j2ee.deployment.NamingBuilder;
067 import org.apache.geronimo.j2ee.deployment.WebModule;
068 import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
069 import org.apache.geronimo.j2ee.deployment.annotation.SecurityAnnotationHelper;
070 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
071 import org.apache.geronimo.kernel.Kernel;
072 import org.apache.geronimo.kernel.Naming;
073 import org.apache.geronimo.kernel.config.Configuration;
074 import org.apache.geronimo.kernel.config.ConfigurationModuleType;
075 import org.apache.geronimo.kernel.config.ConfigurationStore;
076 import org.apache.geronimo.kernel.repository.Artifact;
077 import org.apache.geronimo.kernel.repository.Environment;
078 import org.apache.geronimo.kernel.repository.ImportType;
079 import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter;
080 import org.apache.geronimo.schema.SchemaConversionUtils;
081 import org.apache.geronimo.security.jacc.ComponentPermissions;
082 import org.apache.geronimo.security.util.HTTPMethods;
083 import org.apache.geronimo.security.util.URLPattern;
084 import org.apache.geronimo.xbeans.geronimo.j2ee.GerSecurityDocument;
085 import org.apache.geronimo.xbeans.javaee.FilterMappingType;
086 import org.apache.geronimo.xbeans.javaee.FilterType;
087 import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
088 import org.apache.geronimo.xbeans.javaee.ListenerType;
089 import org.apache.geronimo.xbeans.javaee.RoleNameType;
090 import org.apache.geronimo.xbeans.javaee.SecurityConstraintType;
091 import org.apache.geronimo.xbeans.javaee.SecurityRoleRefType;
092 import org.apache.geronimo.xbeans.javaee.SecurityRoleType;
093 import org.apache.geronimo.xbeans.javaee.ServletMappingType;
094 import org.apache.geronimo.xbeans.javaee.ServletType;
095 import org.apache.geronimo.xbeans.javaee.UrlPatternType;
096 import org.apache.geronimo.xbeans.javaee.WebAppDocument;
097 import org.apache.geronimo.xbeans.javaee.WebAppType;
098 import org.apache.geronimo.xbeans.javaee.WebResourceCollectionType;
099 import org.apache.xbean.finder.ClassFinder;
100 import org.apache.xmlbeans.XmlCursor;
101 import org.apache.xmlbeans.XmlDocumentProperties;
102 import org.apache.xmlbeans.XmlException;
103 import org.apache.xmlbeans.XmlObject;
104
105 /**
106 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
107 */
108 public abstract class AbstractWebModuleBuilder implements ModuleBuilder {
109 private static final Log log = LogFactory.getLog(AbstractWebModuleBuilder.class);
110
111 private static final QName TAGLIB = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "taglib");
112
113 private static final String LINE_SEP = System.getProperty("line.separator");
114
115 protected static final AbstractNameQuery MANAGED_CONNECTION_FACTORY_PATTERN;
116 private static final AbstractNameQuery ADMIN_OBJECT_PATTERN;
117 protected static final AbstractNameQuery STATELESS_SESSION_BEAN_PATTERN;
118 protected static final AbstractNameQuery STATEFUL_SESSION_BEAN_PATTERN;
119 protected static final AbstractNameQuery ENTITY_BEAN_PATTERN;
120 protected final Kernel kernel;
121 protected final NamespaceDrivenBuilderCollection securityBuilders;
122 protected final NamespaceDrivenBuilderCollection serviceBuilders;
123 protected final ResourceEnvironmentSetter resourceEnvironmentSetter;
124 protected final Collection<WebServiceBuilder> webServiceBuilder;
125
126 protected final NamingBuilder namingBuilders;
127 protected final Collection<ModuleBuilderExtension> moduleBuilderExtensions;
128
129 private static final QName SECURITY_QNAME = GerSecurityDocument.type.getDocumentElementName();
130 private static final QName SERVICE_QNAME = ServiceDocument.type.getDocumentElementName();
131
132 /**
133 * Manifest classpath entries in a war configuration must be resolved relative to the war configuration, not the
134 * enclosing ear configuration. Resolving relative to he war configuration using this offset produces the same
135 * effect as URI.create(module.targetPath()).resolve(mcpEntry) executed in the ear configuration.
136 */
137 private static final URI RELATIVE_MODULE_BASE_URI = URI.create("../");
138
139 protected AbstractWebModuleBuilder(Kernel kernel, Collection securityBuilders, Collection serviceBuilders, NamingBuilder namingBuilders, ResourceEnvironmentSetter resourceEnvironmentSetter, Collection<WebServiceBuilder> webServiceBuilder, Collection<ModuleBuilderExtension> moduleBuilderExtensions) {
140 this.kernel = kernel;
141 this.securityBuilders = new NamespaceDrivenBuilderCollection(securityBuilders, SECURITY_QNAME);
142 this.serviceBuilders = new NamespaceDrivenBuilderCollection(serviceBuilders, SERVICE_QNAME);
143 this.namingBuilders = namingBuilders;
144 this.resourceEnvironmentSetter = resourceEnvironmentSetter;
145 this.webServiceBuilder = webServiceBuilder;
146 this.moduleBuilderExtensions = moduleBuilderExtensions == null? new ArrayList<ModuleBuilderExtension>(): moduleBuilderExtensions;
147 }
148
149 static {
150 MANAGED_CONNECTION_FACTORY_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.JCA_MANAGED_CONNECTION_FACTORY));
151 ADMIN_OBJECT_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.JCA_ADMIN_OBJECT));
152 STATELESS_SESSION_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.STATELESS_SESSION_BEAN));
153 STATEFUL_SESSION_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.STATEFUL_SESSION_BEAN));
154 ENTITY_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.ENTITY_BEAN));
155
156 }
157
158 public NamingBuilder getNamingBuilders() {
159 return namingBuilders;
160 }
161
162 protected void addGBeanDependencies(EARContext earContext, GBeanData webModuleData) {
163 Configuration earConfiguration = earContext.getConfiguration();
164 addDependencies(earContext.findGBeanDatas(earConfiguration, MANAGED_CONNECTION_FACTORY_PATTERN), webModuleData);
165 addDependencies(earContext.findGBeanDatas(earConfiguration, ADMIN_OBJECT_PATTERN), webModuleData);
166 addDependencies(earContext.findGBeanDatas(earConfiguration, STATELESS_SESSION_BEAN_PATTERN), webModuleData);
167 addDependencies(earContext.findGBeanDatas(earConfiguration, STATEFUL_SESSION_BEAN_PATTERN), webModuleData);
168 addDependencies(earContext.findGBeanDatas(earConfiguration, ENTITY_BEAN_PATTERN), webModuleData);
169 }
170
171 private void addDependencies(LinkedHashSet<GBeanData> dependencies, GBeanData webModuleData) {
172 for (GBeanData dependency: dependencies) {
173 AbstractName dependencyName = dependency.getAbstractName();
174 webModuleData.addDependency(dependencyName);
175 }
176 }
177
178 public Module createModule(File plan, JarFile moduleFile, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
179 return createModule(plan, moduleFile, ".", null, true, null, null, naming, idBuilder);
180 }
181
182 public Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
183 return createModule(plan, moduleFile, targetPath, specDDUrl, false, (String) moduleContextInfo, earName, naming, idBuilder);
184 }
185
186 protected abstract Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, boolean standAlone, String contextRoot, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException;
187
188 /**
189 * Some servlets will have multiple url patterns. However, webservice servlets
190 * will only have one, which is what this method is intended for.
191 *
192 * @param webApp spec deployment descriptor
193 * @param contextRoot context root for web app from application.xml or geronimo plan
194 * @return map of servlet names to path mapped to them. Possibly inaccurate except for web services.
195 */
196 protected Map<String, String> buildServletNameToPathMap(WebAppType webApp, String contextRoot) {
197 if (contextRoot == null) {
198 contextRoot = "";
199 } else if (!contextRoot.startsWith("/")) {
200 contextRoot = "/" + contextRoot;
201 }
202 Map<String, String> map = new HashMap<String, String>();
203 ServletMappingType[] servletMappings = webApp.getServletMappingArray();
204 for (ServletMappingType servletMapping : servletMappings) {
205 String servletName = servletMapping.getServletName().getStringValue().trim();
206 UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray();
207
208 for (int i = 0; urlPatterns != null && (i < urlPatterns.length); i++) {
209 map.put(servletName, contextRoot + urlPatterns[i].getStringValue().trim());
210 }
211 }
212 return map;
213 }
214
215 protected String determineDefaultContextRoot(WebAppType webApp, boolean isStandAlone, JarFile moduleFile, String targetPath) {
216
217 if (webApp != null && webApp.getId() != null) {
218 return webApp.getId();
219 }
220
221 if (isStandAlone) {
222 // default configId is based on the moduleFile name
223 return "/" + trimPath(new File(moduleFile.getName()).getName());
224 }
225
226 // default configId is based on the module uri from the application.xml
227 return trimPath(targetPath);
228 }
229
230 private String trimPath(String path) {
231
232 if (path == null) {
233 return null;
234 }
235
236 if (path.endsWith(".war")) {
237 path = path.substring(0, path.length() - 4);
238 }
239 if (path.endsWith("/")) {
240 path = path.substring(0, path.length() - 1);
241 }
242
243 return path;
244 }
245
246 public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repositories) throws DeploymentException {
247 EARContext moduleContext;
248 if (module.isStandAlone()) {
249 moduleContext = earContext;
250 } else {
251 Environment environment = module.getEnvironment();
252 Artifact earConfigId = earContext.getConfigID();
253 Artifact configId = new Artifact(earConfigId.getGroupId(), earConfigId.getArtifactId() + "_" + module.getTargetPath(), earConfigId.getVersion(), "car");
254 environment.setConfigId(configId);
255 environment.addDependency(earConfigId, ImportType.ALL);
256 File configurationDir = new File(earContext.getBaseDir(), module.getTargetPath());
257 configurationDir.mkdirs();
258
259 // construct the web app deployment context... this is the same class used by the ear context
260 try {
261 File inPlaceConfigurationDir = null;
262 if (null != earContext.getInPlaceConfigurationDir()) {
263 inPlaceConfigurationDir = new File(earContext.getInPlaceConfigurationDir(), module.getTargetPath());
264 }
265 moduleContext = new EARContext(configurationDir,
266 inPlaceConfigurationDir,
267 environment,
268 ConfigurationModuleType.WAR,
269 module.getModuleName(),
270 earContext);
271 } catch (DeploymentException e) {
272 cleanupConfigurationDir(configurationDir);
273 throw e;
274 }
275 }
276 module.setEarContext(moduleContext);
277 module.setRootEarContext(earContext);
278
279 try {
280 ClassPathList manifestcp = new ClassPathList();
281 // add the warfile's content to the configuration
282 JarFile warFile = module.getModuleFile();
283 Enumeration<JarEntry> entries = warFile.entries();
284 List<ZipEntry> libs = new ArrayList<ZipEntry>();
285 while (entries.hasMoreElements()) {
286 ZipEntry entry = entries.nextElement();
287 URI targetPath = new URI(null, entry.getName(), null);
288 if (entry.getName().equals("WEB-INF/web.xml")) {
289 moduleContext.addFile(targetPath, module.getOriginalSpecDD());
290 } else if (entry.getName().startsWith("WEB-INF/lib") && entry.getName().endsWith(".jar")) {
291 // keep a collection of all libs in the war
292 // libs must be installed after WEB-INF/classes which must be installed after this copy phase
293 libs.add(entry);
294 } else {
295 moduleContext.addFile(targetPath, warFile, entry);
296 }
297 }
298
299 // always add WEB-INF/classes to the classpath regardless of whether
300 // any classes exist. This must be searched BEFORE the WEB-INF/lib jar files,
301 // per the servlet specifications.
302 moduleContext.getConfiguration().addToClassPath("WEB-INF/classes/");
303 manifestcp.add("WEB-INF/classes/");
304
305 // install the libs
306 for (ZipEntry entry : libs) {
307 URI targetPath = new URI(null, entry.getName(), null);
308 moduleContext.addInclude(targetPath, warFile, entry);
309 manifestcp.add(entry.getName());
310 }
311
312 // add the manifest classpath entries declared in the war to the class loader
313 // we have to explicitly add these since we are unpacking the web module
314 // and the url class loader will not pick up a manifest from an unpacked dir
315 moduleContext.addManifestClassPath(warFile, RELATIVE_MODULE_BASE_URI);
316 moduleContext.getGeneralData().put(ClassPathList.class, manifestcp);
317
318 } catch (IOException e) {
319 throw new DeploymentException("Problem deploying war", e);
320 } catch (URISyntaxException e) {
321 throw new DeploymentException("Could not construct URI for location of war entry", e);
322 } finally {
323 if (!module.isStandAlone()) {
324 try {
325 moduleContext.flush();
326 } catch (IOException e) {
327 throw new DeploymentException("Problem closing war context", e);
328 }
329 }
330 }
331 for (ModuleBuilderExtension mbe: moduleBuilderExtensions) {
332 mbe.installModule(earFile, earContext, module, configurationStores, targetConfigurationStore, repositories);
333 }
334 }
335
336 protected void basicInitContext(EARContext earContext, Module module, XmlObject gerWebApp, boolean hasSecurityRealmName) throws DeploymentException {
337 //complete manifest classpath
338 EARContext moduleContext = module.getEarContext();
339 ClassPathList manifestcp = (ClassPathList) moduleContext.getGeneralData().get(ClassPathList.class);
340 ModuleList moduleLocations = (ModuleList) module.getRootEarContext().getGeneralData().get(ModuleList.class);
341 URI baseUri = URI.create(module.getTargetPath());
342 URI resolutionUri = invertURI(baseUri);
343 earContext.getCompleteManifestClassPath(module.getModuleFile(), baseUri, resolutionUri, manifestcp, moduleLocations);
344
345
346 WebAppType webApp = (WebAppType) module.getSpecDD();
347 if ((webApp.getSecurityConstraintArray().length > 0 || webApp.getSecurityRoleArray().length > 0) &&
348 !hasSecurityRealmName) {
349 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.");
350 }
351 XmlObject[] securityElements = XmlBeansUtil.selectSubstitutionGroupElements(SECURITY_QNAME, gerWebApp);
352 if (securityElements.length > 0 && !hasSecurityRealmName) {
353 throw new DeploymentException("You have supplied a security configuration for web app " + module.getName() + " but no security-realm-name to allow login");
354 }
355 getNamingBuilders().buildEnvironment(webApp, module.getVendorDD(), module.getEnvironment());
356 //this is silly
357 getNamingBuilders().initContext(webApp, gerWebApp, module);
358
359 Map servletNameToPathMap = buildServletNameToPathMap((WebAppType) module.getSpecDD(), ((WebModule) module).getContextRoot());
360
361 Map sharedContext = module.getSharedContext();
362 for (Object aWebServiceBuilder : webServiceBuilder) {
363 WebServiceBuilder serviceBuilder = (WebServiceBuilder) aWebServiceBuilder;
364 serviceBuilder.findWebServices(module, false, servletNameToPathMap, module.getEnvironment(), sharedContext);
365 }
366 securityBuilders.build(gerWebApp, earContext, module.getEarContext());
367 serviceBuilders.build(gerWebApp, earContext, module.getEarContext());
368 }
369
370 static URI invertURI(URI baseUri) {
371 URI resolutionUri = URI.create(".");
372 for (URI test = baseUri; !test.equals(RELATIVE_MODULE_BASE_URI); test = test.resolve(RELATIVE_MODULE_BASE_URI)) {
373 resolutionUri = resolutionUri.resolve(RELATIVE_MODULE_BASE_URI);
374 }
375 return resolutionUri;
376 }
377
378 protected WebAppDocument convertToServletSchema(XmlObject xmlObject) throws XmlException {
379
380 String schemaLocationURL = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd";
381 String version = "2.5";
382 XmlCursor cursor = xmlObject.newCursor();
383 try {
384 cursor.toStartDoc();
385 cursor.toFirstChild();
386 if ("http://java.sun.com/xml/ns/j2ee".equals(cursor.getName().getNamespaceURI())) {
387 SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
388 XmlObject result = xmlObject.changeType(WebAppDocument.type);
389 XmlBeansUtil.validateDD(result);
390 return (WebAppDocument) result;
391 }
392
393 if ("http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI())) {
394 SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
395 XmlObject result = xmlObject.changeType(WebAppDocument.type);
396 XmlBeansUtil.validateDD(result);
397 return (WebAppDocument) result;
398 }
399
400 //otherwise assume DTD
401 XmlDocumentProperties xmlDocumentProperties = cursor.documentProperties();
402 String publicId = xmlDocumentProperties.getDoctypePublicId();
403 boolean is22 = "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN".equals(publicId);
404 if ("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN".equals(publicId) ||
405 is22) {
406 XmlCursor moveable = xmlObject.newCursor();
407 try {
408 moveable.toStartDoc();
409 moveable.toFirstChild();
410
411 SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
412 cursor.toStartDoc();
413 cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "web-app");
414 cursor.toFirstChild();
415 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
416 SchemaConversionUtils.convertToJNDIEnvironmentRefsGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
417 cursor.push();
418 if (cursor.toNextSibling(TAGLIB)) {
419 cursor.toPrevSibling();
420 moveable.toCursor(cursor);
421 cursor.beginElement("jsp-config", SchemaConversionUtils.JAVAEE_NAMESPACE);
422 while (moveable.toNextSibling(TAGLIB)) {
423 moveable.moveXml(cursor);
424 }
425 }
426 cursor.pop();
427 do {
428 String name = cursor.getName().getLocalPart();
429 if ("filter".equals(name) || "servlet".equals(name) || "context-param".equals(name)) {
430 cursor.push();
431 cursor.toFirstChild();
432 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
433 while (cursor.toNextSibling(SchemaConversionUtils.JAVAEE_NAMESPACE, "init-param")) {
434 cursor.push();
435 cursor.toFirstChild();
436 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
437 cursor.pop();
438 }
439 cursor.pop();
440 cursor.push();
441 if (cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "jsp-file")) {
442 String jspFile = cursor.getTextValue();
443 if (!jspFile.startsWith("/")){
444 if (is22) {
445 cursor.setTextValue("/" + jspFile);
446 } else {
447 throw new XmlException("jsp-file does not start with / and this is not a 2.2 web app: " + jspFile);
448 }
449 }
450 }
451 cursor.pop();
452 }
453 } while (cursor.toNextSibling());
454 } finally {
455 moveable.dispose();
456 }
457 }
458 } finally {
459 cursor.dispose();
460 }
461 XmlObject result = xmlObject.changeType(WebAppDocument.type);
462 if (result != null) {
463 XmlBeansUtil.validateDD(result);
464 return (WebAppDocument) result;
465 }
466 XmlBeansUtil.validateDD(xmlObject);
467 return (WebAppDocument) xmlObject;
468 }
469
470
471 protected void addUnmappedJSPPermissions(Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) {
472 for (String roleName : securityRoles) {
473 addPermissionToRole(roleName, new WebRoleRefPermission("", roleName), rolePermissions);
474 }
475 }
476
477 protected ComponentPermissions buildSpecSecurityConfig(WebAppType webApp, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) {
478 Map<String, URLPattern> uncheckedPatterns = new HashMap<String, URLPattern>();
479 Map<UncheckedItem, HTTPMethods> uncheckedResourcePatterns = new HashMap<UncheckedItem, HTTPMethods>();
480 Map<UncheckedItem, HTTPMethods> uncheckedUserPatterns = new HashMap<UncheckedItem, HTTPMethods>();
481 Map<String, URLPattern> excludedPatterns = new HashMap<String, URLPattern>();
482 Map<String, URLPattern> rolesPatterns = new HashMap<String, URLPattern>();
483 Set<URLPattern> allSet = new HashSet<URLPattern>(); // == allMap.values()
484 Map<String, URLPattern> allMap = new HashMap<String, URLPattern>(); //uncheckedPatterns union excludedPatterns union rolesPatterns.
485
486 SecurityConstraintType[] securityConstraintArray = webApp.getSecurityConstraintArray();
487 for (SecurityConstraintType securityConstraintType : securityConstraintArray) {
488 Map<String, URLPattern> currentPatterns;
489 if (securityConstraintType.isSetAuthConstraint()) {
490 if (securityConstraintType.getAuthConstraint().getRoleNameArray().length == 0) {
491 currentPatterns = excludedPatterns;
492 } else {
493 currentPatterns = rolesPatterns;
494 }
495 } else {
496 currentPatterns = uncheckedPatterns;
497 }
498
499 String transport = "";
500 if (securityConstraintType.isSetUserDataConstraint()) {
501 transport = securityConstraintType.getUserDataConstraint().getTransportGuarantee().getStringValue().trim().toUpperCase();
502 }
503
504 WebResourceCollectionType[] webResourceCollectionTypeArray = securityConstraintType.getWebResourceCollectionArray();
505 for (WebResourceCollectionType webResourceCollectionType : webResourceCollectionTypeArray) {
506 UrlPatternType[] urlPatternTypeArray = webResourceCollectionType.getUrlPatternArray();
507 for (UrlPatternType urlPatternType : urlPatternTypeArray) {
508 String url = urlPatternType.getStringValue().trim();
509 URLPattern pattern = currentPatterns.get(url);
510 if (pattern == null) {
511 pattern = new URLPattern(url);
512 currentPatterns.put(url, pattern);
513 }
514
515 URLPattern allPattern = allMap.get(url);
516 if (allPattern == null) {
517 allPattern = new URLPattern(url);
518 allSet.add(allPattern);
519 allMap.put(url, allPattern);
520 }
521
522 String[] httpMethodTypeArray = webResourceCollectionType.getHttpMethodArray();
523 if (httpMethodTypeArray.length == 0) {
524 pattern.addMethod("");
525 allPattern.addMethod("");
526 } else {
527 for (String aHttpMethodTypeArray : httpMethodTypeArray) {
528 String method = (aHttpMethodTypeArray == null ? null : aHttpMethodTypeArray.trim());
529 if (method != null) {
530 pattern.addMethod(method);
531 allPattern.addMethod(method);
532 }
533 }
534 }
535 if (currentPatterns == rolesPatterns) {
536 RoleNameType[] roleNameTypeArray = securityConstraintType.getAuthConstraint().getRoleNameArray();
537 for (RoleNameType roleNameType : roleNameTypeArray) {
538 String role = roleNameType.getStringValue().trim();
539 if (role.equals("*")) {
540 pattern.addAllRoles(securityRoles);
541 } else {
542 pattern.addRole(role);
543 }
544 }
545 }
546
547 pattern.setTransport(transport);
548 }
549 }
550 }
551
552 PermissionCollection excludedPermissions = new Permissions();
553 PermissionCollection uncheckedPermissions = new Permissions();
554
555 for (URLPattern pattern : excludedPatterns.values()) {
556 String name = pattern.getQualifiedPattern(allSet);
557 String actions = pattern.getMethods();
558
559 excludedPermissions.add(new WebResourcePermission(name, actions));
560 excludedPermissions.add(new WebUserDataPermission(name, actions));
561 }
562
563 for (URLPattern pattern : rolesPatterns.values()) {
564 String name = pattern.getQualifiedPattern(allSet);
565 String actions = pattern.getMethods();
566 WebResourcePermission permission = new WebResourcePermission(name, actions);
567
568 for (String roleName : pattern.getRoles()) {
569 addPermissionToRole(roleName, permission, rolePermissions);
570 }
571 HTTPMethods methods = pattern.getHTTPMethods();
572 int transportType = pattern.getTransport();
573
574 addOrUpdatePattern(uncheckedUserPatterns, name, methods, transportType);
575 }
576
577 for (URLPattern pattern : uncheckedPatterns.values()) {
578 String name = pattern.getQualifiedPattern(allSet);
579 HTTPMethods methods = pattern.getHTTPMethods();
580
581 addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA);
582
583 int transportType = pattern.getTransport();
584 addOrUpdatePattern(uncheckedUserPatterns, name, methods, transportType);
585 }
586
587 /**
588 * A <code>WebResourcePermission</code> and a <code>WebUserDataPermission</code> must be instantiated for
589 * each <tt>url-pattern</tt> in the deployment descriptor and the default pattern "/", that is not combined
590 * by the <tt>web-resource-collection</tt> elements of the deployment descriptor with ever HTTP method
591 * value. The permission objects must be contructed using the qualified pattern as their name and with
592 * actions defined by the subset of the HTTP methods that do not occur in combination with the pattern.
593 * The resulting permissions that must be added to the unchecked policy statements by calling the
594 * <code>addToUncheckedPolcy</code> method on the <code>PolicyConfiguration</code> object.
595 */
596 for (URLPattern pattern : allSet) {
597 String name = pattern.getQualifiedPattern(allSet);
598 HTTPMethods methods = pattern.getComplementedHTTPMethods();
599
600 if (methods.isNone()) {
601 continue;
602 }
603
604 addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA);
605 addOrUpdatePattern(uncheckedUserPatterns, name, methods, URLPattern.NA);
606 }
607
608 URLPattern pattern = new URLPattern("/");
609 if (!allSet.contains(pattern)) {
610 String name = pattern.getQualifiedPattern(allSet);
611 HTTPMethods methods = pattern.getComplementedHTTPMethods();
612
613 addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA);
614 addOrUpdatePattern(uncheckedUserPatterns, name, methods, URLPattern.NA);
615 }
616
617 //Create the uncheckedPermissions for WebResourcePermissions
618 for (UncheckedItem item : uncheckedResourcePatterns.keySet()) {
619 HTTPMethods methods = uncheckedResourcePatterns.get(item);
620 String actions = URLPattern.getMethodsWithTransport(methods, item.getTransportType());
621
622 uncheckedPermissions.add(new WebResourcePermission(item.getName(), actions));
623 }
624 //Create the uncheckedPermissions for WebUserDataPermissions
625 for (UncheckedItem item : uncheckedUserPatterns.keySet()) {
626 HTTPMethods methods = uncheckedUserPatterns.get(item);
627 String actions = URLPattern.getMethodsWithTransport(methods, item.getTransportType());
628
629 uncheckedPermissions.add(new WebUserDataPermission(item.getName(), actions));
630 }
631
632 return new ComponentPermissions(excludedPermissions, uncheckedPermissions, rolePermissions);
633
634 }
635
636 protected void addPermissionToRole(String roleName, Permission permission, Map<String, PermissionCollection> rolePermissions) {
637 PermissionCollection permissionsForRole = rolePermissions.get(roleName);
638 if (permissionsForRole == null) {
639 permissionsForRole = new Permissions();
640 rolePermissions.put(roleName, permissionsForRole);
641 }
642 permissionsForRole.add(permission);
643 }
644
645 private void addOrUpdatePattern(Map<UncheckedItem, HTTPMethods> patternMap, String name, HTTPMethods actions, int transportType) {
646 UncheckedItem item = new UncheckedItem(name, transportType);
647 HTTPMethods existingActions = patternMap.get(item);
648 if (existingActions != null) {
649 patternMap.put(item, existingActions.add(actions));
650 return;
651 }
652
653 patternMap.put(item, new HTTPMethods(actions, false));
654 }
655
656 protected static Set<String> collectRoleNames(WebAppType webApp) {
657 Set<String> roleNames = new HashSet<String>();
658
659 SecurityRoleType[] securityRoles = webApp.getSecurityRoleArray();
660 for (SecurityRoleType securityRole : securityRoles) {
661 roleNames.add(securityRole.getRoleName().getStringValue().trim());
662 }
663
664 return roleNames;
665 }
666
667 protected static void check(WebAppType webApp) throws DeploymentException {
668 checkURLPattern(webApp);
669 checkMultiplicities(webApp);
670 }
671
672 private static void checkURLPattern(WebAppType webApp) throws DeploymentException {
673
674 FilterMappingType[] filterMappings = webApp.getFilterMappingArray();
675 for (FilterMappingType filterMapping : filterMappings) {
676 UrlPatternType[] urlPatterns = filterMapping.getUrlPatternArray();
677 for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) {
678 checkString(urlPatterns[j].getStringValue().trim());
679 }
680 }
681
682 ServletMappingType[] servletMappings = webApp.getServletMappingArray();
683 for (ServletMappingType servletMapping : servletMappings) {
684 UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray();
685 for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) {
686 checkString(urlPatterns[j].getStringValue().trim());
687 }
688 }
689
690 SecurityConstraintType[] constraints = webApp.getSecurityConstraintArray();
691 for (SecurityConstraintType constraint : constraints) {
692 WebResourceCollectionType[] collections = constraint.getWebResourceCollectionArray();
693 for (WebResourceCollectionType collection : collections) {
694 UrlPatternType[] patterns = collection.getUrlPatternArray();
695 for (UrlPatternType pattern : patterns) {
696 checkString(pattern.getStringValue().trim());
697 }
698 }
699 }
700 }
701
702 protected static void checkString(String pattern) throws DeploymentException {
703 //j2ee_1_4.xsd explicitly requires preserving all whitespace. Do not trim.
704 if (pattern.indexOf(0x0D) >= 0) throw new DeploymentException("<url-pattern> must not contain CR(#xD)");
705 if (pattern.indexOf(0x0A) >= 0) throw new DeploymentException("<url-pattern> must not contain LF(#xA)");
706 }
707
708 private static void checkMultiplicities(WebAppType webApp) throws DeploymentException {
709 if (webApp.getSessionConfigArray().length > 1)
710 throw new DeploymentException("Multiple <session-config> elements found");
711 if (webApp.getJspConfigArray().length > 1)
712 throw new DeploymentException("Multiple <jsp-config> elements found");
713 if (webApp.getLoginConfigArray().length > 1)
714 throw new DeploymentException("Multiple <login-config> elements found");
715 }
716
717 private boolean cleanupConfigurationDir(File configurationDir) {
718 LinkedList<String> cannotBeDeletedList = new LinkedList<String>();
719
720 if (!DeploymentUtil.recursiveDelete(configurationDir, cannotBeDeletedList)) {
721 // Output a message to help user track down file problem
722 log.warn("Unable to delete " + cannotBeDeletedList.size() +
723 " files while recursively deleting directory "
724 + configurationDir.getAbsolutePath() + LINE_SEP +
725 "The first file that could not be deleted was:" + LINE_SEP + " " +
726 (!cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : ""));
727 return false;
728 }
729 return true;
730 }
731
732 protected void processRoleRefPermissions(ServletType servletType, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) {
733 String servletName = servletType.getServletName().getStringValue().trim();
734 //WebRoleRefPermissions
735 SecurityRoleRefType[] securityRoleRefTypeArray = servletType.getSecurityRoleRefArray();
736 Set<String> unmappedRoles = new HashSet<String>(securityRoles);
737 for (SecurityRoleRefType securityRoleRefType : securityRoleRefTypeArray) {
738 String roleName = securityRoleRefType.getRoleName().getStringValue().trim();
739 String roleLink = securityRoleRefType.getRoleLink().getStringValue().trim();
740 //jacc 3.1.3.2
741 /* The name of the WebRoleRefPermission must be the servlet-name in whose
742 * context the security-role-ref is defined. The actions of the WebRoleRefPermission
743 * must be the value of the role-name (that is the reference), appearing in the security-role-ref.
744 * The deployment tools must call the addToRole method on the PolicyConfiguration object to add the
745 * WebRoleRefPermission object resulting from the translation to the role
746 * identified in the role-link appearing in the security-role-ref.
747 */
748 addPermissionToRole(roleLink, new WebRoleRefPermission(servletName, roleName), rolePermissions);
749 unmappedRoles.remove(roleName);
750 }
751 for (String roleName : unmappedRoles) {
752 addPermissionToRole(roleName, new WebRoleRefPermission(servletName, roleName), rolePermissions);
753 }
754 }
755
756 protected ClassFinder createWebAppClassFinder(WebAppType webApp, WebModule webModule) throws DeploymentException {
757 // Get the classloader from the module's EARContext
758 ClassLoader classLoader = webModule.getEarContext().getClassLoader();
759 return createWebAppClassFinder(webApp, classLoader);
760 }
761
762 public static ClassFinder createWebAppClassFinder(WebAppType webApp, ClassLoader classLoader) throws DeploymentException {
763 //------------------------------------------------------------------------------------
764 // Find the list of classes from the web.xml we want to search for annotations in
765 //------------------------------------------------------------------------------------
766 List<Class> classes = new ArrayList<Class>();
767
768 // Get all the servlets from the deployment descriptor
769 ServletType[] servlets = webApp.getServletArray();
770 for (ServletType servlet : servlets) {
771 FullyQualifiedClassType cls = servlet.getServletClass();
772 if (cls != null) { // Don't try this for JSPs
773 Class<?> clas;
774 try {
775 clas = classLoader.loadClass(cls.getStringValue());
776 } catch (ClassNotFoundException e) {
777 throw new DeploymentException("AbstractWebModuleBuilder: Could not load servlet class: " + cls.getStringValue(), e);
778 }
779 addClass(classes, clas);
780 }
781 }
782
783 // Get all the listeners from the deployment descriptor
784 ListenerType[] listeners = webApp.getListenerArray();
785 for (ListenerType listener : listeners) {
786 FullyQualifiedClassType cls = listener.getListenerClass();
787 Class<?> clas;
788 try {
789 clas = classLoader.loadClass(cls.getStringValue());
790 } catch (ClassNotFoundException e) {
791 throw new DeploymentException("AbstractWebModuleBuilder: Could not load listener class: " + cls.getStringValue(), e);
792 }
793 addClass(classes, clas);
794 }
795
796 // Get all the filters from the deployment descriptor
797 FilterType[] filters = webApp.getFilterArray();
798 for (FilterType filter : filters) {
799 FullyQualifiedClassType cls = filter.getFilterClass();
800 Class<?> clas;
801 try {
802 clas = classLoader.loadClass(cls.getStringValue());
803 } catch (ClassNotFoundException e) {
804 throw new DeploymentException("AbstractWebModuleBuilder: Could not load filter class: " + cls.getStringValue(), e);
805 }
806 addClass(classes, clas);
807 }
808
809 // see https://issues.apache.org/jira/browse/GERONIMO-3421 .
810 // if the user has botched her classloader config (perhaps by
811 // not including a jar that her app needs) then ClassFinder
812 // will throw NoClassDefFoundError. we want to indicate that
813 // it's the user's error and provide a little context to help
814 // her fix it.
815 try {
816 return new ClassFinder(classes);
817 } catch (NoClassDefFoundError e) {
818 throw new DeploymentException("Classloader for " + webApp.getId() + "can't find " + e.getMessage(), e);
819 }
820 }
821
822 private static void addClass(List<Class> classes, Class<?> clas) {
823 while (clas != Object.class) {
824 classes.add(clas);
825 clas = clas.getSuperclass();
826 }
827 }
828
829 protected void configureBasicWebModuleAttributes(WebAppType webApp, XmlObject vendorPlan, EARContext moduleContext, EARContext earContext, WebModule webModule, GBeanData webModuleData) throws DeploymentException {
830 Map<NamingBuilder.Key, Object> buildingContext = new HashMap<NamingBuilder.Key, Object>();
831 buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, moduleContext.getModuleName());
832
833 if (!webApp.getMetadataComplete()) {
834 // Create a classfinder and populate it for the naming builder(s). The absence of a
835 // classFinder in the module will convey whether metadata-complete is set (or not)
836 webModule.setClassFinder(createWebAppClassFinder(webApp, webModule));
837 SecurityAnnotationHelper.processAnnotations(webApp, webModule.getClassFinder());
838 }
839 //N.B. we use the ear context which has all the gbeans we could possibly be looking up from this ear.
840 //nope, persistence units can be in the war.
841 //This means that you cannot use the default environment of the web builder to add configs that will be searched.
842 getNamingBuilders().buildNaming(webApp, vendorPlan, webModule, buildingContext);
843
844 Map compContext = NamingBuilder.JNDI_KEY.get(buildingContext);
845 Holder holder = NamingBuilder.INJECTION_KEY.get(buildingContext);
846
847 webModule.getSharedContext().put(WebModule.WEB_APP_DATA, webModuleData);
848 webModule.getSharedContext().put(NamingBuilder.JNDI_KEY, compContext);
849 webModule.getSharedContext().put(NamingBuilder.INJECTION_KEY, holder);
850 if (moduleContext.getServerName() != null) {
851 webModuleData.setReferencePattern("J2EEServer", moduleContext.getServerName());
852 }
853 if (!webModule.isStandAlone()) {
854 webModuleData.setReferencePattern("J2EEApplication", earContext.getModuleName());
855 }
856
857 webModuleData.setAttribute("holder", holder);
858
859 //Add dependencies on managed connection factories and ejbs in this app
860 //This is overkill, but allows for people not using java:comp context (even though we don't support it)
861 //and sidesteps the problem of circular references between ejbs.
862 if (earContext != moduleContext) {
863 addGBeanDependencies(earContext, webModuleData);
864 }
865
866 webModuleData.setAttribute("componentContext", compContext);
867 webModuleData.setReferencePattern("TransactionManager", moduleContext.getTransactionManagerName());
868 webModuleData.setReferencePattern("TrackedConnectionAssociator", moduleContext.getConnectionTrackerName());
869 }
870
871 class UncheckedItem {
872 final static int NA = 0x00;
873 final static int INTEGRAL = 0x01;
874 final static int CONFIDENTIAL = 0x02;
875
876 private int transportType = NA;
877 private String name;
878
879 public UncheckedItem(String name, int transportType) {
880 setName(name);
881 setTransportType(transportType);
882 }
883
884 public boolean equals(Object o) {
885 UncheckedItem item = (UncheckedItem) o;
886 return item.transportType == transportType && item.name.equals(this.name);
887 }
888
889
890 public int hashCode() {
891 return name.hashCode() + transportType;
892 }
893
894 public String getName() {
895 return name;
896 }
897
898 public void setName(String name) {
899 this.name = name;
900 }
901
902 public int getTransportType() {
903 return transportType;
904 }
905
906 public void setTransportType(int transportType) {
907 this.transportType = transportType;
908 }
909 }
910 }