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