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 }