1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.geronimo.web25.deployment; 19 20 import java.io.File; 21 import java.io.IOException; 22 import java.io.StringWriter; 23 import java.net.URI; 24 import java.net.URISyntaxException; 25 import java.net.URL; 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.Collections; 29 import java.util.Enumeration; 30 import java.util.HashMap; 31 import java.util.LinkedHashSet; 32 import java.util.LinkedList; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.jar.JarEntry; 36 import java.util.jar.JarFile; 37 import java.util.zip.ZipEntry; 38 39 import javax.xml.namespace.QName; 40 import javax.xml.namespace.NamespaceContext; 41 import javax.xml.stream.XMLStreamReader; 42 import javax.xml.stream.XMLStreamException; 43 import javax.xml.stream.Location; 44 import javax.xml.parsers.ParserConfigurationException; 45 import javax.xml.bind.JAXBException; 46 import javax.security.auth.message.module.ServerAuthModule; 47 48 import org.apache.geronimo.common.DeploymentException; 49 import org.apache.geronimo.deployment.ClassPathList; 50 import org.apache.geronimo.deployment.ModuleIDBuilder; 51 import org.apache.geronimo.deployment.ModuleList; 52 import org.apache.geronimo.deployment.NamespaceDrivenBuilder; 53 import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection; 54 import org.apache.geronimo.deployment.util.DeploymentUtil; 55 import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil; 56 import org.apache.geronimo.gbean.AbstractName; 57 import org.apache.geronimo.gbean.AbstractNameQuery; 58 import org.apache.geronimo.gbean.GBeanData; 59 import org.apache.geronimo.gbean.GBeanInfoBuilder; 60 import org.apache.geronimo.j2ee.annotation.Holder; 61 import org.apache.geronimo.j2ee.deployment.EARContext; 62 import org.apache.geronimo.j2ee.deployment.Module; 63 import org.apache.geronimo.j2ee.deployment.ModuleBuilder; 64 import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension; 65 import org.apache.geronimo.j2ee.deployment.NamingBuilder; 66 import org.apache.geronimo.j2ee.deployment.WebModule; 67 import org.apache.geronimo.j2ee.deployment.WebServiceBuilder; 68 import org.apache.geronimo.j2ee.deployment.annotation.SecurityAnnotationHelper; 69 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 70 import org.apache.geronimo.kernel.Kernel; 71 import org.apache.geronimo.kernel.Naming; 72 import org.apache.geronimo.kernel.GBeanAlreadyExistsException; 73 import org.apache.geronimo.kernel.config.Configuration; 74 import org.apache.geronimo.kernel.config.ConfigurationModuleType; 75 import org.apache.geronimo.kernel.config.ConfigurationStore; 76 import org.apache.geronimo.kernel.repository.Artifact; 77 import org.apache.geronimo.kernel.repository.Environment; 78 import org.apache.geronimo.kernel.repository.ImportType; 79 import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter; 80 import org.apache.geronimo.schema.SchemaConversionUtils; 81 import org.apache.geronimo.security.jacc.ComponentPermissions; 82 import org.apache.geronimo.security.jaspi.AuthConfigProviderGBean; 83 import org.apache.geronimo.security.jaspi.ServerAuthConfigGBean; 84 import org.apache.geronimo.security.jaspi.ServerAuthContextGBean; 85 import org.apache.geronimo.security.jaspi.ServerAuthModuleGBean; 86 import org.apache.geronimo.web25.deployment.security.SpecSecurityBuilder; 87 import org.apache.geronimo.web25.deployment.security.AuthenticationWrapper; 88 import org.apache.geronimo.xbeans.geronimo.j2ee.GerSecurityDocument; 89 import org.apache.geronimo.xbeans.javaee.FilterMappingType; 90 import org.apache.geronimo.xbeans.javaee.FilterType; 91 import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType; 92 import org.apache.geronimo.xbeans.javaee.ListenerType; 93 import org.apache.geronimo.xbeans.javaee.SecurityConstraintType; 94 import org.apache.geronimo.xbeans.javaee.ServletMappingType; 95 import org.apache.geronimo.xbeans.javaee.ServletType; 96 import org.apache.geronimo.xbeans.javaee.UrlPatternType; 97 import org.apache.geronimo.xbeans.javaee.WebAppDocument; 98 import org.apache.geronimo.xbeans.javaee.WebAppType; 99 import org.apache.geronimo.xbeans.javaee.WebResourceCollectionType; 100 import org.apache.geronimo.components.jaspi.model.ConfigProviderType; 101 import org.apache.geronimo.components.jaspi.model.JaspiXmlUtil; 102 import org.apache.geronimo.components.jaspi.model.ServerAuthConfigType; 103 import org.apache.geronimo.components.jaspi.model.ServerAuthContextType; 104 import org.apache.geronimo.components.jaspi.model.AuthModuleType; 105 import org.apache.xbean.finder.ClassFinder; 106 import org.apache.xmlbeans.XmlCursor; 107 import org.apache.xmlbeans.XmlDocumentProperties; 108 import org.apache.xmlbeans.XmlException; 109 import org.apache.xmlbeans.XmlObject; 110 import org.slf4j.Logger; 111 import org.slf4j.LoggerFactory; 112 import org.xml.sax.SAXException; 113 114 /** 115 * @version $Rev: 802422 $ $Date: 2009-08-08 10:13:25 -0700 (Sat, 08 Aug 2009) $ 116 */ 117 public abstract class AbstractWebModuleBuilder implements ModuleBuilder { 118 119 public final static NamingBuilder.Key<GBeanData> DEFAULT_JSP_SERVLET_KEY = new NamingBuilder.Key<GBeanData>() { 120 public GBeanData get(Map context) { 121 return (GBeanData) context.get(this); 122 } 123 124 }; 125 private static final Logger log = LoggerFactory.getLogger(AbstractWebModuleBuilder.class); 126 127 private static final QName TAGLIB = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "taglib"); 128 129 private static final String LINE_SEP = System.getProperty("line.separator"); 130 131 protected static final AbstractNameQuery MANAGED_CONNECTION_FACTORY_PATTERN; 132 private static final AbstractNameQuery ADMIN_OBJECT_PATTERN; 133 protected static final AbstractNameQuery STATELESS_SESSION_BEAN_PATTERN; 134 protected static final AbstractNameQuery STATEFUL_SESSION_BEAN_PATTERN; 135 protected static final AbstractNameQuery ENTITY_BEAN_PATTERN; 136 protected final Kernel kernel; 137 protected final NamespaceDrivenBuilderCollection serviceBuilders; 138 protected final ResourceEnvironmentSetter resourceEnvironmentSetter; 139 protected final Collection<WebServiceBuilder> webServiceBuilder; 140 141 protected final NamingBuilder namingBuilders; 142 protected final Collection<ModuleBuilderExtension> moduleBuilderExtensions; 143 144 private static final QName SECURITY_QNAME = GerSecurityDocument.type.getDocumentElementName(); 145 146 /** 147 * Manifest classpath entries in a war configuration must be resolved relative to the war configuration, not the 148 * enclosing ear configuration. Resolving relative to he war configuration using this offset produces the same 149 * effect as URI.create(module.targetPath()).resolve(mcpEntry) executed in the ear configuration. 150 */ 151 private static final URI RELATIVE_MODULE_BASE_URI = URI.create("../"); 152 153 protected AbstractWebModuleBuilder(Kernel kernel, Collection<NamespaceDrivenBuilder> serviceBuilders, NamingBuilder namingBuilders, ResourceEnvironmentSetter resourceEnvironmentSetter, Collection<WebServiceBuilder> webServiceBuilder, Collection<ModuleBuilderExtension> moduleBuilderExtensions) { 154 this.kernel = kernel; 155 this.serviceBuilders = new NamespaceDrivenBuilderCollection(serviceBuilders); 156 this.namingBuilders = namingBuilders; 157 this.resourceEnvironmentSetter = resourceEnvironmentSetter; 158 this.webServiceBuilder = webServiceBuilder; 159 this.moduleBuilderExtensions = moduleBuilderExtensions == null? new ArrayList<ModuleBuilderExtension>(): moduleBuilderExtensions; 160 } 161 162 static { 163 MANAGED_CONNECTION_FACTORY_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.JCA_MANAGED_CONNECTION_FACTORY)); 164 ADMIN_OBJECT_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.JCA_ADMIN_OBJECT)); 165 STATELESS_SESSION_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.STATELESS_SESSION_BEAN)); 166 STATEFUL_SESSION_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.STATEFUL_SESSION_BEAN)); 167 ENTITY_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.ENTITY_BEAN)); 168 169 } 170 171 public NamingBuilder getNamingBuilders() { 172 return namingBuilders; 173 } 174 175 protected void addGBeanDependencies(EARContext earContext, GBeanData webModuleData) { 176 Configuration earConfiguration = earContext.getConfiguration(); 177 addDependencies(earContext.findGBeanDatas(earConfiguration, MANAGED_CONNECTION_FACTORY_PATTERN), webModuleData); 178 addDependencies(earContext.findGBeanDatas(earConfiguration, ADMIN_OBJECT_PATTERN), webModuleData); 179 addDependencies(earContext.findGBeanDatas(earConfiguration, STATELESS_SESSION_BEAN_PATTERN), webModuleData); 180 addDependencies(earContext.findGBeanDatas(earConfiguration, STATEFUL_SESSION_BEAN_PATTERN), webModuleData); 181 addDependencies(earContext.findGBeanDatas(earConfiguration, ENTITY_BEAN_PATTERN), webModuleData); 182 } 183 184 private void addDependencies(LinkedHashSet<GBeanData> dependencies, GBeanData webModuleData) { 185 for (GBeanData dependency: dependencies) { 186 AbstractName dependencyName = dependency.getAbstractName(); 187 webModuleData.addDependency(dependencyName); 188 } 189 } 190 191 public Module createModule(File plan, JarFile moduleFile, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException { 192 return createModule(plan, moduleFile, ".", null, true, null, null, naming, idBuilder); 193 } 194 195 public Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException { 196 return createModule(plan, moduleFile, targetPath, specDDUrl, false, (String) moduleContextInfo, earName, naming, idBuilder); 197 } 198 199 protected abstract Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, boolean standAlone, String contextRoot, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException; 200 201 /** 202 * Some servlets will have multiple url patterns. However, webservice servlets 203 * will only have one, which is what this method is intended for. 204 * 205 * @param webApp spec deployment descriptor 206 * @param contextRoot context root for web app from application.xml or geronimo plan 207 * @return map of servlet names to path mapped to them. Possibly inaccurate except for web services. 208 */ 209 protected Map<String, String> buildServletNameToPathMap(WebAppType webApp, String contextRoot) { 210 if (contextRoot == null) { 211 contextRoot = ""; 212 } else if (!contextRoot.startsWith("/")) { 213 contextRoot = "/" + contextRoot; 214 } 215 Map<String, String> map = new HashMap<String, String>(); 216 ServletMappingType[] servletMappings = webApp.getServletMappingArray(); 217 for (ServletMappingType servletMapping : servletMappings) { 218 String servletName = servletMapping.getServletName().getStringValue().trim(); 219 UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray(); 220 221 for (int i = 0; urlPatterns != null && (i < urlPatterns.length); i++) { 222 map.put(servletName, contextRoot + urlPatterns[i].getStringValue().trim()); 223 } 224 } 225 return map; 226 } 227 228 protected String determineDefaultContextRoot(WebAppType webApp, boolean isStandAlone, JarFile moduleFile, String targetPath) { 229 230 if (webApp != null && webApp.getId() != null) { 231 return webApp.getId(); 232 } 233 234 if (isStandAlone) { 235 // default configId is based on the moduleFile name 236 return "/" + trimPath(new File(moduleFile.getName()).getName()); 237 } 238 239 // default configId is based on the module uri from the application.xml 240 return trimPath(targetPath); 241 } 242 243 private String trimPath(String path) { 244 245 if (path == null) { 246 return null; 247 } 248 249 if (path.endsWith(".war")) { 250 path = path.substring(0, path.length() - 4); 251 } 252 if (path.endsWith("/")) { 253 path = path.substring(0, path.length() - 1); 254 } 255 256 return path; 257 } 258 259 public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repositories) throws DeploymentException { 260 EARContext moduleContext; 261 if (module.isStandAlone()) { 262 moduleContext = earContext; 263 } else { 264 Environment environment = module.getEnvironment(); 265 Artifact earConfigId = earContext.getConfigID(); 266 Artifact configId = new Artifact(earConfigId.getGroupId(), earConfigId.getArtifactId() + "_" + module.getTargetPath(), earConfigId.getVersion(), "car"); 267 environment.setConfigId(configId); 268 environment.addDependency(earConfigId, ImportType.ALL); 269 File configurationDir = new File(earContext.getBaseDir(), module.getTargetPath()); 270 configurationDir.mkdirs(); 271 272 // construct the web app deployment context... this is the same class used by the ear context 273 try { 274 File inPlaceConfigurationDir = null; 275 if (null != earContext.getInPlaceConfigurationDir()) { 276 inPlaceConfigurationDir = new File(earContext.getInPlaceConfigurationDir(), module.getTargetPath()); 277 } 278 moduleContext = new EARContext(configurationDir, 279 inPlaceConfigurationDir, 280 environment, 281 ConfigurationModuleType.WAR, 282 module.getModuleName(), 283 earContext); 284 } catch (DeploymentException e) { 285 cleanupConfigurationDir(configurationDir); 286 throw e; 287 } 288 } 289 module.setEarContext(moduleContext); 290 module.setRootEarContext(earContext); 291 292 try { 293 ClassPathList manifestcp = new ClassPathList(); 294 // add the warfile's content to the configuration 295 JarFile warFile = module.getModuleFile(); 296 Enumeration<JarEntry> entries = warFile.entries(); 297 List<ZipEntry> libs = new ArrayList<ZipEntry>(); 298 while (entries.hasMoreElements()) { 299 ZipEntry entry = entries.nextElement(); 300 URI targetPath = new URI(null, entry.getName(), null); 301 if (entry.getName().equals("WEB-INF/web.xml")) { 302 moduleContext.addFile(targetPath, module.getOriginalSpecDD()); 303 } else if (entry.getName().startsWith("WEB-INF/lib") && entry.getName().endsWith(".jar")) { 304 // keep a collection of all libs in the war 305 // libs must be installed after WEB-INF/classes which must be installed after this copy phase 306 libs.add(entry); 307 } else { 308 moduleContext.addFile(targetPath, warFile, entry); 309 } 310 } 311 312 // always add WEB-INF/classes to the classpath regardless of whether 313 // any classes exist. This must be searched BEFORE the WEB-INF/lib jar files, 314 // per the servlet specifications. 315 moduleContext.getConfiguration().addToClassPath("WEB-INF/classes/"); 316 manifestcp.add("WEB-INF/classes/"); 317 318 // install the libs 319 for (ZipEntry entry : libs) { 320 URI targetPath = new URI(null, entry.getName(), null); 321 moduleContext.addInclude(targetPath, warFile, entry); 322 manifestcp.add(entry.getName()); 323 } 324 325 // add the manifest classpath entries declared in the war to the class loader 326 // we have to explicitly add these since we are unpacking the web module 327 // and the url class loader will not pick up a manifest from an unpacked dir 328 moduleContext.addManifestClassPath(warFile, RELATIVE_MODULE_BASE_URI); 329 moduleContext.getGeneralData().put(ClassPathList.class, manifestcp); 330 331 } catch (IOException e) { 332 throw new DeploymentException("Problem deploying war", e); 333 } catch (URISyntaxException e) { 334 throw new DeploymentException("Could not construct URI for location of war entry", e); 335 } finally { 336 if (!module.isStandAlone()) { 337 try { 338 moduleContext.flush(); 339 } catch (IOException e) { 340 throw new DeploymentException("Problem closing war context", e); 341 } 342 } 343 } 344 for (ModuleBuilderExtension mbe: moduleBuilderExtensions) { 345 mbe.installModule(earFile, earContext, module, configurationStores, targetConfigurationStore, repositories); 346 } 347 } 348 349 protected void basicInitContext(EARContext earContext, Module module, XmlObject gerWebApp, boolean hasSecurityRealmName) throws DeploymentException { 350 //complete manifest classpath 351 EARContext moduleContext = module.getEarContext(); 352 ClassPathList manifestcp = (ClassPathList) moduleContext.getGeneralData().get(ClassPathList.class); 353 ModuleList moduleLocations = (ModuleList) module.getRootEarContext().getGeneralData().get(ModuleList.class); 354 URI baseUri = URI.create(module.getTargetPath()); 355 URI resolutionUri = invertURI(baseUri); 356 earContext.getCompleteManifestClassPath(module.getModuleFile(), baseUri, resolutionUri, manifestcp, moduleLocations); 357 358 359 WebAppType webApp = (WebAppType) module.getSpecDD(); 360 if ((webApp.getSecurityConstraintArray().length > 0 || webApp.getSecurityRoleArray().length > 0)) { 361 if (!hasSecurityRealmName) { 362 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."); 363 } 364 } 365 if (hasSecurityRealmName) { 366 earContext.setHasSecurity(true); 367 } 368 //TODO think about how to provide a default security realm name 369 XmlObject[] securityElements = XmlBeansUtil.selectSubstitutionGroupElements(SECURITY_QNAME, gerWebApp); 370 if (securityElements.length > 0 && !hasSecurityRealmName) { 371 throw new DeploymentException("You have supplied a security configuration for web app " + module.getName() + " but no security-realm-name to allow login"); 372 } 373 getNamingBuilders().buildEnvironment(webApp, module.getVendorDD(), module.getEnvironment()); 374 //this is silly 375 getNamingBuilders().initContext(webApp, gerWebApp, module); 376 377 Map servletNameToPathMap = buildServletNameToPathMap((WebAppType) module.getSpecDD(), ((WebModule) module).getContextRoot()); 378 379 Map sharedContext = module.getSharedContext(); 380 for (Object aWebServiceBuilder : webServiceBuilder) { 381 WebServiceBuilder serviceBuilder = (WebServiceBuilder) aWebServiceBuilder; 382 serviceBuilder.findWebServices(module, false, servletNameToPathMap, module.getEnvironment(), sharedContext); 383 } 384 serviceBuilders.build(gerWebApp, earContext, module.getEarContext()); 385 } 386 387 static URI invertURI(URI baseUri) { 388 URI resolutionUri = URI.create("."); 389 for (URI test = baseUri; !test.equals(RELATIVE_MODULE_BASE_URI); test = test.resolve(RELATIVE_MODULE_BASE_URI)) { 390 resolutionUri = resolutionUri.resolve(RELATIVE_MODULE_BASE_URI); 391 } 392 return resolutionUri; 393 } 394 395 protected WebAppDocument convertToServletSchema(XmlObject xmlObject) throws XmlException { 396 397 String schemaLocationURL = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"; 398 String version = "2.5"; 399 XmlCursor cursor = xmlObject.newCursor(); 400 try { 401 cursor.toStartDoc(); 402 cursor.toFirstChild(); 403 if ("http://java.sun.com/xml/ns/j2ee".equals(cursor.getName().getNamespaceURI())) { 404 SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version); 405 XmlObject result = xmlObject.changeType(WebAppDocument.type); 406 XmlBeansUtil.validateDD(result); 407 return (WebAppDocument) result; 408 } 409 410 if ("http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI())) { 411 SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version); 412 XmlObject result = xmlObject.changeType(WebAppDocument.type); 413 XmlBeansUtil.validateDD(result); 414 return (WebAppDocument) result; 415 } 416 417 //otherwise assume DTD 418 XmlDocumentProperties xmlDocumentProperties = cursor.documentProperties(); 419 String publicId = xmlDocumentProperties.getDoctypePublicId(); 420 boolean is22 = "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN".equals(publicId); 421 if ("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN".equals(publicId) || 422 is22) { 423 XmlCursor moveable = xmlObject.newCursor(); 424 try { 425 moveable.toStartDoc(); 426 moveable.toFirstChild(); 427 428 SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version); 429 cursor.toStartDoc(); 430 cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "web-app"); 431 cursor.toFirstChild(); 432 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 433 SchemaConversionUtils.convertToJNDIEnvironmentRefsGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 434 cursor.push(); 435 if (cursor.toNextSibling(TAGLIB)) { 436 cursor.toPrevSibling(); 437 moveable.toCursor(cursor); 438 cursor.beginElement("jsp-config", SchemaConversionUtils.JAVAEE_NAMESPACE); 439 while (moveable.toNextSibling(TAGLIB)) { 440 moveable.moveXml(cursor); 441 } 442 } 443 cursor.pop(); 444 do { 445 String name = cursor.getName().getLocalPart(); 446 if ("filter".equals(name) || "servlet".equals(name) || "context-param".equals(name)) { 447 cursor.push(); 448 cursor.toFirstChild(); 449 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 450 while (cursor.toNextSibling(SchemaConversionUtils.JAVAEE_NAMESPACE, "init-param")) { 451 cursor.push(); 452 cursor.toFirstChild(); 453 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 454 cursor.pop(); 455 } 456 cursor.pop(); 457 cursor.push(); 458 if (cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "jsp-file")) { 459 String jspFile = cursor.getTextValue(); 460 if (!jspFile.startsWith("/")){ 461 if (is22) { 462 cursor.setTextValue("/" + jspFile); 463 } else { 464 throw new XmlException("jsp-file does not start with / and this is not a 2.2 web app: " + jspFile); 465 } 466 } 467 } 468 cursor.pop(); 469 } 470 } while (cursor.toNextSibling()); 471 } finally { 472 moveable.dispose(); 473 } 474 } 475 } finally { 476 cursor.dispose(); 477 } 478 XmlObject result = xmlObject.changeType(WebAppDocument.type); 479 if (result != null) { 480 XmlBeansUtil.validateDD(result); 481 return (WebAppDocument) result; 482 } 483 XmlBeansUtil.validateDD(xmlObject); 484 return (WebAppDocument) xmlObject; 485 } 486 487 protected ComponentPermissions buildSpecSecurityConfig(WebAppType webApp) { 488 SpecSecurityBuilder builder = new SpecSecurityBuilder(); 489 return builder.buildSpecSecurityConfig(webApp); 490 } 491 492 protected void configureLocalJaspicProvider(AuthenticationWrapper authType, String contextPath, Module module, GBeanData securityFactoryData) throws DeploymentException, GBeanAlreadyExistsException { 493 EARContext moduleContext = module.getEarContext(); 494 GBeanData authConfigProviderData = null; 495 AbstractName providerName = moduleContext.getNaming().createChildName(module.getModuleName(), "authConfigProvider", GBeanInfoBuilder.DEFAULT_J2EE_TYPE); 496 try { 497 if (authType.isSetConfigProvider()) { 498 authConfigProviderData = new GBeanData(providerName, AuthConfigProviderGBean.class); 499 final XmlCursor xmlCursor = authType.getConfigProvider().newCursor(); 500 try { 501 XMLStreamReader reader = new InternWrapper(xmlCursor.newXMLStreamReader()); 502 ConfigProviderType configProviderType = JaspiXmlUtil.loadConfigProvider(reader); 503 StringWriter out = new StringWriter(); 504 JaspiXmlUtil.writeConfigProvider(configProviderType, out); 505 authConfigProviderData.setAttribute("config", out.toString()); 506 } finally { 507 xmlCursor.dispose(); 508 } 509 } else if (authType.isSetServerAuthConfig()) { 510 authConfigProviderData = new GBeanData(providerName, ServerAuthConfigGBean.class); 511 final XmlCursor xmlCursor = authType.getServerAuthConfig().newCursor(); 512 try { 513 XMLStreamReader reader = new InternWrapper(xmlCursor.newXMLStreamReader()); 514 ServerAuthConfigType serverAuthConfigType = JaspiXmlUtil.loadServerAuthConfig(reader); 515 StringWriter out = new StringWriter(); 516 JaspiXmlUtil.writeServerAuthConfig(serverAuthConfigType, out); 517 authConfigProviderData.setAttribute("config", out.toString()); 518 } finally { 519 xmlCursor.dispose(); 520 } 521 } else if (authType.isSetServerAuthContext()) { 522 authConfigProviderData = new GBeanData(providerName, ServerAuthContextGBean.class); 523 final XmlCursor xmlCursor = authType.getServerAuthContext().newCursor(); 524 try { 525 XMLStreamReader reader = new InternWrapper(xmlCursor.newXMLStreamReader()); 526 ServerAuthContextType serverAuthContextType = JaspiXmlUtil.loadServerAuthContext(reader); 527 StringWriter out = new StringWriter(); 528 JaspiXmlUtil.writeServerAuthContext(serverAuthContextType, out); 529 authConfigProviderData.setAttribute("config", out.toString()); 530 } finally { 531 xmlCursor.dispose(); 532 } 533 } else if (authType.isSetServerAuthModule()) { 534 authConfigProviderData = new GBeanData(providerName, ServerAuthModuleGBean.class); 535 final XmlCursor xmlCursor = authType.getServerAuthModule().newCursor(); 536 try { 537 XMLStreamReader reader = new InternWrapper(xmlCursor.newXMLStreamReader()); 538 AuthModuleType<ServerAuthModule> authModuleType = JaspiXmlUtil.loadServerAuthModule(reader); 539 StringWriter out = new StringWriter(); 540 JaspiXmlUtil.writeServerAuthModule(authModuleType, out); 541 authConfigProviderData.setAttribute("config", out.toString()); 542 authConfigProviderData.setAttribute("messageLayer", "Http"); 543 authConfigProviderData.setAttribute("appContext", contextPath); 544 //TODO ?? 545 authConfigProviderData.setAttribute("authenticationID", contextPath); 546 } finally { 547 xmlCursor.dispose(); 548 } 549 } 550 } catch (ParserConfigurationException e) { 551 throw new DeploymentException("Could not read auth config", e); 552 } catch (IOException e) { 553 throw new DeploymentException("Could not read auth config", e); 554 } catch (SAXException e) { 555 throw new DeploymentException("Could not read auth config", e); 556 } catch (JAXBException e) { 557 throw new DeploymentException("Could not read auth config", e); 558 } catch (XMLStreamException e) { 559 throw new DeploymentException("Could not read auth config", e); 560 } 561 if (authConfigProviderData != null) { 562 moduleContext.addGBean(authConfigProviderData); 563 securityFactoryData.addDependency(authConfigProviderData.getAbstractName()); 564 } 565 } 566 567 protected static void check(WebAppType webApp) throws DeploymentException { 568 checkURLPattern(webApp); 569 checkMultiplicities(webApp); 570 } 571 572 private static void checkURLPattern(WebAppType webApp) throws DeploymentException { 573 574 FilterMappingType[] filterMappings = webApp.getFilterMappingArray(); 575 for (FilterMappingType filterMapping : filterMappings) { 576 UrlPatternType[] urlPatterns = filterMapping.getUrlPatternArray(); 577 for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) { 578 checkString(urlPatterns[j].getStringValue().trim()); 579 } 580 } 581 582 ServletMappingType[] servletMappings = webApp.getServletMappingArray(); 583 for (ServletMappingType servletMapping : servletMappings) { 584 UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray(); 585 for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) { 586 checkString(urlPatterns[j].getStringValue().trim()); 587 } 588 } 589 590 SecurityConstraintType[] constraints = webApp.getSecurityConstraintArray(); 591 for (SecurityConstraintType constraint : constraints) { 592 WebResourceCollectionType[] collections = constraint.getWebResourceCollectionArray(); 593 for (WebResourceCollectionType collection : collections) { 594 UrlPatternType[] patterns = collection.getUrlPatternArray(); 595 for (UrlPatternType pattern : patterns) { 596 checkString(pattern.getStringValue().trim()); 597 } 598 } 599 } 600 } 601 602 protected static void checkString(String pattern) throws DeploymentException { 603 //j2ee_1_4.xsd explicitly requires preserving all whitespace. Do not trim. 604 if (pattern.indexOf(0x0D) >= 0) throw new DeploymentException("<url-pattern> must not contain CR(#xD)"); 605 if (pattern.indexOf(0x0A) >= 0) throw new DeploymentException("<url-pattern> must not contain LF(#xA)"); 606 } 607 608 private static void checkMultiplicities(WebAppType webApp) throws DeploymentException { 609 if (webApp.getSessionConfigArray().length > 1) 610 throw new DeploymentException("Multiple <session-config> elements found"); 611 if (webApp.getJspConfigArray().length > 1) 612 throw new DeploymentException("Multiple <jsp-config> elements found"); 613 if (webApp.getLoginConfigArray().length > 1) 614 throw new DeploymentException("Multiple <login-config> elements found"); 615 } 616 617 private boolean cleanupConfigurationDir(File configurationDir) { 618 LinkedList<String> cannotBeDeletedList = new LinkedList<String>(); 619 620 if (!DeploymentUtil.recursiveDelete(configurationDir, cannotBeDeletedList)) { 621 // Output a message to help user track down file problem 622 log.warn("Unable to delete " + cannotBeDeletedList.size() + 623 " files while recursively deleting directory " 624 + configurationDir.getAbsolutePath() + LINE_SEP + 625 "The first file that could not be deleted was:" + LINE_SEP + " " + 626 (!cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : "")); 627 return false; 628 } 629 return true; 630 } 631 632 633 protected ClassFinder createWebAppClassFinder(WebAppType webApp, WebModule webModule) throws DeploymentException { 634 // Get the classloader from the module's EARContext 635 ClassLoader classLoader = webModule.getEarContext().getClassLoader(); 636 return createWebAppClassFinder(webApp, classLoader); 637 } 638 639 public static ClassFinder createWebAppClassFinder(WebAppType webApp, ClassLoader classLoader) throws DeploymentException { 640 //------------------------------------------------------------------------------------ 641 // Find the list of classes from the web.xml we want to search for annotations in 642 //------------------------------------------------------------------------------------ 643 List<Class> classes = new ArrayList<Class>(); 644 645 // Get all the servlets from the deployment descriptor 646 ServletType[] servlets = webApp.getServletArray(); 647 for (ServletType servlet : servlets) { 648 FullyQualifiedClassType cls = servlet.getServletClass(); 649 if (cls != null) { // Don't try this for JSPs 650 Class<?> clas; 651 try { 652 clas = classLoader.loadClass(cls.getStringValue()); 653 } catch (ClassNotFoundException e) { 654 throw new DeploymentException("AbstractWebModuleBuilder: Could not load servlet class: " + cls.getStringValue(), e); 655 } 656 addClass(classes, clas); 657 } 658 } 659 660 // Get all the listeners from the deployment descriptor 661 ListenerType[] listeners = webApp.getListenerArray(); 662 for (ListenerType listener : listeners) { 663 FullyQualifiedClassType cls = listener.getListenerClass(); 664 Class<?> clas; 665 try { 666 clas = classLoader.loadClass(cls.getStringValue()); 667 } catch (ClassNotFoundException e) { 668 throw new DeploymentException("AbstractWebModuleBuilder: Could not load listener class: " + cls.getStringValue(), e); 669 } 670 addClass(classes, clas); 671 } 672 673 // Get all the filters from the deployment descriptor 674 FilterType[] filters = webApp.getFilterArray(); 675 for (FilterType filter : filters) { 676 FullyQualifiedClassType cls = filter.getFilterClass(); 677 Class<?> clas; 678 try { 679 clas = classLoader.loadClass(cls.getStringValue()); 680 } catch (ClassNotFoundException e) { 681 throw new DeploymentException("AbstractWebModuleBuilder: Could not load filter class: " + cls.getStringValue(), e); 682 } 683 addClass(classes, clas); 684 } 685 686 // see https://issues.apache.org/jira/browse/GERONIMO-3421 . 687 // if the user has botched her classloader config (perhaps by 688 // not including a jar that her app needs) then ClassFinder 689 // will throw NoClassDefFoundError. we want to indicate that 690 // it's the user's error and provide a little context to help 691 // her fix it. 692 try { 693 return new ClassFinder(classes); 694 } catch (NoClassDefFoundError e) { 695 throw new DeploymentException("Classloader for " + webApp.getId() + "can't find " + e.getMessage(), e); 696 } 697 } 698 699 private static void addClass(List<Class> classes, Class<?> clas) { 700 while (clas != Object.class) { 701 classes.add(clas); 702 clas = clas.getSuperclass(); 703 } 704 } 705 706 protected void configureBasicWebModuleAttributes(WebAppType webApp, XmlObject vendorPlan, EARContext moduleContext, EARContext earContext, WebModule webModule, GBeanData webModuleData) throws DeploymentException { 707 Map<NamingBuilder.Key, Object> buildingContext = new HashMap<NamingBuilder.Key, Object>(); 708 buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, moduleContext.getModuleName()); 709 710 if (!webApp.getMetadataComplete()) { 711 // Create a classfinder and populate it for the naming builder(s). The absence of a 712 // classFinder in the module will convey whether metadata-complete is set (or not) 713 webModule.setClassFinder(createWebAppClassFinder(webApp, webModule)); 714 SecurityAnnotationHelper.processAnnotations(webApp, webModule.getClassFinder()); 715 } 716 //N.B. we use the ear context which has all the gbeans we could possibly be looking up from this ear. 717 //nope, persistence units can be in the war. 718 //This means that you cannot use the default environment of the web builder to add configs that will be searched. 719 getNamingBuilders().buildNaming(webApp, vendorPlan, webModule, buildingContext); 720 721 Map compContext = NamingBuilder.JNDI_KEY.get(buildingContext); 722 Holder holder = NamingBuilder.INJECTION_KEY.get(buildingContext); 723 724 webModule.getSharedContext().put(WebModule.WEB_APP_DATA, webModuleData); 725 webModule.getSharedContext().put(NamingBuilder.JNDI_KEY, compContext); 726 webModule.getSharedContext().put(NamingBuilder.INJECTION_KEY, holder); 727 if (moduleContext.getServerName() != null) { 728 webModuleData.setReferencePattern("J2EEServer", moduleContext.getServerName()); 729 } 730 if (!webModule.isStandAlone()) { 731 webModuleData.setReferencePattern("J2EEApplication", earContext.getModuleName()); 732 } 733 734 webModuleData.setAttribute("holder", holder); 735 736 //Add dependencies on managed connection factories and ejbs in this app 737 //This is overkill, but allows for people not using java:comp context (even though we don't support it) 738 //and sidesteps the problem of circular references between ejbs. 739 if (earContext != moduleContext) { 740 addGBeanDependencies(earContext, webModuleData); 741 } 742 743 webModuleData.setAttribute("componentContext", compContext); 744 webModuleData.setReferencePattern("TransactionManager", moduleContext.getTransactionManagerName()); 745 webModuleData.setReferencePattern("TrackedConnectionAssociator", moduleContext.getConnectionTrackerName()); 746 } 747 748 private static class InternWrapper implements XMLStreamReader { 749 private final XMLStreamReader delegate; 750 751 private InternWrapper(XMLStreamReader delegate) { 752 this.delegate = delegate; 753 } 754 755 public void close() throws XMLStreamException { 756 delegate.close(); 757 } 758 759 public int getAttributeCount() { 760 return delegate.getAttributeCount(); 761 } 762 763 public String getAttributeLocalName(int i) { 764 return delegate.getAttributeLocalName(i); 765 } 766 767 public QName getAttributeName(int i) { 768 return delegate.getAttributeName(i); 769 } 770 771 public String getAttributeNamespace(int i) { 772 return delegate.getAttributeNamespace(i); 773 } 774 775 public String getAttributePrefix(int i) { 776 return delegate.getAttributePrefix(i); 777 } 778 779 public String getAttributeType(int i) { 780 return delegate.getAttributeType(i); 781 } 782 783 public String getAttributeValue(int i) { 784 return delegate.getAttributeValue(i); 785 } 786 787 public String getAttributeValue(String s, String s1) { 788 return delegate.getAttributeValue(s, s1); 789 } 790 791 public String getCharacterEncodingScheme() { 792 return delegate.getCharacterEncodingScheme(); 793 } 794 795 public String getElementText() throws XMLStreamException { 796 return delegate.getElementText(); 797 } 798 799 public String getEncoding() { 800 return delegate.getEncoding(); 801 } 802 803 public int getEventType() { 804 return delegate.getEventType(); 805 } 806 807 public String getLocalName() { 808 return delegate.getLocalName().intern(); 809 } 810 811 public Location getLocation() { 812 return delegate.getLocation(); 813 } 814 815 public QName getName() { 816 return delegate.getName(); 817 } 818 819 public NamespaceContext getNamespaceContext() { 820 return delegate.getNamespaceContext(); 821 } 822 823 public int getNamespaceCount() { 824 return delegate.getNamespaceCount(); 825 } 826 827 public String getNamespacePrefix(int i) { 828 return delegate.getNamespacePrefix(i); 829 } 830 831 public String getNamespaceURI() { 832 return delegate.getNamespaceURI().intern(); 833 } 834 835 public String getNamespaceURI(int i) { 836 return delegate.getNamespaceURI(i); 837 } 838 839 public String getNamespaceURI(String s) { 840 return delegate.getNamespaceURI(s); 841 } 842 843 public String getPIData() { 844 return delegate.getPIData(); 845 } 846 847 public String getPITarget() { 848 return delegate.getPITarget(); 849 } 850 851 public String getPrefix() { 852 return delegate.getPrefix(); 853 } 854 855 public Object getProperty(String s) throws IllegalArgumentException { 856 return delegate.getProperty(s); 857 } 858 859 public String getText() { 860 return delegate.getText(); 861 } 862 863 public char[] getTextCharacters() { 864 return delegate.getTextCharacters(); 865 } 866 867 public int getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException { 868 return delegate.getTextCharacters(i, chars, i1, i2); 869 } 870 871 public int getTextLength() { 872 return delegate.getTextLength(); 873 } 874 875 public int getTextStart() { 876 return delegate.getTextStart(); 877 } 878 879 public String getVersion() { 880 return delegate.getVersion(); 881 } 882 883 public boolean hasName() { 884 return delegate.hasName(); 885 } 886 887 public boolean hasNext() throws XMLStreamException { 888 return delegate.hasNext(); 889 } 890 891 public boolean hasText() { 892 return delegate.hasText(); 893 } 894 895 public boolean isAttributeSpecified(int i) { 896 return delegate.isAttributeSpecified(i); 897 } 898 899 public boolean isCharacters() { 900 return delegate.isCharacters(); 901 } 902 903 public boolean isEndElement() { 904 return delegate.isEndElement(); 905 } 906 907 public boolean isStandalone() { 908 return delegate.isStandalone(); 909 } 910 911 public boolean isStartElement() { 912 return delegate.isStartElement(); 913 } 914 915 public boolean isWhiteSpace() { 916 return delegate.isWhiteSpace(); 917 } 918 919 public int next() throws XMLStreamException { 920 return delegate.next(); 921 } 922 923 public int nextTag() throws XMLStreamException { 924 return delegate.nextTag(); 925 } 926 927 public void require(int i, String s, String s1) throws XMLStreamException { 928 delegate.require(i, s, s1); 929 } 930 931 public boolean standaloneSet() { 932 return delegate.standaloneSet(); 933 } 934 } 935 936 }