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.jetty6.deployment;
019    
020    import java.io.File;
021    import java.io.FileNotFoundException;
022    import java.io.IOException;
023    import static java.lang.Boolean.FALSE;
024    import static java.lang.Boolean.TRUE;
025    import java.net.URL;
026    import java.security.PermissionCollection;
027    import java.util.ArrayList;
028    import java.util.Arrays;
029    import java.util.Collection;
030    import java.util.Collections;
031    import java.util.Comparator;
032    import java.util.HashMap;
033    import java.util.HashSet;
034    import java.util.List;
035    import java.util.Map;
036    import java.util.Set;
037    import java.util.TreeSet;
038    import java.util.jar.JarFile;
039    
040    import javax.management.ObjectName;
041    import javax.servlet.Servlet;
042    
043    import org.apache.commons.logging.Log;
044    import org.apache.commons.logging.LogFactory;
045    import org.apache.geronimo.common.DeploymentException;
046    import org.apache.geronimo.deployment.ModuleIDBuilder;
047    import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
048    import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
049    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
050    import org.apache.geronimo.deployment.util.DeploymentUtil;
051    import org.apache.geronimo.deployment.xbeans.EnvironmentType;
052    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
053    import org.apache.geronimo.gbean.AbstractName;
054    import org.apache.geronimo.gbean.AbstractNameQuery;
055    import org.apache.geronimo.gbean.GBeanData;
056    import org.apache.geronimo.gbean.GBeanInfo;
057    import org.apache.geronimo.gbean.GBeanInfoBuilder;
058    import org.apache.geronimo.gbean.GBeanLifecycle;
059    import org.apache.geronimo.j2ee.deployment.EARContext;
060    import org.apache.geronimo.j2ee.deployment.Module;
061    import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
062    import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
063    import org.apache.geronimo.j2ee.deployment.NamingBuilder;
064    import org.apache.geronimo.j2ee.deployment.WebModule;
065    import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
066    import org.apache.geronimo.j2ee.deployment.annotation.AnnotatedWebApp;
067    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
068    import org.apache.geronimo.jetty6.Host;
069    import org.apache.geronimo.jetty6.JettyDefaultServletHolder;
070    import org.apache.geronimo.jetty6.JettyFilterHolder;
071    import org.apache.geronimo.jetty6.JettyFilterMapping;
072    import org.apache.geronimo.jetty6.JettyServletHolder;
073    import org.apache.geronimo.jetty6.JettyWebAppContext;
074    import org.apache.geronimo.jetty6.NonAuthenticator;
075    import org.apache.geronimo.jetty6.JettyJspServletHolder;
076    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
077    import org.apache.geronimo.kernel.GBeanNotFoundException;
078    import org.apache.geronimo.kernel.Kernel;
079    import org.apache.geronimo.kernel.Naming;
080    import org.apache.geronimo.kernel.config.ConfigurationData;
081    import org.apache.geronimo.kernel.repository.Environment;
082    import org.apache.geronimo.naming.deployment.ENCConfigBuilder;
083    import org.apache.geronimo.naming.deployment.GBeanResourceEnvironmentBuilder;
084    import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter;
085    import org.apache.geronimo.security.jacc.ComponentPermissions;
086    import org.apache.geronimo.web.deployment.GenericToSpecificPlanConverter;
087    import org.apache.geronimo.web25.deployment.AbstractWebModuleBuilder;
088    import org.apache.geronimo.xbeans.geronimo.j2ee.GerClusteringDocument;
089    import org.apache.geronimo.xbeans.geronimo.web.jetty.JettyWebAppDocument;
090    import org.apache.geronimo.xbeans.geronimo.web.jetty.JettyWebAppType;
091    import org.apache.geronimo.xbeans.geronimo.web.jetty.config.GerJettyDocument;
092    import org.apache.geronimo.xbeans.javaee.DispatcherType;
093    import org.apache.geronimo.xbeans.javaee.ErrorPageType;
094    import org.apache.geronimo.xbeans.javaee.FilterMappingType;
095    import org.apache.geronimo.xbeans.javaee.FilterType;
096    import org.apache.geronimo.xbeans.javaee.FormLoginConfigType;
097    import org.apache.geronimo.xbeans.javaee.JspConfigType;
098    import org.apache.geronimo.xbeans.javaee.ListenerType;
099    import org.apache.geronimo.xbeans.javaee.LocaleEncodingMappingListType;
100    import org.apache.geronimo.xbeans.javaee.LocaleEncodingMappingType;
101    import org.apache.geronimo.xbeans.javaee.LoginConfigType;
102    import org.apache.geronimo.xbeans.javaee.MimeMappingType;
103    import org.apache.geronimo.xbeans.javaee.ParamValueType;
104    import org.apache.geronimo.xbeans.javaee.ServletMappingType;
105    import org.apache.geronimo.xbeans.javaee.ServletType;
106    import org.apache.geronimo.xbeans.javaee.TaglibType;
107    import org.apache.geronimo.xbeans.javaee.UrlPatternType;
108    import org.apache.geronimo.xbeans.javaee.WebAppDocument;
109    import org.apache.geronimo.xbeans.javaee.WebAppType;
110    import org.apache.geronimo.xbeans.javaee.WelcomeFileListType;
111    import org.apache.geronimo.xbeans.javaee.JspPropertyGroupType;
112    import org.apache.xmlbeans.XmlException;
113    import org.apache.xmlbeans.XmlObject;
114    import org.mortbay.jetty.security.BasicAuthenticator;
115    import org.mortbay.jetty.security.ClientCertAuthenticator;
116    import org.mortbay.jetty.security.DigestAuthenticator;
117    import org.mortbay.jetty.security.FormAuthenticator;
118    
119    
120    /**
121     * @version $Rev:385659 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
122     */
123    public class JettyModuleBuilder extends AbstractWebModuleBuilder implements GBeanLifecycle {
124        private final static Log log = LogFactory.getLog(JettyModuleBuilder.class);
125        private static final Map<String, String> NAMESPACE_UPDATES = new HashMap<String, String>();
126        static {
127            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
128            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web-1.1", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
129            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web-1.2", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
130            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web-2.0", "http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1");
131            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web/jetty", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.1");
132            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-1.1", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
133            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-1.2", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
134            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
135            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.1", "http://geronimo.apache.org/xml/ns/j2ee/web/jetty-2.0.2");
136            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web/jetty/config", "http://geronimo.apache.org/xml/ns/web/jetty/config-1.0.1");
137            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/web/jetty/config-1.0", "http://geronimo.apache.org/xml/ns/web/jetty/config-1.0.1");
138        }
139    
140        private final Environment defaultEnvironment;
141        private final AbstractNameQuery jettyContainerObjectName;
142        private final JettyJspServletHolder jspServlet;
143        private final Collection defaultServlets;
144        private final Collection defaultFilters;
145        private final Collection defaultFilterMappings;
146        private final GBeanData pojoWebServiceTemplate;
147    
148        protected final NamespaceDrivenBuilderCollection clusteringBuilders;
149    
150        private final List<String> defaultWelcomeFiles;
151        private final Integer defaultSessionTimeoutSeconds;
152        private final Map<String, String> defaultLocaleEncodingMappings;
153        private final Map<String, String> defaultMimeTypeMappings;
154    
155        private static final String JETTY_NAMESPACE = JettyWebAppDocument.type.getDocumentElementName().getNamespaceURI();
156    
157        public JettyModuleBuilder(Environment defaultEnvironment,
158                                  Integer defaultSessionTimeoutSeconds,
159                                  List<String> defaultWelcomeFiles,
160                                  AbstractNameQuery jettyContainerName,
161                                  JettyJspServletHolder jspServlet,
162                                  Collection defaultServlets,
163                                  Collection defaultFilters,
164                                  Collection defaultFilterMappings,
165                                  Map<String, String> defaultLocaleEncodingMappings,
166                                  Map<String, String> defaultMimeTypeMappings,
167                                  Object pojoWebServiceTemplate,
168                                  Collection<WebServiceBuilder> webServiceBuilder,
169                                  Collection clusteringBuilders,
170                                  Collection securityBuilders,
171                                  Collection serviceBuilders,
172                                  NamingBuilder namingBuilders,
173                                  Collection<ModuleBuilderExtension> moduleBuilderExtensions,
174                                  ResourceEnvironmentSetter resourceEnvironmentSetter,
175                                  Kernel kernel) throws GBeanNotFoundException {
176            super(kernel, securityBuilders, serviceBuilders, namingBuilders, resourceEnvironmentSetter, webServiceBuilder, moduleBuilderExtensions);
177            this.defaultEnvironment = defaultEnvironment;
178            this.defaultSessionTimeoutSeconds = (defaultSessionTimeoutSeconds == null) ? 30 * 60 : defaultSessionTimeoutSeconds;
179            this.jettyContainerObjectName = jettyContainerName;
180            this.jspServlet = jspServlet;
181            this.defaultServlets = defaultServlets;
182            this.defaultFilters = defaultFilters;
183            this.defaultFilterMappings = defaultFilterMappings;
184            this.pojoWebServiceTemplate = getGBeanData(kernel, pojoWebServiceTemplate);
185            this.clusteringBuilders = new NamespaceDrivenBuilderCollection(clusteringBuilders, GerClusteringDocument.type.getDocumentElementName());
186    
187            this.defaultWelcomeFiles = defaultWelcomeFiles == null ? new ArrayList<String>() : defaultWelcomeFiles;
188            this.defaultLocaleEncodingMappings = defaultLocaleEncodingMappings == null ? new HashMap<String, String>() : defaultLocaleEncodingMappings;
189            this.defaultMimeTypeMappings = defaultMimeTypeMappings == null ? new HashMap<String, String>() : defaultMimeTypeMappings;
190        }
191    
192        public void doStart() throws Exception {
193            XmlBeansUtil.registerNamespaceUpdates(NAMESPACE_UPDATES);
194        }
195    
196        public void doStop() {
197            XmlBeansUtil.unregisterNamespaceUpdates(NAMESPACE_UPDATES);
198        }
199    
200        public void doFail() {
201            doStop();
202        }
203    
204        private static GBeanData getGBeanData(Kernel kernel, Object template) throws GBeanNotFoundException {
205            if (template == null) {
206                return null;
207            }
208            AbstractName templateName = kernel.getAbstractNameFor(template);
209            return kernel.getGBeanData(templateName);
210        }
211    
212        protected Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, boolean standAlone, String contextRoot, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
213            assert moduleFile != null : "moduleFile is null";
214            assert targetPath != null : "targetPath is null";
215            assert !targetPath.endsWith("/") : "targetPath must not end with a '/'";
216    
217            // parse the spec dd
218            String specDD = null;
219            WebAppType webApp = null;
220            try {
221                if (specDDUrl == null) {
222                    specDDUrl = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/web.xml");
223                }
224    
225                // read in the entire specDD as a string, we need this for getDeploymentDescriptor
226                // on the J2ee management object
227                specDD = DeploymentUtil.readAll(specDDUrl);
228    
229                // we found web.xml, if it won't parse that's an error.
230                XmlObject parsed = XmlBeansUtil.parse(specDD);
231                WebAppDocument webAppDoc = convertToServletSchema(parsed);
232                webApp = webAppDoc.getWebApp();
233                check(webApp);
234            } catch (XmlException e) {
235                // Output the target path in the error to make it clearer to the user which webapp
236                // has the problem.  The targetPath is used, as moduleFile may have an unhelpful
237                // value such as C:\geronimo-1.1\var\temp\geronimo-deploymentUtil22826.tmpdir
238                throw new DeploymentException("Error parsing web.xml for " + targetPath, e);
239            } catch (Exception e) {
240                if (!moduleFile.getName().endsWith(".war")) {
241                    //not for us
242                    return null;
243                }
244                //else ignore as jee5 allows optional spec dd for .war's
245            }
246    
247            if (webApp == null) {
248                webApp = WebAppType.Factory.newInstance();
249            }
250    
251            // parse vendor dd
252            JettyWebAppType jettyWebApp = getJettyWebApp(plan, moduleFile, standAlone, targetPath, webApp);
253            contextRoot = getContextRoot(jettyWebApp, contextRoot, webApp, standAlone, moduleFile, targetPath);
254    
255            EnvironmentType environmentType = jettyWebApp.getEnvironment();
256            Environment environment = EnvironmentBuilder.buildEnvironment(environmentType, defaultEnvironment);
257    
258            Boolean distributable = webApp.getDistributableArray().length == 1 ? TRUE : FALSE;
259            if (TRUE == distributable) {
260                clusteringBuilders.buildEnvironment(jettyWebApp, environment);
261            }
262    
263            // Note: logic elsewhere depends on the default artifact ID being the file name less extension (ConfigIDExtractor)
264            String warName = new File(moduleFile.getName()).getName();
265            if (warName.lastIndexOf('.') > -1) {
266                warName = warName.substring(0, warName.lastIndexOf('.'));
267            }
268            idBuilder.resolve(environment, warName, "war");
269    
270            AbstractName moduleName;
271            if (earName == null) {
272                earName = naming.createRootName(environment.getConfigId(), NameFactory.NULL, NameFactory.J2EE_APPLICATION);
273                moduleName = naming.createChildName(earName, environment.getConfigId().toString(), NameFactory.WEB_MODULE);
274            } else {
275                moduleName = naming.createChildName(earName, targetPath, NameFactory.WEB_MODULE);
276            }
277    
278            // Create the AnnotatedApp interface for the WebModule
279            AnnotatedWebApp annotatedWebApp = new AnnotatedWebApp(webApp);
280    
281            WebModule module = new WebModule(standAlone, moduleName, environment, moduleFile, targetPath, webApp, jettyWebApp, specDD, contextRoot, JETTY_NAMESPACE, annotatedWebApp);
282    
283            for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
284                mbe.createModule(module, plan, moduleFile, targetPath, specDDUrl, environment, contextRoot, earName, naming, idBuilder);
285            }
286            return module;
287        }
288    
289        String getContextRoot(JettyWebAppType jettyWebApp, String contextRoot, WebAppType webApp, boolean standAlone, JarFile moduleFile, String targetPath) {
290            if (jettyWebApp.isSetContextRoot()) {
291                contextRoot = jettyWebApp.getContextRoot();
292            } else if (contextRoot == null || contextRoot.trim().equals("")) {
293                contextRoot = determineDefaultContextRoot(webApp, standAlone, moduleFile, targetPath);
294            }
295    
296            contextRoot = contextRoot.trim();
297            return contextRoot;
298        }
299    
300        JettyWebAppType getJettyWebApp(Object plan, JarFile moduleFile, boolean standAlone, String targetPath, WebAppType webApp) throws DeploymentException {
301            XmlObject rawPlan = null;
302            try {
303                // load the geronimo-web.xml from either the supplied plan or from the earFile
304                try {
305                    if (plan instanceof XmlObject) {
306                        rawPlan = (XmlObject) plan;
307                    } else {
308                        if (plan != null) {
309                            rawPlan = XmlBeansUtil.parse(((File) plan).toURL(), getClass().getClassLoader());
310                        } else {
311                            URL path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-web.xml");
312                            try {
313                                rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
314                            } catch (FileNotFoundException e) {
315                                path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-jetty.xml");
316                                try {
317                                    rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
318                                } catch (FileNotFoundException e1) {
319                                    log.warn("Web application " + targetPath + " does not contain a WEB-INF/geronimo-web.xml deployment plan.  This may or may not be a problem, depending on whether you have things like resource references that need to be resolved.  You can also give the deployer a separate deployment plan file on the command line.");
320                                }
321                            }
322                        }
323                    }
324                } catch (IOException e) {
325                    log.warn(e);
326                }
327    
328                JettyWebAppType jettyWebApp;
329                if (rawPlan != null) {
330                    XmlObject webPlan = new GenericToSpecificPlanConverter(GerJettyDocument.type.getDocumentElementName().getNamespaceURI(),
331                            JettyWebAppDocument.type.getDocumentElementName().getNamespaceURI(), "jetty").convertToSpecificPlan(rawPlan);
332                    jettyWebApp = (JettyWebAppType) webPlan.changeType(JettyWebAppType.type);
333                    XmlBeansUtil.validateDD(jettyWebApp);
334                } else {
335                    jettyWebApp = createDefaultPlan();
336                }
337                return jettyWebApp;
338            } catch (XmlException e) {
339                throw new DeploymentException("xml problem for web app " + targetPath, e);
340            }
341        }
342    
343        private JettyWebAppType createDefaultPlan() {
344            return JettyWebAppType.Factory.newInstance();
345        }
346    
347        public void initContext(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
348            JettyWebAppType gerWebApp = (JettyWebAppType) module.getVendorDD();
349            boolean hasSecurityRealmName = gerWebApp.isSetSecurityRealmName();
350            basicInitContext(earContext, module, gerWebApp, hasSecurityRealmName);
351            for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
352                mbe.initContext(earContext, module, cl);
353            }
354        }
355    
356        public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repository) throws DeploymentException {
357            EARContext moduleContext = module.getEarContext();
358            AbstractName moduleName = moduleContext.getModuleName();
359            WebModule webModule = (WebModule) module;
360    
361            WebAppType webApp = (WebAppType) webModule.getSpecDD();
362            JettyWebAppType jettyWebApp = (JettyWebAppType) webModule.getVendorDD();
363            GBeanData webModuleData = new GBeanData(moduleName, JettyWebAppContext.GBEAN_INFO);
364    
365            configureBasicWebModuleAttributes(webApp, jettyWebApp, moduleContext, earContext, webModule, webModuleData);
366    
367            // unsharableResources, applicationManagedSecurityResources
368            GBeanResourceEnvironmentBuilder rebuilder = new GBeanResourceEnvironmentBuilder(webModuleData);
369            //N.B. use earContext not moduleContext
370            //TODO fix this for javaee 5 !!!
371            resourceEnvironmentSetter.setResourceEnvironment(rebuilder, webApp.getResourceRefArray(), jettyWebApp.getResourceRefArray());
372            try {
373                moduleContext.addGBean(webModuleData);
374                Set<String> securityRoles = collectRoleNames(webApp);
375                Map<String, PermissionCollection> rolePermissions = new HashMap<String, PermissionCollection>();
376    
377                // configure hosts and virtual-hosts
378                configureHosts(earContext, jettyWebApp, webModuleData);
379    
380    
381                String contextPath = webModule.getContextRoot();
382                if (contextPath == null) {
383                    throw new DeploymentException("null contextPath");
384                }
385                if (!contextPath.startsWith("/")) {
386                    contextPath = "/" + contextPath;
387                }
388                webModuleData.setAttribute("contextPath", contextPath);
389    
390                if (jettyWebApp.isSetWorkDir()) {
391                    String workDir = jettyWebApp.getWorkDir();
392                    webModuleData.setAttribute("workDir", workDir);
393                }
394    
395                if (jettyWebApp.isSetWebContainer()) {
396                    AbstractNameQuery webContainerName = ENCConfigBuilder.getGBeanQuery(NameFactory.GERONIMO_SERVICE, jettyWebApp.getWebContainer());
397                    webModuleData.setReferencePattern("JettyContainer", webContainerName);
398                } else {
399                    webModuleData.setReferencePattern("JettyContainer", jettyContainerObjectName);
400                }
401                //stuff that jetty used to do
402                if (webApp.getDisplayNameArray().length > 0) {
403                    webModuleData.setAttribute("displayName", webApp.getDisplayNameArray()[0].getStringValue());
404                }
405    
406                // configure context parameters.
407                configureContextParams(webApp, webModuleData);
408    
409                // configure listeners.
410                configureListeners(webApp, webModuleData);
411    
412                webModuleData.setAttribute(JettyWebAppContext.GBEAN_ATTR_SESSION_TIMEOUT,
413                        (webApp.getSessionConfigArray().length == 1 && webApp.getSessionConfigArray(0).getSessionTimeout() != null) ?
414                                webApp.getSessionConfigArray(0).getSessionTimeout().getBigIntegerValue().intValue() * 60 :
415                                defaultSessionTimeoutSeconds);
416    
417                Boolean distributable = webApp.getDistributableArray().length == 1 ? TRUE : FALSE;
418                webModuleData.setAttribute("distributable", distributable);
419                if (TRUE == distributable) {
420                    clusteringBuilders.build(jettyWebApp, earContext, moduleContext);
421                    if (webModuleData.getReferencePatterns(JettyWebAppContext.GBEAN_REF_SESSION_HANDLER_FACTORY) == null) {
422                        log.warn("No clustering builders configured: app will not be clustered");
423                        configureNoClustering(moduleContext, webModuleData);
424                    }
425                } else {
426                    configureNoClustering(moduleContext, webModuleData);
427                }
428    
429                // configure mime mappings.
430                configureMimeMappings(webApp, webModuleData);
431    
432                // configure welcome file lists.
433                configureWelcomeFileLists(webApp, webModuleData);
434    
435                // configure local encoding mapping lists.
436                configureLocaleEncodingMappingLists(webApp, webModuleData);
437    
438                // configure error pages.
439                configureErrorPages(webApp, webModuleData);
440    
441                // configure tag libs.
442                Set<String> knownServletMappings = new HashSet<String>();
443                Map<String, Set<String>> servletMappings = new HashMap<String, Set<String>>();
444                if (jspServlet != null) {
445                    configureTagLibs(module, webApp, webModuleData, servletMappings, knownServletMappings, jspServlet.getServletName());
446                    GBeanData jspServletData = configureDefaultServlet(jspServlet, earContext, moduleName, knownServletMappings, moduleContext);
447                    Set<String> jspMappings = (Set<String>) jspServletData.getAttribute("servletMappings");
448                    jspMappings.addAll(knownServletMappings);
449                    jspServletData.setAttribute("servletMappings", jspMappings);
450                }
451    
452                // configure login configs.
453                configureLoginConfigs(module, webApp, jettyWebApp, webModuleData);
454    
455                // Make sure that servlet mappings point to available servlets and never add a duplicate pattern.
456    
457                buildServletMappings(module, webApp, servletMappings, knownServletMappings);
458    
459                //"previous" filter mapping for linked list to keep dd's ordering.
460                AbstractName previous = null;
461    
462                //add default filters
463                if (defaultFilters != null) {
464                    previous = addDefaultFiltersGBeans(earContext, moduleContext, moduleName, previous);
465                }
466    
467                //add default filtermappings
468    //            if (defaultFilterMappings != null) {
469    //                for (Iterator iterator = defaultFilterMappings.iterator(); iterator.hasNext();) {
470    //                    Object defaultFilterMapping = iterator.next();
471    //                    GBeanData filterMappingGBeanData = getGBeanData(kernel, defaultFilterMapping);
472    //                    String filterName = (String) filterMappingGBeanData.getAttribute("filterName");
473    //                    ObjectName defaultFilterMappingObjectName;
474    //                    if (filterMappingGBeanData.getAttribute("urlPattern") != null) {
475    //                        String urlPattern = (String) filterMappingGBeanData.getAttribute("urlPattern");
476    //                        defaultFilterMappingObjectName = NameFactory.getWebFilterMappingName(null, null, null, null, filterName, null, urlPattern, moduleName);
477    //                    } else {
478    //                        Set servletNames = filterMappingGBeanData.getReferencePatterns("Servlet");
479    //                        if (servletNames == null || servletNames.size() != 1) {
480    //                            throw new DeploymentException("Exactly one servlet name must be supplied");
481    //                        }
482    //                        ObjectName servletObjectName = (ObjectName) servletNames.iterator().next();
483    //                        String servletName = servletObjectName.getKeyProperty("name");
484    //                        defaultFilterMappingObjectName = NameFactory.getWebFilterMappingName(null, null, null, null, filterName, servletName, null, moduleName);
485    //                    }
486    //                    filterMappingGBeanData.setName(defaultFilterMappingObjectName);
487    //                    filterMappingGBeanData.setReferencePattern("JettyFilterMappingRegistration", webModuleName);
488    //                    moduleContext.addGBean(filterMappingGBeanData);
489    //                }
490    //            }
491    
492                // add filter mapping GBeans.
493                addFilterMappingsGBeans(earContext, moduleContext, moduleName, webApp, previous);
494    
495                // add filter GBeans.
496                addFiltersGBeans(earContext, moduleContext, moduleName, webApp);
497    
498                //add default servlets
499                if (defaultServlets != null) {
500                    addDefaultServletsGBeans(earContext, moduleContext, moduleName, knownServletMappings);
501                }
502    
503                //set up servlet gbeans.
504                ServletType[] servletTypes = webApp.getServletArray();
505                addServlets(moduleName, webModule, servletTypes, servletMappings, securityRoles, rolePermissions, moduleContext);
506    
507                if (jettyWebApp.isSetSecurityRealmName()) {
508                    configureSecurityRealm(earContext, webApp, jettyWebApp, webModuleData, securityRoles, rolePermissions);
509                }
510    
511                //See Jetty-386, GERONIMO-3738
512                if (jettyWebApp.getCompactPath()) {
513                    webModuleData.setAttribute("compactPath", Boolean.TRUE);
514                }
515    
516                //TODO this may definitely not be the best place for this!
517                for (ModuleBuilderExtension mbe : moduleBuilderExtensions) {
518                    mbe.addGBeans(earContext, module, cl, repository);
519                }
520    
521                //not truly metadata complete until MBEs have run
522                if (!webApp.getMetadataComplete()) {
523                    webApp.setMetadataComplete(true);
524                    module.setOriginalSpecDD(module.getSpecDD().toString());
525                }
526                webModuleData.setAttribute("deploymentDescriptor", module.getOriginalSpecDD());
527    
528                if (!module.isStandAlone()) {
529                    ConfigurationData moduleConfigurationData = moduleContext.getConfigurationData();
530                    earContext.addChildConfiguration(module.getTargetPath(), moduleConfigurationData);
531                }
532            } catch (DeploymentException de) {
533                throw de;
534            } catch (Exception e) {
535                throw new DeploymentException("Unable to initialize webapp GBean for " + module.getName(), e);
536            }
537    
538        }
539    
540        private void configureNoClustering(EARContext moduleContext, GBeanData webModuleData) throws GBeanAlreadyExistsException {
541    //        AbstractName name = moduleContext.getNaming().createChildName(moduleContext.getModuleName(),
542    //                "DefaultWebApplicationHandlerFactory",
543    //                NameFactory.GERONIMO_SERVICE);
544    //        GBeanData beanData = new GBeanData(name, DefaultWebApplicationHandlerFactory.GBEAN_INFO);
545    //        webModuleData.setReferencePattern(JettyWebAppContext.GBEAN_REF_WEB_APPLICATION_HANDLER_FACTORY, name);
546    //        moduleContext.addGBean(beanData);
547        }
548    
549        private void configureSecurityRealm(EARContext earContext, WebAppType webApp, JettyWebAppType jettyWebApp, GBeanData webModuleData, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) throws DeploymentException {
550            AbstractName moduleName = webModuleData.getAbstractName();
551            if (earContext.getSecurityConfiguration() == null) {
552                throw new DeploymentException("You have specified a <security-realm-name> for the webapp " + moduleName + " but no <security> configuration (role mapping) is supplied in the Geronimo plan for the web application (or the Geronimo plan for the EAR if the web app is in an EAR)");
553            }
554            String securityRealmName = jettyWebApp.getSecurityRealmName().trim();
555            webModuleData.setAttribute("securityRealmName", securityRealmName);
556            webModuleData.setReferencePattern("RunAsSource", earContext.getJaccManagerName());
557    
558            /**
559             * TODO - go back to commented version when possible.
560             */
561            String policyContextID = moduleName.toString().replaceAll("[, :]", "_");
562            //String policyContextID = webModuleName.getCanonicalName();
563            webModuleData.setAttribute("policyContextID", policyContextID);
564    
565            ComponentPermissions componentPermissions = buildSpecSecurityConfig(webApp, securityRoles, rolePermissions);
566    
567            earContext.addSecurityContext(policyContextID, componentPermissions);
568        }
569    
570        private void addDefaultServletsGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, Set knownServletMappings) throws GBeanNotFoundException, GBeanAlreadyExistsException {
571            for (Object defaultServlet : defaultServlets) {
572                configureDefaultServlet(defaultServlet, earContext, moduleName, knownServletMappings, moduleContext);
573            }
574        }
575    
576        private GBeanData configureDefaultServlet(Object defaultServlet, EARContext earContext, AbstractName moduleName, Set knownServletMappings, EARContext moduleContext) throws GBeanNotFoundException, GBeanAlreadyExistsException {
577            GBeanData servletGBeanData = getGBeanData(kernel, defaultServlet);
578            AbstractName defaultServletObjectName = earContext.getNaming().createChildName(moduleName, (String) servletGBeanData.getAttribute("servletName"), NameFactory.SERVLET);
579            servletGBeanData.setAbstractName(defaultServletObjectName);
580            servletGBeanData.setReferencePattern("JettyServletRegistration", moduleName);
581            Set<String> defaultServletMappings = new HashSet<String>((Collection<String>) servletGBeanData.getAttribute("servletMappings"));
582            defaultServletMappings.removeAll(knownServletMappings);
583            servletGBeanData.setAttribute("servletMappings", defaultServletMappings);
584            moduleContext.addGBean(servletGBeanData);
585            return servletGBeanData;
586        }
587    
588        private void addFiltersGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, WebAppType webApp) throws GBeanAlreadyExistsException {
589            FilterType[] filterArray = webApp.getFilterArray();
590            for (FilterType filterType : filterArray) {
591                String filterName = filterType.getFilterName().getStringValue().trim();
592                AbstractName filterAbstractName = earContext.getNaming().createChildName(moduleName, filterName, NameFactory.WEB_FILTER);
593                GBeanData filterData = new GBeanData(filterAbstractName, JettyFilterHolder.GBEAN_INFO);
594                filterData.setAttribute("filterName", filterName);
595                filterData.setAttribute("filterClass", filterType.getFilterClass().getStringValue().trim());
596                Map<String, String> initParams = new HashMap<String, String>();
597                ParamValueType[] initParamArray = filterType.getInitParamArray();
598                for (ParamValueType paramValueType : initParamArray) {
599                    initParams.put(paramValueType.getParamName().getStringValue().trim(), paramValueType.getParamValue().getStringValue().trim());
600                }
601                filterData.setAttribute("initParams", initParams);
602                filterData.setReferencePattern("JettyServletRegistration", moduleName);
603                moduleContext.addGBean(filterData);
604            }
605        }
606    
607        private void addFilterMappingsGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, WebAppType webApp, AbstractName previous) throws GBeanAlreadyExistsException {
608            FilterMappingType[] filterMappingArray = webApp.getFilterMappingArray();
609            for (FilterMappingType filterMappingType : filterMappingArray) {
610                String filterName = filterMappingType.getFilterName().getStringValue().trim();
611                GBeanData filterMappingData = new GBeanData(JettyFilterMapping.GBEAN_INFO);
612                if (previous != null) {
613                    filterMappingData.addDependency(previous);
614                }
615                filterMappingData.setReferencePattern("JettyServletRegistration", moduleName);
616                AbstractName filterAbstractName = earContext.getNaming().createChildName(moduleName, filterName, NameFactory.WEB_FILTER);
617    
618                AbstractName filterMappingName = null;
619                if (filterMappingType.sizeOfUrlPatternArray() > 0) {
620                    String[] urlPatterns = new String[filterMappingType.sizeOfUrlPatternArray()];
621                    for (int j = 0; j < urlPatterns.length; j++) {
622                        urlPatterns[j] = filterMappingType.getUrlPatternArray(j).getStringValue().trim();
623                    }
624    
625                    filterMappingData.setAttribute("urlPatterns", urlPatterns);
626                    filterMappingName = earContext.getNaming().createChildName(filterAbstractName, ObjectName.quote(Arrays.deepToString(urlPatterns)), NameFactory.URL_WEB_FILTER_MAPPING);
627                }
628                if (filterMappingType.sizeOfServletNameArray() > 0) {
629                    Set<AbstractName> servletNameSet = new HashSet<AbstractName>();
630                    for (int j = 0; j < filterMappingType.sizeOfServletNameArray(); j++) {
631                        String servletName = filterMappingType.getServletNameArray(j).getStringValue().trim();
632                        AbstractName abstractServletName = earContext.getNaming().createChildName(moduleName, servletName, NameFactory.SERVLET);
633                        servletNameSet.add(abstractServletName);
634                        filterMappingData.addDependency(abstractServletName);
635                    }
636    
637                    filterMappingData.setReferencePatterns("Servlets", servletNameSet);
638                    filterMappingName = earContext.getNaming().createChildName(filterAbstractName, ObjectName.quote(Arrays.deepToString(servletNameSet.toArray())), NameFactory.SERVLET_WEB_FILTER_MAPPING);
639    
640                }
641                filterMappingData.setAbstractName(filterMappingName);
642                previous = filterMappingName;
643    
644                boolean request = filterMappingType.getDispatcherArray().length == 0;
645                boolean forward = false;
646                boolean include = false;
647                boolean error = false;
648                for (int j = 0; j < filterMappingType.getDispatcherArray().length; j++) {
649                    DispatcherType dispatcherType = filterMappingType.getDispatcherArray()[j];
650                    if (dispatcherType.getStringValue().equals("REQUEST")) {
651                        request = true;
652                    } else if (dispatcherType.getStringValue().equals("FORWARD")) {
653                        forward = true;
654                    } else if (dispatcherType.getStringValue().equals("INCLUDE")) {
655                        include = true;
656                    } else if (dispatcherType.getStringValue().equals("ERROR")) {
657                        error = true;
658                    }
659                }
660                filterMappingData.setAttribute("requestDispatch", request);
661                filterMappingData.setAttribute("forwardDispatch", forward);
662                filterMappingData.setAttribute("includeDispatch", include);
663                filterMappingData.setAttribute("errorDispatch", error);
664                filterMappingData.setReferencePattern("Filter", filterAbstractName);
665                moduleContext.addGBean(filterMappingData);
666            }
667        }
668    
669        private AbstractName addDefaultFiltersGBeans(EARContext earContext, EARContext moduleContext, AbstractName moduleName, AbstractName previous) throws GBeanNotFoundException, GBeanAlreadyExistsException {
670            for (Object defaultFilter : defaultFilters) {
671                GBeanData filterGBeanData = getGBeanData(kernel, defaultFilter);
672                String filterName = (String) filterGBeanData.getAttribute("filterName");
673                AbstractName defaultFilterAbstractName = earContext.getNaming().createChildName(moduleName, filterName, NameFactory.WEB_FILTER);
674                filterGBeanData.setAbstractName(defaultFilterAbstractName);
675                filterGBeanData.setReferencePattern("JettyServletRegistration", moduleName);
676                moduleContext.addGBean(filterGBeanData);
677                //add a mapping to /*
678    
679                GBeanData filterMappingGBeanData = new GBeanData(JettyFilterMapping.GBEAN_INFO);
680                if (previous != null) {
681                    filterMappingGBeanData.addDependency(previous);
682                }
683                filterMappingGBeanData.setReferencePattern("JettyServletRegistration", moduleName);
684                String urlPattern = "/*";
685                filterMappingGBeanData.setAttribute("urlPattern", urlPattern);
686                AbstractName filterMappingName = earContext.getNaming().createChildName(defaultFilterAbstractName, urlPattern, NameFactory.URL_WEB_FILTER_MAPPING);
687                filterMappingGBeanData.setAbstractName(filterMappingName);
688                previous = filterMappingName;
689    
690    
691                filterMappingGBeanData.setAttribute("requestDispatch", TRUE);
692                filterMappingGBeanData.setAttribute("forwardDispatch", TRUE);
693                filterMappingGBeanData.setAttribute("includeDispatch", TRUE);
694                filterMappingGBeanData.setAttribute("errorDispatch", FALSE);
695                filterMappingGBeanData.setReferencePattern("Filter", defaultFilterAbstractName);
696                moduleContext.addGBean(filterMappingGBeanData);
697            }
698            return previous;
699        }
700    
701        private Map buildServletMappings(Module module, WebAppType webApp, Map<String, Set<String>> servletMappings, Set<String> knownServletMappings) throws DeploymentException {
702            ServletType[] servletTypes = webApp.getServletArray();
703            Set<String> knownServlets = new HashSet<String>();
704            for (ServletType type : servletTypes) {
705                knownServlets.add(type.getServletName().getStringValue().trim());
706            }
707    
708            ServletMappingType[] servletMappingArray = webApp.getServletMappingArray();
709            for (ServletMappingType servletMappingType : servletMappingArray) {
710                String servletName = servletMappingType.getServletName().getStringValue().trim();
711                if (!knownServlets.contains(servletName)) {
712                    throw new DeploymentException("Web app " + module.getName() +
713                            " contains a servlet mapping that refers to servlet '" + servletName +
714                            "' but no such servlet was found!");
715                }
716                UrlPatternType[] urlPatterns = servletMappingType.getUrlPatternArray();
717                addMappingsForServlet(servletName, urlPatterns, knownServletMappings, servletMappings);
718            }
719    
720            return servletMappings;
721        }
722    
723        private void addMappingsForServlet(String servletName, UrlPatternType[] urlPatterns, Set<String> knownServletMappings, Map<String, Set<String>> servletMappings) throws DeploymentException {
724            for (UrlPatternType patternType : urlPatterns) {
725                String urlPattern = patternType.getStringValue().trim();
726                if (!urlPattern.startsWith("*") && !urlPattern.startsWith("/")) {
727                    urlPattern = "/" + urlPattern;
728                }
729                if (!knownServletMappings.contains(urlPattern)) {
730                    knownServletMappings.add(urlPattern);
731                    checkString(urlPattern);
732                    Set<String> urlsForServlet = servletMappings.get(servletName);
733                    if (urlsForServlet == null) {
734                        urlsForServlet = new HashSet<String>();
735                        servletMappings.put(servletName, urlsForServlet);
736                    }
737                    urlsForServlet.add(urlPattern);
738                }
739            }
740        }
741    
742        private void configureLoginConfigs(Module module, WebAppType webApp, JettyWebAppType jettyWebApp, GBeanData webModuleData) throws DeploymentException {
743            LoginConfigType[] loginConfigArray = webApp.getLoginConfigArray();
744            if (loginConfigArray.length > 1) {
745                throw new DeploymentException("Web app " + module.getName() + " cannot have more than one login-config element.  Currently has " + loginConfigArray.length + " login-config elements.");
746            }
747            if (loginConfigArray.length == 1) {
748                LoginConfigType loginConfig = loginConfigArray[0];
749                if (loginConfig.isSetAuthMethod()) {
750                    String authMethod = loginConfig.getAuthMethod().getStringValue();
751                    if ("BASIC".equals(authMethod)) {
752                        webModuleData.setAttribute("authenticator", new BasicAuthenticator());
753                    } else if ("DIGEST".equals(authMethod)) {
754                        webModuleData.setAttribute("authenticator", new DigestAuthenticator());
755                    } else if ("FORM".equals(authMethod)) {
756    
757                        FormAuthenticator formAuthenticator = new FormAuthenticator();
758                        webModuleData.setAttribute("authenticator", formAuthenticator);
759                        if (loginConfig.isSetFormLoginConfig()) {
760                            FormLoginConfigType formLoginConfig = loginConfig.getFormLoginConfig();
761                            formAuthenticator.setLoginPage(formLoginConfig.getFormLoginPage().getStringValue());
762                            formAuthenticator.setErrorPage(formLoginConfig.getFormErrorPage().getStringValue());
763                        }
764                    } else if ("CLIENT-CERT".equals(authMethod)) {
765                        webModuleData.setAttribute("authenticator", new ClientCertAuthenticator());
766                    }
767                }
768                if (loginConfig.isSetRealmName()) {
769                    webModuleData.setAttribute("realmName", loginConfig.getRealmName().getStringValue());
770                }
771    
772            } else if (jettyWebApp.isSetSecurityRealmName()) {
773                webModuleData.setAttribute("authenticator", new NonAuthenticator());
774            }
775        }
776    
777        private void configureTagLibs(Module module, WebAppType webApp, GBeanData webModuleData, Map<String, Set<String>> servletMappings, Set<String> knownServletMappings, String jspServletName) throws DeploymentException {
778            JspConfigType[] jspConfigArray = webApp.getJspConfigArray();
779            if (jspConfigArray.length > 1) {
780                throw new DeploymentException("Web app " + module.getName() + " cannot have more than one jsp-config element.  Currently has " + jspConfigArray.length + " jsp-config elements.");
781            }
782            Map<String, String> tagLibMap = new HashMap<String, String>();
783            for (JspConfigType aJspConfigArray : jspConfigArray) {
784                TaglibType[] tagLibArray = aJspConfigArray.getTaglibArray();
785                for (TaglibType taglib : tagLibArray) {
786                    tagLibMap.put(taglib.getTaglibUri().getStringValue().trim(), taglib.getTaglibLocation().getStringValue().trim());
787                }
788                for (JspPropertyGroupType propertyGroup: aJspConfigArray.getJspPropertyGroupArray()) {
789                    UrlPatternType[] urlPatterns = propertyGroup.getUrlPatternArray();
790                    addMappingsForServlet(jspServletName, urlPatterns, knownServletMappings, servletMappings);
791                }
792            }
793            webModuleData.setAttribute("tagLibMap", tagLibMap);
794        }
795    
796        private void configureErrorPages(WebAppType webApp, GBeanData webModuleData) {
797            ErrorPageType[] errorPageArray = webApp.getErrorPageArray();
798            Map<String, String> errorPageMap = new HashMap<String, String>();
799            for (ErrorPageType errorPageType : errorPageArray) {
800                if (errorPageType.isSetErrorCode()) {
801                    errorPageMap.put(errorPageType.getErrorCode().getStringValue().trim(), errorPageType.getLocation().getStringValue().trim());
802                } else {
803                    errorPageMap.put(errorPageType.getExceptionType().getStringValue().trim(), errorPageType.getLocation().getStringValue().trim());
804                }
805            }
806            webModuleData.setAttribute("errorPages", errorPageMap);
807        }
808    
809        private void configureLocaleEncodingMappingLists(WebAppType webApp, GBeanData webModuleData) {
810            LocaleEncodingMappingListType[] localeEncodingMappingListArray = webApp.getLocaleEncodingMappingListArray();
811            Map<String, String> localeEncodingMappingMap = new HashMap<String, String>(defaultLocaleEncodingMappings);
812            for (LocaleEncodingMappingListType aLocaleEncodingMappingListArray : localeEncodingMappingListArray) {
813                LocaleEncodingMappingType[] localeEncodingMappingArray = aLocaleEncodingMappingListArray.getLocaleEncodingMappingArray();
814                for (LocaleEncodingMappingType localeEncodingMapping : localeEncodingMappingArray) {
815                    localeEncodingMappingMap.put(localeEncodingMapping.getLocale().trim(), localeEncodingMapping.getEncoding().trim());
816                }
817            }
818            webModuleData.setAttribute("localeEncodingMapping", localeEncodingMappingMap);
819        }
820    
821        private void configureWelcomeFileLists(WebAppType webApp, GBeanData webModuleData) {
822            WelcomeFileListType[] welcomeFileArray = webApp.getWelcomeFileListArray();
823            List<String> welcomeFiles;
824            if (welcomeFileArray.length > 0) {
825                welcomeFiles = new ArrayList<String>();
826                for (WelcomeFileListType aWelcomeFileArray : welcomeFileArray) {
827                    String[] welcomeFileListType = aWelcomeFileArray.getWelcomeFileArray();
828                    for (String welcomeFile : welcomeFileListType) {
829                        welcomeFiles.add(welcomeFile.trim());
830                    }
831                }
832            } else {
833                welcomeFiles = new ArrayList<String>(defaultWelcomeFiles);
834            }
835            webModuleData.setAttribute("welcomeFiles", welcomeFiles.toArray(new String[welcomeFiles.size()]));
836        }
837    
838        private void configureMimeMappings(WebAppType webApp, GBeanData webModuleData) {
839            MimeMappingType[] mimeMappingArray = webApp.getMimeMappingArray();
840            Map<String, String> mimeMappingMap = new HashMap<String, String>(defaultMimeTypeMappings);
841            for (MimeMappingType mimeMappingType : mimeMappingArray) {
842                mimeMappingMap.put(mimeMappingType.getExtension().getStringValue().trim(), mimeMappingType.getMimeType().getStringValue().trim());
843            }
844            webModuleData.setAttribute("mimeMap", mimeMappingMap);
845        }
846    
847        private void configureListeners(WebAppType webApp, GBeanData webModuleData) {
848            ListenerType[] listenerArray = webApp.getListenerArray();
849            Collection<String> listeners = new ArrayList<String>();
850            for (ListenerType listenerType : listenerArray) {
851                listeners.add(listenerType.getListenerClass().getStringValue().trim());
852            }
853            webModuleData.setAttribute("listenerClassNames", listeners);
854        }
855    
856        private void configureContextParams(WebAppType webApp, GBeanData webModuleData) {
857            ParamValueType[] contextParamArray = webApp.getContextParamArray();
858            Map<String, String> contextParams = new HashMap<String, String>();
859            for (ParamValueType contextParam : contextParamArray) {
860                contextParams.put(contextParam.getParamName().getStringValue().trim(), contextParam.getParamValue().getStringValue().trim());
861            }
862            webModuleData.setAttribute("contextParamMap", contextParams);
863        }
864    
865        private void configureHosts(EARContext earContext, JettyWebAppType jettyWebApp, GBeanData webModuleData) throws GBeanAlreadyExistsException {
866            String[] hosts = jettyWebApp.getHostArray();
867            for (int i = 0; i < hosts.length; i++) {
868                hosts[i] = hosts[i].trim();
869            }
870            String[] virtualHosts = jettyWebApp.getVirtualHostArray();
871            for (int i = 0; i < virtualHosts.length; i++) {
872                virtualHosts[i] = virtualHosts[i].trim();
873            }
874            if (hosts.length > 0 || virtualHosts.length > 0) {
875                //use name same as module
876                AbstractName hostName = earContext.getNaming().createChildName(webModuleData.getAbstractName(), "Host", "Host");
877                GBeanData hostData = new GBeanData(hostName, Host.GBEAN_INFO);
878                hostData.setAttribute("hosts", hosts);
879                hostData.setAttribute("virtualHosts", virtualHosts);
880                earContext.addGBean(hostData);
881                webModuleData.setReferencePattern("Host", hostName);
882            }
883        }
884    
885        public String getSchemaNamespace() {
886            return JETTY_NAMESPACE;
887        }
888    
889        /**
890         * Adds the provided servlets, taking into account the load-on-startup ordering.
891         *
892         * @param webModuleName   an <code>ObjectName</code> value
893         * @param module          a <code>Module</code> value
894         * @param servletTypes    a <code>ServletType[]</code> value, contains the <code>servlet</code> entries from <code>web.xml</code>.
895         * @param servletMappings a <code>Map</code> value
896         * @param securityRoles   a <code>Set</code> value
897         * @param rolePermissions a <code>Map</code> value
898         * @param moduleContext   an <code>EARContext</code> value
899         * @throws DeploymentException if an error occurs
900         */
901        private void addServlets(AbstractName webModuleName,
902                Module module,
903                ServletType[] servletTypes,
904                Map<String, Set<String>> servletMappings,
905                Set<String> securityRoles,
906                Map<String, PermissionCollection> rolePermissions,
907                EARContext moduleContext) throws DeploymentException {
908    
909            // this TreeSet will order the ServletTypes based on whether
910            // they have a load-on-startup element and what its value is
911            TreeSet<ServletType> loadOrder = new TreeSet<ServletType>(new StartupOrderComparator());
912    
913            // add all of the servlets to the sorted set
914            for (ServletType servletType1 : servletTypes) {
915                loadOrder.add(servletType1);
916            }
917    
918            // now that they're sorted, read them in order and add them to
919            // the context.  we'll use a GBean reference to enforce the
920            // load order.  Each servlet GBean (except the first) has a
921            // reference to the previous GBean.  The kernel will ensure
922            // that each "previous" GBean is running before it starts any
923            // other GBeans that reference it.  See also
924            // o.a.g.jetty6.JettyFilterMapping which provided the example
925            // of how to do this.
926            // http://issues.apache.org/jira/browse/GERONIMO-645
927            AbstractName previousServlet = null;
928            for (Object aLoadOrder : loadOrder) {
929                ServletType servletType = (ServletType) aLoadOrder;
930                previousServlet = addServlet(webModuleName, module, previousServlet, servletType, servletMappings, securityRoles, rolePermissions, moduleContext);
931            }
932    
933            // JACC v1.0 secion B.19
934            addUnmappedJSPPermissions(securityRoles, rolePermissions);
935        }
936    
937        /**
938         * @param webModuleName AbstractName of the web module
939         * @param module the web module being added
940         * @param previousServlet the servlet to start before this one in init order
941         * @param servletType XMLObject specifying the servlet configuration
942         * @param servletMappings Map of servlet name to set of ServletMapping strings for this web app
943         * @param securityRoles security roles in the web app
944         * @param rolePermissions RolePermissions for the roles this servlet needs to access
945         * @param moduleContext deployment context for this module
946         * @return AbstractName of servlet gbean added
947         * @throws DeploymentException if something goes wrong
948         */
949        private AbstractName addServlet(AbstractName webModuleName,
950                Module module,
951                AbstractName previousServlet,
952                ServletType servletType,
953                Map<String, Set<String>> servletMappings,
954                Set<String> securityRoles,
955                Map<String, PermissionCollection> rolePermissions,
956                EARContext moduleContext) throws DeploymentException {
957            String servletName = servletType.getServletName().getStringValue().trim();
958            AbstractName servletAbstractName = moduleContext.getNaming().createChildName(webModuleName, servletName, NameFactory.SERVLET);
959            GBeanData servletData;
960            Map<String, String> initParams = new HashMap<String, String>();
961            if (servletType.isSetServletClass()) {
962                ClassLoader webClassLoader = moduleContext.getClassLoader();
963                String servletClassName = servletType.getServletClass().getStringValue().trim();
964                Class servletClass;
965                try {
966                    servletClass = webClassLoader.loadClass(servletClassName);
967                } catch (ClassNotFoundException e) {
968                    throw new DeploymentException("Could not load servlet class " + servletClassName, e); // TODO identify web app in message
969                }
970                Class baseServletClass;
971                try {
972                    baseServletClass = webClassLoader.loadClass(Servlet.class.getName());
973                } catch (ClassNotFoundException e) {
974                    throw new DeploymentException("Could not load javax.servlet.Servlet in web classloader", e); // TODO identify web app in message
975                }
976                if (baseServletClass.isAssignableFrom(servletClass)) {
977                    servletData = new GBeanData(servletAbstractName, JettyServletHolder.GBEAN_INFO);
978                    servletData.setAttribute("servletClass", servletClassName);
979                } else {
980                    servletData = new GBeanData(pojoWebServiceTemplate);
981                    servletData.setAbstractName(servletAbstractName);
982                    //let the web service builder deal with configuring the gbean with the web service stack
983    //                Object portInfo = portMap.get(servletName);
984    //                if (portInfo == null) {
985    //                    throw new DeploymentException("No web service deployment info for servlet name " + servletName); // TODO identify web app in message
986    //                }
987                    boolean configured = false;
988                    for (Object aWebServiceBuilder : webServiceBuilder) {
989                        WebServiceBuilder serviceBuilder = (WebServiceBuilder) aWebServiceBuilder;
990                        if (serviceBuilder.configurePOJO(servletData, servletName, module, servletClassName, moduleContext)) {
991                            configured = true;
992                            break;
993                        }
994                    }
995                    if (!configured) {
996                        throw new DeploymentException("POJO web service: " + servletName + " not configured by any web service builder");
997                    }
998                }
999            } else if (servletType.isSetJspFile()) {
1000                servletData = new GBeanData(servletAbstractName, JettyServletHolder.GBEAN_INFO);
1001                servletData.setAttribute("jspFile", servletType.getJspFile().getStringValue().trim());
1002                servletData.setAttribute("servletClass", jspServlet.getServletClassName());
1003                initParams.put("development", "false");
1004            } else {
1005                throw new DeploymentException("Neither servlet class nor jsp file is set for " + servletName); // TODO identify web app in message
1006            }
1007    
1008            // link to previous servlet, if there is one, so that we
1009            // preserve the <load-on-startup> ordering.
1010            // http://issues.apache.org/jira/browse/GERONIMO-645
1011            if (null != previousServlet) {
1012                servletData.addDependency(previousServlet);
1013            }
1014    
1015            //TODO in init param setter, add classpath if jspFile is not null.
1016            servletData.setReferencePattern("JettyServletRegistration", webModuleName);
1017            servletData.setAttribute("servletName", servletName);
1018            ParamValueType[] initParamArray = servletType.getInitParamArray();
1019            for (ParamValueType paramValueType : initParamArray) {
1020                initParams.put(paramValueType.getParamName().getStringValue().trim(), paramValueType.getParamValue().getStringValue().trim());
1021            }
1022            servletData.setAttribute("initParams", initParams);
1023            if (servletType.isSetLoadOnStartup()) {
1024                Integer loadOnStartup = new Integer(servletType.xgetLoadOnStartup().getStringValue());
1025                servletData.setAttribute("loadOnStartup", loadOnStartup);
1026            }
1027    
1028            Set mappings = servletMappings.get(servletName);
1029            servletData.setAttribute("servletMappings", mappings == null ? Collections.EMPTY_SET : mappings);
1030    
1031            //run-as
1032            if (servletType.isSetRunAs()) {
1033                String runAsRole = servletType.getRunAs().getRoleName().getStringValue().trim();
1034                servletData.setAttribute("runAsRole", runAsRole);
1035            }
1036    
1037            processRoleRefPermissions(servletType, securityRoles, rolePermissions);
1038    
1039            try {
1040                moduleContext.addGBean(servletData);
1041            } catch (GBeanAlreadyExistsException e) {
1042                throw new DeploymentException("Could not add servlet gbean to context", e); // TODO identify web app in message
1043            }
1044            return servletAbstractName;
1045        }
1046    
1047        public static final GBeanInfo GBEAN_INFO;
1048    
1049        static {
1050            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(JettyModuleBuilder.class, NameFactory.MODULE_BUILDER);
1051            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
1052            infoBuilder.addAttribute("defaultSessionTimeoutSeconds", Integer.class, true, true);
1053            infoBuilder.addAttribute("defaultWelcomeFiles", List.class, true, true);
1054            infoBuilder.addAttribute("defaultLocaleEncodingMappings", Map.class, true, true);
1055            infoBuilder.addAttribute("defaultMimeTypeMappings", Map.class, true, true);
1056            infoBuilder.addAttribute("jettyContainerObjectName", AbstractNameQuery.class, true, true);
1057            infoBuilder.addReference("JspServlet", JettyJspServletHolder.class, NameFactory.SERVLET_TEMPLATE);
1058            infoBuilder.addReference("DefaultServlets", JettyDefaultServletHolder.class, NameFactory.SERVLET_TEMPLATE);
1059            infoBuilder.addReference("DefaultFilters", Object.class);
1060            infoBuilder.addReference("DefaultFilterMappings", Object.class);
1061            infoBuilder.addReference("PojoWebServiceTemplate", Object.class, NameFactory.SERVLET_WEB_SERVICE_TEMPLATE);
1062            infoBuilder.addReference("WebServiceBuilder", WebServiceBuilder.class, NameFactory.MODULE_BUILDER);
1063            infoBuilder.addReference("ClusteringBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
1064            infoBuilder.addReference("SecurityBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
1065            infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, NameFactory.MODULE_BUILDER);
1066            infoBuilder.addReference("NamingBuilders", NamingBuilder.class, NameFactory.MODULE_BUILDER);
1067            infoBuilder.addReference("ModuleBuilderExtensions", ModuleBuilderExtension.class, NameFactory.MODULE_BUILDER);
1068            infoBuilder.addReference("ResourceEnvironmentSetter", ResourceEnvironmentSetter.class, NameFactory.MODULE_BUILDER);
1069            infoBuilder.addAttribute("kernel", Kernel.class, false);
1070            infoBuilder.addInterface(ModuleBuilder.class);
1071    
1072            infoBuilder.setConstructor(new String[]{
1073                    "defaultEnvironment",
1074                    "defaultSessionTimeoutSeconds",
1075                    "defaultWelcomeFiles",
1076                    "jettyContainerObjectName",
1077                    "JspServlet",
1078                    "DefaultServlets",
1079                    "DefaultFilters",
1080                    "DefaultFilterMappings",
1081                    "defaultLocaleEncodingMappings",
1082                    "defaultMimeTypeMappings",
1083                    "PojoWebServiceTemplate",
1084                    "WebServiceBuilder",
1085                    "ClusteringBuilders",
1086                    "SecurityBuilders",
1087                    "ServiceBuilders",
1088                    "NamingBuilders",
1089                    "ModuleBuilderExtensions",
1090                    "ResourceEnvironmentSetter",
1091                    "kernel"});
1092            GBEAN_INFO = infoBuilder.getBeanInfo();
1093        }
1094    
1095        public static GBeanInfo getGBeanInfo() {
1096            return GBEAN_INFO;
1097        }
1098    
1099        static class StartupOrderComparator implements Comparator<ServletType> {
1100            /**
1101             * comparator that compares first on the basis of startup order, and then on the lexicographical
1102             * ordering of servlet name.  Since the servlet names have a uniqueness constraint, this should
1103             * provide a total ordering consistent with equals.  All servlets with no startup order are after
1104             * all servlets with a startup order.
1105             *
1106             * @param s1 first ServletType object
1107             * @param s2 second ServletType object
1108             * @return an int < 0 if o1 precedes o2, 0 if they are equal, and > 0 if o2 preceeds o1.
1109             */
1110            public int compare(ServletType s1, ServletType s2) {
1111    
1112                // load-on-startup is set for neither.  the
1113                // ordering at this point doesn't matter, but we
1114                // should return "0" only if the two objects say
1115                // they are equal
1116                if (!s1.isSetLoadOnStartup() && !s2.isSetLoadOnStartup()) {
1117                    return s1.equals(s2) ? 0 : s1.getServletName().getStringValue().trim().compareTo(s2.getServletName().getStringValue().trim());
1118                }
1119    
1120                // load-on-startup is set for one but not the
1121                // other.  whichever one is set will be "less
1122                // than", i.e. it will be loaded first
1123                if (s1.isSetLoadOnStartup() && !s2.isSetLoadOnStartup()) {
1124                    return -1;
1125                }
1126                if (!s1.isSetLoadOnStartup() && s2.isSetLoadOnStartup()) {
1127                    return 1;
1128                }
1129    
1130                // load-on-startup is set for both.  whichever one
1131                // has a smaller value is "less than"
1132                int comp = new Integer(s1.xgetLoadOnStartup().getStringValue()).compareTo(new Integer(s2.xgetLoadOnStartup().getStringValue()));
1133                if (comp == 0) {
1134                    return s1.getServletName().getStringValue().trim().compareTo(s2.getServletName().getStringValue().trim());
1135                }
1136                return comp;
1137            }
1138        }
1139    }