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: 558910 $ $Date: 2007-07-23 21:41:35 -0400 (Mon, 23 Jul 2007) $
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                while (entries.hasMoreElements()) {
285                    ZipEntry entry = entries.nextElement();
286                    URI targetPath = new URI(null, entry.getName(), null);
287                    if (entry.getName().equals("WEB-INF/web.xml")) {
288                        moduleContext.addFile(targetPath, module.getOriginalSpecDD());
289                    } else if (entry.getName().startsWith("WEB-INF/lib") && entry.getName().endsWith(".jar")) {
290                        moduleContext.addInclude(targetPath, warFile, entry);
291                        manifestcp.add(entry.getName());
292                    } else {
293                        moduleContext.addFile(targetPath, warFile, entry);
294                    }
295                }
296    
297                //always add WEB-INF/classes to the classpath regardless of whether
298                //any classes exist
299                moduleContext.getConfiguration().addToClassPath("WEB-INF/classes/");
300                manifestcp.add("WEB-INF/classes/");
301                // add the manifest classpath entries declared in the war to the class loader
302                // we have to explicitly add these since we are unpacking the web module
303                // and the url class loader will not pick up a manifest from an unpacked dir
304                moduleContext.addManifestClassPath(warFile, RELATIVE_MODULE_BASE_URI);
305                moduleContext.getGeneralData().put(ClassPathList.class, manifestcp);
306    
307            } catch (IOException e) {
308                throw new DeploymentException("Problem deploying war", e);
309            } catch (URISyntaxException e) {
310                throw new DeploymentException("Could not construct URI for location of war entry", e);
311            } finally {
312                if (!module.isStandAlone()) {
313                    try {
314                        moduleContext.flush();
315                    } catch (IOException e) {
316                        throw new DeploymentException("Problem closing war context", e);
317                    }
318                }
319            }
320            for (ModuleBuilderExtension mbe: moduleBuilderExtensions) {
321                mbe.installModule(earFile, earContext, module, configurationStores, targetConfigurationStore, repositories);
322            }
323        }
324    
325        protected void basicInitContext(EARContext earContext, Module module, XmlObject gerWebApp, boolean hasSecurityRealmName) throws DeploymentException {
326            //complete manifest classpath
327            EARContext moduleContext = module.getEarContext();
328            ClassPathList manifestcp = (ClassPathList) moduleContext.getGeneralData().get(ClassPathList.class);
329            ModuleList moduleLocations = (ModuleList) module.getRootEarContext().getGeneralData().get(ModuleList.class);
330            URI baseUri = URI.create(module.getTargetPath());
331            URI resolutionUri = invertURI(baseUri);
332            earContext.getCompleteManifestClassPath(module.getModuleFile(), baseUri, resolutionUri, manifestcp, moduleLocations);
333    
334    
335            WebAppType webApp = (WebAppType) module.getSpecDD();
336            if ((webApp.getSecurityConstraintArray().length > 0 || webApp.getSecurityRoleArray().length > 0) &&
337                    !hasSecurityRealmName) {
338                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.");
339            }
340            XmlObject[] securityElements = XmlBeansUtil.selectSubstitutionGroupElements(SECURITY_QNAME, gerWebApp);
341            if (securityElements.length > 0 && !hasSecurityRealmName) {
342                throw new DeploymentException("You have supplied a security configuration for web app " + module.getName() + " but no security-realm-name to allow login");
343            }
344            getNamingBuilders().buildEnvironment(webApp, module.getVendorDD(), module.getEnvironment());
345            //this is silly
346            getNamingBuilders().initContext(webApp, gerWebApp, module);
347    
348            Map servletNameToPathMap = buildServletNameToPathMap((WebAppType) module.getSpecDD(), ((WebModule) module).getContextRoot());
349    
350            Map sharedContext = module.getSharedContext();
351            for (Object aWebServiceBuilder : webServiceBuilder) {
352                WebServiceBuilder serviceBuilder = (WebServiceBuilder) aWebServiceBuilder;
353                serviceBuilder.findWebServices(module, false, servletNameToPathMap, module.getEnvironment(), sharedContext);
354            }
355            securityBuilders.build(gerWebApp, earContext, module.getEarContext());
356            serviceBuilders.build(gerWebApp, earContext, module.getEarContext());
357        }
358    
359        static URI invertURI(URI baseUri) {
360            URI resolutionUri = URI.create(".");
361            for (URI test = baseUri; !test.equals(RELATIVE_MODULE_BASE_URI); test = test.resolve(RELATIVE_MODULE_BASE_URI)) {
362                resolutionUri = resolutionUri.resolve(RELATIVE_MODULE_BASE_URI);
363            }
364            return resolutionUri;
365        }
366    
367        protected WebAppDocument convertToServletSchema(XmlObject xmlObject) throws XmlException {
368    
369            String schemaLocationURL = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd";
370            String version = "2.5";
371            XmlCursor cursor = xmlObject.newCursor();
372            try {
373                cursor.toStartDoc();
374                cursor.toFirstChild();
375                if ("http://java.sun.com/xml/ns/j2ee".equals(cursor.getName().getNamespaceURI())) {
376                    SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
377                    XmlObject result = xmlObject.changeType(WebAppDocument.type);
378                    XmlBeansUtil.validateDD(result);
379                    return (WebAppDocument) result;
380                }
381    
382                if ("http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI())) {
383                    SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
384                    XmlObject result = xmlObject.changeType(WebAppDocument.type);
385                    XmlBeansUtil.validateDD(result);
386                    return (WebAppDocument) result;
387                }
388    
389                //otherwise assume DTD
390                XmlDocumentProperties xmlDocumentProperties = cursor.documentProperties();
391                String publicId = xmlDocumentProperties.getDoctypePublicId();
392                boolean is22 = "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN".equals(publicId);
393                if ("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN".equals(publicId) ||
394                        is22) {
395                    XmlCursor moveable = xmlObject.newCursor();
396                    try {
397                        moveable.toStartDoc();
398                        moveable.toFirstChild();
399    
400                        SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
401                        cursor.toStartDoc();
402                        cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "web-app");
403                        cursor.toFirstChild();
404                        SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
405                        SchemaConversionUtils.convertToJNDIEnvironmentRefsGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
406                        cursor.push();
407                        if (cursor.toNextSibling(TAGLIB)) {
408                            cursor.toPrevSibling();
409                            moveable.toCursor(cursor);
410                            cursor.beginElement("jsp-config", SchemaConversionUtils.JAVAEE_NAMESPACE);
411                            while (moveable.toNextSibling(TAGLIB)) {
412                                moveable.moveXml(cursor);
413                            }
414                        }
415                        cursor.pop();
416                        do {
417                            String name = cursor.getName().getLocalPart();
418                            if ("filter".equals(name) || "servlet".equals(name) || "context-param".equals(name)) {
419                                cursor.push();
420                                cursor.toFirstChild();
421                                SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
422                                while (cursor.toNextSibling(SchemaConversionUtils.JAVAEE_NAMESPACE, "init-param")) {
423                                    cursor.push();
424                                    cursor.toFirstChild();
425                                    SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
426                                    cursor.pop();
427                                }
428                                cursor.pop();
429                                cursor.push();
430                                if (cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "jsp-file")) {
431                                    String jspFile = cursor.getTextValue();
432                                    if (!jspFile.startsWith("/")){
433                                        if (is22) {
434                                            cursor.setTextValue("/" + jspFile);
435                                        } else {
436                                            throw new XmlException("jsp-file does not start with / and this is not a 2.2 web app: " + jspFile);
437                                        }
438                                    }
439                                }
440                                cursor.pop();
441                            }
442                        } while (cursor.toNextSibling());
443                    } finally {
444                        moveable.dispose();
445                    }
446                }
447            } finally {
448                cursor.dispose();
449            }
450            XmlObject result = xmlObject.changeType(WebAppDocument.type);
451            if (result != null) {
452                XmlBeansUtil.validateDD(result);
453                return (WebAppDocument) result;
454            }
455            XmlBeansUtil.validateDD(xmlObject);
456            return (WebAppDocument) xmlObject;
457        }
458    
459    
460        protected void addUnmappedJSPPermissions(Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) {
461            for (String roleName : securityRoles) {
462                addPermissionToRole(roleName, new WebRoleRefPermission("", roleName), rolePermissions);
463            }
464        }
465    
466        protected ComponentPermissions buildSpecSecurityConfig(WebAppType webApp, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) {
467            Map<String, URLPattern> uncheckedPatterns = new HashMap<String, URLPattern>();
468            Map<UncheckedItem, HTTPMethods> uncheckedResourcePatterns = new HashMap<UncheckedItem, HTTPMethods>();
469            Map<UncheckedItem, HTTPMethods> uncheckedUserPatterns = new HashMap<UncheckedItem, HTTPMethods>();
470            Map<String, URLPattern> excludedPatterns = new HashMap<String, URLPattern>();
471            Map<String, URLPattern> rolesPatterns = new HashMap<String, URLPattern>();
472            Set<URLPattern> allSet = new HashSet<URLPattern>();   // == allMap.values()
473            Map<String, URLPattern> allMap = new HashMap<String, URLPattern>();   //uncheckedPatterns union excludedPatterns union rolesPatterns.
474    
475            SecurityConstraintType[] securityConstraintArray = webApp.getSecurityConstraintArray();
476            for (SecurityConstraintType securityConstraintType : securityConstraintArray) {
477                Map<String, URLPattern> currentPatterns;
478                if (securityConstraintType.isSetAuthConstraint()) {
479                    if (securityConstraintType.getAuthConstraint().getRoleNameArray().length == 0) {
480                        currentPatterns = excludedPatterns;
481                    } else {
482                        currentPatterns = rolesPatterns;
483                    }
484                } else {
485                    currentPatterns = uncheckedPatterns;
486                }
487    
488                String transport = "";
489                if (securityConstraintType.isSetUserDataConstraint()) {
490                    transport = securityConstraintType.getUserDataConstraint().getTransportGuarantee().getStringValue().trim().toUpperCase();
491                }
492    
493                WebResourceCollectionType[] webResourceCollectionTypeArray = securityConstraintType.getWebResourceCollectionArray();
494                for (WebResourceCollectionType webResourceCollectionType : webResourceCollectionTypeArray) {
495                    UrlPatternType[] urlPatternTypeArray = webResourceCollectionType.getUrlPatternArray();
496                    for (UrlPatternType urlPatternType : urlPatternTypeArray) {
497                        String url = urlPatternType.getStringValue().trim();
498                        URLPattern pattern = currentPatterns.get(url);
499                        if (pattern == null) {
500                            pattern = new URLPattern(url);
501                            currentPatterns.put(url, pattern);
502                        }
503    
504                        URLPattern allPattern = allMap.get(url);
505                        if (allPattern == null) {
506                            allPattern = new URLPattern(url);
507                            allSet.add(allPattern);
508                            allMap.put(url, allPattern);
509                        }
510    
511                        String[] httpMethodTypeArray = webResourceCollectionType.getHttpMethodArray();
512                        if (httpMethodTypeArray.length == 0) {
513                            pattern.addMethod("");
514                            allPattern.addMethod("");
515                        } else {
516                            for (String aHttpMethodTypeArray : httpMethodTypeArray) {
517                                String method = (aHttpMethodTypeArray == null ? null : aHttpMethodTypeArray.trim());
518                                if (method != null) {
519                                    pattern.addMethod(method);
520                                    allPattern.addMethod(method);
521                                }
522                            }
523                        }
524                        if (currentPatterns == rolesPatterns) {
525                            RoleNameType[] roleNameTypeArray = securityConstraintType.getAuthConstraint().getRoleNameArray();
526                            for (RoleNameType roleNameType : roleNameTypeArray) {
527                                String role = roleNameType.getStringValue().trim();
528                                if (role.equals("*")) {
529                                    pattern.addAllRoles(securityRoles);
530                                } else {
531                                    pattern.addRole(role);
532                                }
533                            }
534                        }
535    
536                        pattern.setTransport(transport);
537                    }
538                }
539            }
540    
541            PermissionCollection excludedPermissions = new Permissions();
542            PermissionCollection uncheckedPermissions = new Permissions();
543    
544            for (URLPattern pattern : excludedPatterns.values()) {
545                String name = pattern.getQualifiedPattern(allSet);
546                String actions = pattern.getMethods();
547    
548                excludedPermissions.add(new WebResourcePermission(name, actions));
549                excludedPermissions.add(new WebUserDataPermission(name, actions));
550            }
551    
552            for (URLPattern pattern : rolesPatterns.values()) {
553                String name = pattern.getQualifiedPattern(allSet);
554                String actions = pattern.getMethods();
555                WebResourcePermission permission = new WebResourcePermission(name, actions);
556    
557                for (String roleName : pattern.getRoles()) {
558                    addPermissionToRole(roleName, permission, rolePermissions);
559                }
560                HTTPMethods methods = pattern.getHTTPMethods();
561                int transportType = pattern.getTransport();
562    
563                addOrUpdatePattern(uncheckedUserPatterns, name, methods, transportType);
564            }
565    
566            for (URLPattern pattern : uncheckedPatterns.values()) {
567                String name = pattern.getQualifiedPattern(allSet);
568                HTTPMethods methods = pattern.getHTTPMethods();
569    
570                addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA);
571    
572                int transportType = pattern.getTransport();
573                addOrUpdatePattern(uncheckedUserPatterns, name, methods, transportType);
574            }
575    
576            /**
577             * A <code>WebResourcePermission</code> and a <code>WebUserDataPermission</code> must be instantiated for
578             * each <tt>url-pattern</tt> in the deployment descriptor and the default pattern "/", that is not combined
579             * by the <tt>web-resource-collection</tt> elements of the deployment descriptor with ever HTTP method
580             * value.  The permission objects must be contructed using the qualified pattern as their name and with
581             * actions defined by the subset of the HTTP methods that do not occur in combination with the pattern.
582             * The resulting permissions that must be added to the unchecked policy statements by calling the
583             * <code>addToUncheckedPolcy</code> method on the <code>PolicyConfiguration</code> object.
584             */
585            for (URLPattern pattern : allSet) {
586                String name = pattern.getQualifiedPattern(allSet);
587                HTTPMethods methods = pattern.getComplementedHTTPMethods();
588    
589                if (methods.isNone()) {
590                    continue;
591                }
592    
593                addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA);
594                addOrUpdatePattern(uncheckedUserPatterns, name, methods, URLPattern.NA);
595            }
596    
597            URLPattern pattern = new URLPattern("/");
598            if (!allSet.contains(pattern)) {
599                String name = pattern.getQualifiedPattern(allSet);
600                HTTPMethods methods = pattern.getComplementedHTTPMethods();
601    
602                addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA);
603                addOrUpdatePattern(uncheckedUserPatterns, name, methods, URLPattern.NA);
604            }
605    
606            //Create the uncheckedPermissions for WebResourcePermissions
607            for (UncheckedItem item : uncheckedResourcePatterns.keySet()) {
608                HTTPMethods methods = uncheckedResourcePatterns.get(item);
609                String actions = URLPattern.getMethodsWithTransport(methods, item.getTransportType());
610    
611                uncheckedPermissions.add(new WebResourcePermission(item.getName(), actions));
612            }
613            //Create the uncheckedPermissions for WebUserDataPermissions
614            for (UncheckedItem item : uncheckedUserPatterns.keySet()) {
615                HTTPMethods methods = uncheckedUserPatterns.get(item);
616                String actions = URLPattern.getMethodsWithTransport(methods, item.getTransportType());
617    
618                uncheckedPermissions.add(new WebUserDataPermission(item.getName(), actions));
619            }
620    
621            return new ComponentPermissions(excludedPermissions, uncheckedPermissions, rolePermissions);
622    
623        }
624    
625        protected void addPermissionToRole(String roleName, Permission permission, Map<String, PermissionCollection> rolePermissions) {
626            PermissionCollection permissionsForRole = rolePermissions.get(roleName);
627            if (permissionsForRole == null) {
628                permissionsForRole = new Permissions();
629                rolePermissions.put(roleName, permissionsForRole);
630            }
631            permissionsForRole.add(permission);
632        }
633    
634        private void addOrUpdatePattern(Map<UncheckedItem, HTTPMethods> patternMap, String name, HTTPMethods actions, int transportType) {
635            UncheckedItem item = new UncheckedItem(name, transportType);
636            HTTPMethods existingActions = patternMap.get(item);
637            if (existingActions != null) {
638                patternMap.put(item, existingActions.add(actions));
639                return;
640            }
641    
642            patternMap.put(item, new HTTPMethods(actions, false));
643        }
644    
645        protected static Set<String> collectRoleNames(WebAppType webApp) {
646            Set<String> roleNames = new HashSet<String>();
647    
648            SecurityRoleType[] securityRoles = webApp.getSecurityRoleArray();
649            for (SecurityRoleType securityRole : securityRoles) {
650                roleNames.add(securityRole.getRoleName().getStringValue().trim());
651            }
652    
653            return roleNames;
654        }
655    
656        protected static void check(WebAppType webApp) throws DeploymentException {
657            checkURLPattern(webApp);
658            checkMultiplicities(webApp);
659        }
660    
661        private static void checkURLPattern(WebAppType webApp) throws DeploymentException {
662    
663            FilterMappingType[] filterMappings = webApp.getFilterMappingArray();
664            for (FilterMappingType filterMapping : filterMappings) {
665                UrlPatternType[] urlPatterns = filterMapping.getUrlPatternArray();
666                for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) {
667                    checkString(urlPatterns[j].getStringValue().trim());
668                }
669            }
670    
671            ServletMappingType[] servletMappings = webApp.getServletMappingArray();
672            for (ServletMappingType servletMapping : servletMappings) {
673                UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray();
674                for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) {
675                    checkString(urlPatterns[j].getStringValue().trim());
676                }
677            }
678    
679            SecurityConstraintType[] constraints = webApp.getSecurityConstraintArray();
680            for (SecurityConstraintType constraint : constraints) {
681                WebResourceCollectionType[] collections = constraint.getWebResourceCollectionArray();
682                for (WebResourceCollectionType collection : collections) {
683                    UrlPatternType[] patterns = collection.getUrlPatternArray();
684                    for (UrlPatternType pattern : patterns) {
685                        checkString(pattern.getStringValue().trim());
686                    }
687                }
688            }
689        }
690    
691        protected static void checkString(String pattern) throws DeploymentException {
692            //j2ee_1_4.xsd explicitly requires preserving all whitespace. Do not trim.
693            if (pattern.indexOf(0x0D) >= 0) throw new DeploymentException("<url-pattern> must not contain CR(#xD)");
694            if (pattern.indexOf(0x0A) >= 0) throw new DeploymentException("<url-pattern> must not contain LF(#xA)");
695        }
696    
697        private static void checkMultiplicities(WebAppType webApp) throws DeploymentException {
698            if (webApp.getSessionConfigArray().length > 1)
699                throw new DeploymentException("Multiple <session-config> elements found");
700            if (webApp.getJspConfigArray().length > 1)
701                throw new DeploymentException("Multiple <jsp-config> elements found");
702            if (webApp.getLoginConfigArray().length > 1)
703                throw new DeploymentException("Multiple <login-config> elements found");
704        }
705    
706        private boolean cleanupConfigurationDir(File configurationDir) {
707            LinkedList<String> cannotBeDeletedList = new LinkedList<String>();
708    
709            if (!DeploymentUtil.recursiveDelete(configurationDir, cannotBeDeletedList)) {
710                // Output a message to help user track down file problem
711                log.warn("Unable to delete " + cannotBeDeletedList.size() +
712                        " files while recursively deleting directory "
713                        + configurationDir.getAbsolutePath() + LINE_SEP +
714                        "The first file that could not be deleted was:" + LINE_SEP + "  " +
715                        (!cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : ""));
716                return false;
717            }
718            return true;
719        }
720    
721        protected void processRoleRefPermissions(ServletType servletType, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) {
722            String servletName = servletType.getServletName().getStringValue().trim();
723            //WebRoleRefPermissions
724            SecurityRoleRefType[] securityRoleRefTypeArray = servletType.getSecurityRoleRefArray();
725            Set<String> unmappedRoles = new HashSet<String>(securityRoles);
726            for (SecurityRoleRefType securityRoleRefType : securityRoleRefTypeArray) {
727                String roleName = securityRoleRefType.getRoleName().getStringValue().trim();
728                String roleLink = securityRoleRefType.getRoleLink().getStringValue().trim();
729                //jacc 3.1.3.2
730                /*   The name of the WebRoleRefPermission must be the servlet-name in whose
731                * context the security-role-ref is defined. The actions of the  WebRoleRefPermission
732                * must be the value of the role-name (that is the  reference), appearing in the security-role-ref.
733                * The deployment tools must  call the addToRole method on the PolicyConfiguration object to add the
734                * WebRoleRefPermission object resulting from the translation to the role
735                * identified in the role-link appearing in the security-role-ref.
736                */
737                addPermissionToRole(roleLink, new WebRoleRefPermission(servletName, roleName), rolePermissions);
738                unmappedRoles.remove(roleName);
739            }
740            for (String roleName : unmappedRoles) {
741                addPermissionToRole(roleName, new WebRoleRefPermission(servletName, roleName), rolePermissions);
742            }
743        }
744    
745        protected ClassFinder createWebAppClassFinder(WebAppType webApp, WebModule webModule) throws DeploymentException {
746    
747            //------------------------------------------------------------------------------------
748            // Find the list of classes from the web.xml we want to search for annotations in
749            //------------------------------------------------------------------------------------
750            List<Class> classes = new ArrayList<Class>();
751    
752            // Get the classloader from the module's EARContext
753            ClassLoader classLoader = webModule.getEarContext().getClassLoader();
754    
755            // Get all the servlets from the deployment descriptor
756            ServletType[] servlets = webApp.getServletArray();
757            for (ServletType servlet : servlets) {
758                FullyQualifiedClassType cls = servlet.getServletClass();
759                if (cls != null) {                              // Don't try this for JSPs
760                    Class<?> clas;
761                    try {
762                        clas = classLoader.loadClass(cls.getStringValue());
763                    } catch (ClassNotFoundException e) {
764                        throw new DeploymentException("AbstractWebModuleBuilder: Could not load servlet class: " + cls.getStringValue(), e);
765                    }
766                    addClass(classes, clas);
767                }
768            }
769    
770            // Get all the listeners from the deployment descriptor
771            ListenerType[] listeners = webApp.getListenerArray();
772            for (ListenerType listener : listeners) {
773                FullyQualifiedClassType cls = listener.getListenerClass();
774                Class<?> clas;
775                try {
776                    clas = classLoader.loadClass(cls.getStringValue());
777                } catch (ClassNotFoundException e) {
778                    throw new DeploymentException("AbstractWebModuleBuilder: Could not load listener class: " + cls.getStringValue(), e);
779                }
780                addClass(classes, clas);
781            }
782    
783            // Get all the filters from the deployment descriptor
784            FilterType[] filters = webApp.getFilterArray();
785            for (FilterType filter : filters) {
786                FullyQualifiedClassType cls = filter.getFilterClass();
787                Class<?> clas;
788                try {
789                    clas = classLoader.loadClass(cls.getStringValue());
790                } catch (ClassNotFoundException e) {
791                    throw new DeploymentException("AbstractWebModuleBuilder: Could not load filter class: " + cls.getStringValue(), e);
792                }
793                addClass(classes, clas);
794            }
795    
796            return new ClassFinder(classes);
797        }
798    
799        private void addClass(List<Class> classes, Class<?> clas) {
800            while (clas != Object.class) {
801                classes.add(clas);
802                clas = clas.getSuperclass();
803            }
804        }
805    
806        protected void configureBasicWebModuleAttributes(WebAppType webApp, XmlObject vendorPlan, EARContext moduleContext, EARContext earContext, WebModule webModule, GBeanData webModuleData) throws DeploymentException {
807            Map<NamingBuilder.Key, Object> buildingContext = new HashMap<NamingBuilder.Key, Object>();
808            buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, moduleContext.getModuleName());
809    
810            if (!webApp.getMetadataComplete()) {
811                // Create a classfinder and populate it for the naming builder(s). The absence of a
812                // classFinder in the module will convey whether metadata-complete is set (or not)
813                webModule.setClassFinder(createWebAppClassFinder(webApp, webModule));
814                SecurityAnnotationHelper.processAnnotations(webApp, webModule.getClassFinder());
815            }
816            //N.B. we use the ear context which has all the gbeans we could possibly be looking up from this ear.
817            //nope, persistence units can be in the war.
818            //This means that you cannot use the default environment of the web builder to add configs that will be searched.
819            getNamingBuilders().buildNaming(webApp, vendorPlan, webModule, buildingContext);
820    
821            Map compContext = NamingBuilder.JNDI_KEY.get(buildingContext);
822            Holder holder = NamingBuilder.INJECTION_KEY.get(buildingContext);
823    
824            webModule.getSharedContext().put(WebModule.WEB_APP_DATA, webModuleData);
825            webModule.getSharedContext().put(NamingBuilder.JNDI_KEY, compContext);
826            webModule.getSharedContext().put(NamingBuilder.INJECTION_KEY, holder);
827            if (moduleContext.getServerName() != null) {
828                webModuleData.setReferencePattern("J2EEServer", moduleContext.getServerName());
829            }
830            if (!webModule.isStandAlone()) {
831                webModuleData.setReferencePattern("J2EEApplication", earContext.getModuleName());
832            }
833    
834            webModuleData.setAttribute("holder", holder);
835    
836            //Add dependencies on managed connection factories and ejbs in this app
837            //This is overkill, but allows for people not using java:comp context (even though we don't support it)
838            //and sidesteps the problem of circular references between ejbs.
839            if (earContext != moduleContext) {
840                addGBeanDependencies(earContext, webModuleData);
841            }
842    
843            webModuleData.setAttribute("componentContext", compContext);
844            webModuleData.setReferencePattern("TransactionManager", moduleContext.getTransactionManagerName());
845            webModuleData.setReferencePattern("TrackedConnectionAssociator", moduleContext.getConnectionTrackerName());
846        }
847    
848        class UncheckedItem {
849            final static int NA = 0x00;
850            final static int INTEGRAL = 0x01;
851            final static int CONFIDENTIAL = 0x02;
852    
853            private int transportType = NA;
854            private String name;
855    
856            public UncheckedItem(String name, int transportType) {
857                setName(name);
858                setTransportType(transportType);
859            }
860    
861            public boolean equals(Object o) {
862                UncheckedItem item = (UncheckedItem) o;
863                return item.transportType == transportType && item.name.equals(this.name);
864            }
865    
866    
867            public int hashCode() {
868                return name.hashCode() + transportType;
869            }
870    
871            public String getName() {
872                return name;
873            }
874    
875            public void setName(String name) {
876                this.name = name;
877            }
878    
879            public int getTransportType() {
880                return transportType;
881            }
882    
883            public void setTransportType(int transportType) {
884                this.transportType = transportType;
885            }
886        }
887    }