/* * JBoss, Home of Professional Open Source. * Copyright 2010, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.undertow.deployment; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.EventListener; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.Filter; import javax.servlet.Servlet; import javax.servlet.ServletContainerInitializer; import javax.servlet.http.HttpServletRequest; import io.undertow.jsp.JspFileWrapper; import io.undertow.jsp.JspServletBuilder; import io.undertow.server.HttpServerExchange; import io.undertow.servlet.api.ClassIntrospecter; import io.undertow.servlet.api.DefaultServletConfig; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.ErrorPage; import io.undertow.servlet.api.FilterInfo; import io.undertow.servlet.api.HttpMethodSecurityInfo; import io.undertow.servlet.api.InstanceFactory; import io.undertow.servlet.api.InstanceHandle; import io.undertow.servlet.api.ListenerInfo; import io.undertow.servlet.api.LoginConfig; import io.undertow.servlet.api.MimeMapping; import io.undertow.servlet.api.SecurityConstraint; import io.undertow.servlet.api.ServletContainerInitializerInfo; import io.undertow.servlet.api.ServletInfo; import io.undertow.servlet.api.ServletSecurityInfo; import io.undertow.servlet.api.ThreadSetupAction; import io.undertow.servlet.api.WebResourceCollection; import io.undertow.servlet.util.ConstructorInstanceFactory; import io.undertow.servlet.util.ImmediateInstanceFactory; import org.apache.jasper.deploy.FunctionInfo; import org.apache.jasper.deploy.JspPropertyGroup; import org.apache.jasper.deploy.TagAttributeInfo; import org.apache.jasper.deploy.TagFileInfo; import org.apache.jasper.deploy.TagInfo; import org.apache.jasper.deploy.TagLibraryInfo; import org.apache.jasper.deploy.TagLibraryValidatorInfo; import org.apache.jasper.deploy.TagVariableInfo; import org.apache.jasper.servlet.JspServlet; import org.jboss.annotation.javaee.Icon; import org.jboss.as.clustering.web.DistributedCacheManagerFactory; import org.jboss.as.clustering.web.DistributedCacheManagerFactoryService; import org.jboss.as.controller.PathElement; import org.jboss.as.ee.component.ComponentRegistry; import org.jboss.as.ee.component.EEModuleDescription; import org.jboss.as.naming.ManagedReference; import org.jboss.as.security.plugins.SecurityDomainContext; import org.jboss.as.security.service.SecurityDomainService; import org.jboss.as.server.deployment.Attachments; import org.jboss.as.server.deployment.DeploymentPhaseContext; import org.jboss.as.server.deployment.DeploymentUnit; import org.jboss.as.server.deployment.DeploymentUnitProcessingException; import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.as.server.deployment.SetupAction; import org.jboss.as.server.deployment.reflect.DeploymentClassIndex; import org.jboss.as.undertow.DeploymentDefinition; import org.jboss.as.undertow.Host; import org.jboss.as.undertow.ServletContainerService; import org.jboss.as.undertow.UndertowExtension; import org.jboss.as.undertow.UndertowService; import org.jboss.as.undertow.security.SecurityContextAssociationHandler; import org.jboss.as.undertow.security.SecurityContextCreationHandler; import org.jboss.as.web.common.ServletContextAttribute; import org.jboss.as.web.common.WarMetaData; import org.jboss.as.web.common.WebComponentDescription; import org.jboss.as.web.common.WebInjectionContainer; import org.jboss.as.web.host.ContextActivator; import org.jboss.dmr.ModelNode; import org.jboss.metadata.ear.jboss.JBossAppMetaData; import org.jboss.metadata.ear.spec.EarMetaData; import org.jboss.metadata.javaee.spec.DescriptionGroupMetaData; import org.jboss.metadata.javaee.spec.ParamValueMetaData; import org.jboss.metadata.javaee.spec.SecurityRoleRefMetaData; import org.jboss.metadata.web.jboss.JBossServletMetaData; import org.jboss.metadata.web.jboss.JBossWebMetaData; import org.jboss.metadata.web.spec.AttributeMetaData; import org.jboss.metadata.web.spec.DispatcherType; import org.jboss.metadata.web.spec.EmptyRoleSemanticType; import org.jboss.metadata.web.spec.ErrorPageMetaData; import org.jboss.metadata.web.spec.FilterMappingMetaData; import org.jboss.metadata.web.spec.FilterMetaData; import org.jboss.metadata.web.spec.FunctionMetaData; import org.jboss.metadata.web.spec.HttpMethodConstraintMetaData; import org.jboss.metadata.web.spec.JspConfigMetaData; import org.jboss.metadata.web.spec.JspPropertyGroupMetaData; import org.jboss.metadata.web.spec.ListenerMetaData; import org.jboss.metadata.web.spec.LocaleEncodingMetaData; import org.jboss.metadata.web.spec.LoginConfigMetaData; import org.jboss.metadata.web.spec.MimeMappingMetaData; import org.jboss.metadata.web.spec.SecurityConstraintMetaData; import org.jboss.metadata.web.spec.ServletMappingMetaData; import org.jboss.metadata.web.spec.TagFileMetaData; import org.jboss.metadata.web.spec.TagMetaData; import org.jboss.metadata.web.spec.TldMetaData; import org.jboss.metadata.web.spec.TransportGuaranteeType; import org.jboss.metadata.web.spec.VariableMetaData; import org.jboss.metadata.web.spec.WebResourceCollectionMetaData; import org.jboss.modules.Module; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController.Mode; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.jboss.security.SecurityConstants; import org.jboss.security.SecurityUtil; import org.jboss.vfs.VirtualFile; import static javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic.DENY; import static javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic.PERMIT; import static org.jboss.as.undertow.UndertowMessages.MESSAGES; public class UndertowDeploymentProcessor implements DeploymentUnitProcessor { private final String defaultServer; private final String defaultHost; private final String defaultContainer; public UndertowDeploymentProcessor(String defaultHost, final String defaultContainer, String defaultServer) { this.defaultHost = defaultHost; if (defaultHost == null) { throw MESSAGES.nullDefaultHost(); } this.defaultContainer = defaultContainer; this.defaultServer = defaultServer; } @Override public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY); if (warMetaData == null) { return; } String hostName = hostNameOfDeployment(warMetaData, defaultHost); processDeployment(warMetaData, deploymentUnit, phaseContext.getServiceTarget(),hostName); } static String hostNameOfDeployment(final WarMetaData metaData, final String defaultHost) { Collection<String> hostNames = null; if (metaData.getMergedJBossWebMetaData() != null) { hostNames = metaData.getMergedJBossWebMetaData().getVirtualHosts(); } if (hostNames == null || hostNames.isEmpty()) { hostNames = Collections.singleton(defaultHost); } String hostName = hostNames.iterator().next(); if (hostName == null) { throw MESSAGES.nullHostName(); } return hostName; } @Override public void undeploy(final DeploymentUnit context) { //AbstractSecurityDeployer<?> deployer = new WarSecurityDeployer(); //deployer.undeploy(context); } private void processDeployment(final WarMetaData warMetaData, final DeploymentUnit deploymentUnit, final ServiceTarget serviceTarget, String hostName) throws DeploymentUnitProcessingException { final VirtualFile deploymentRoot = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT).getRoot(); final Module module = deploymentUnit.getAttachment(Attachments.MODULE); if (module == null) { throw new DeploymentUnitProcessingException(MESSAGES.failedToResolveModule(deploymentUnit)); } final DeploymentClassIndex deploymentClassIndex = deploymentUnit.getAttachment(Attachments.CLASS_INDEX); final JBossWebMetaData metaData = warMetaData.getMergedJBossWebMetaData(); final List<SetupAction> setupActions = deploymentUnit.getAttachmentList(org.jboss.as.ee.component.Attachments.WEB_SETUP_ACTIONS); ScisMetaData scisMetaData = deploymentUnit.getAttachment(ScisMetaData.ATTACHMENT_KEY); final Set<ServiceName> dependentComponents = new HashSet<>(); // see AS7-2077 // basically we want to ignore components that have failed for whatever reason // if they are important they will be picked up when the web deployment actually starts final List<ServiceName> components = deploymentUnit.getAttachmentList(WebComponentDescription.WEB_COMPONENTS); final Set<ServiceName> failed = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.FAILED_COMPONENTS); for (final ServiceName component : components) { boolean skip = false; if (!failed.contains(component)) { dependentComponents.add(component); } } ComponentRegistry componentRegistry = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.COMPONENT_REGISTRY); if (componentRegistry == null) { //we do this to avoid lots of other null checks //this will only happen if the EE subsystem is not installed componentRegistry = new ComponentRegistry(null); } final WebInjectionContainer injectionContainer = new WebInjectionContainer(module.getClassLoader(), componentRegistry); DeploymentInfo deploymentInfo = createServletConfig(metaData, deploymentUnit, module, deploymentClassIndex, injectionContainer, componentRegistry, scisMetaData, deploymentRoot); final String pathName = pathNameOfDeployment(deploymentUnit, metaData); deploymentInfo.setContextPath(pathName); String metaDataSecurityDomain = metaData.getSecurityDomain(); if (metaDataSecurityDomain == null) { metaDataSecurityDomain = getJBossAppSecurityDomain(deploymentUnit); } if (metaDataSecurityDomain != null) { metaDataSecurityDomain = metaDataSecurityDomain.trim(); } String securityDomain = metaDataSecurityDomain == null ? SecurityConstants.DEFAULT_APPLICATION_POLICY : SecurityUtil .unprefixSecurityDomain(metaDataSecurityDomain); final ServiceName deploymentServiceName = UndertowService.deploymentServiceName(hostName, deploymentInfo.getContextPath()); final ServiceName hostServiceName = UndertowService.virtualHostName(defaultServer, hostName); final UndertowDeploymentService service = new UndertowDeploymentService(deploymentInfo, injectionContainer, module, warMetaData.getMergedJBossWebMetaData()); final ServiceBuilder<UndertowDeploymentService> builder = serviceTarget.addService(deploymentServiceName, service) .addDependencies(dependentComponents) .addDependency(UndertowService.SERVLET_CONTAINER.append(defaultContainer), ServletContainerService.class, service.getContainer()) .addDependency(hostServiceName, Host.class, service.getHost()) .addDependency(SecurityDomainService.SERVICE_NAME.append(securityDomain), SecurityDomainContext.class, service.getSecurityDomainContextValue()) .addDependency(UndertowService.UNDERTOW, UndertowService.class, service.getUndertowService()); deploymentUnit.addToAttachmentList(Attachments.DEPLOYMENT_COMPLETE_SERVICES, deploymentServiceName); // add any dependencies required by the setup action for (final SetupAction action : setupActions) { builder.addDependencies(action.dependencies()); deploymentInfo.addThreadSetupAction(new ThreadSetupAction() { @Override public Handle setup(final HttpServerExchange exchange) { action.setup(Collections.<String, Object>emptyMap()); return new Handle() { @Override public void tearDown() { action.teardown(Collections.<String, Object>emptyMap()); } }; } }); } if (metaData.getDistributable() != null) { DistributedCacheManagerFactoryService factoryService = new DistributedCacheManagerFactoryService(); DistributedCacheManagerFactory factory = factoryService.getValue(); if (factory != null) { ServiceName factoryServiceName = deploymentServiceName.append("session"); builder.addDependency(ServiceBuilder.DependencyType.OPTIONAL, factoryServiceName, DistributedCacheManagerFactory.class, service.getDistributedCacheManagerFactoryInjectedValue()); ServiceBuilder<DistributedCacheManagerFactory> factoryBuilder = serviceTarget.addService(factoryServiceName, factoryService); boolean enabled = factory.addDeploymentDependencies(deploymentServiceName, deploymentUnit.getServiceRegistry(), serviceTarget, factoryBuilder, metaData); factoryBuilder.setInitialMode(enabled ? Mode.ON_DEMAND : Mode.NEVER).install(); } } // OSGi web applications are activated in {@link WebContextActivationProcessor} according to bundle lifecycle changes if (deploymentUnit.hasAttachment(Attachments.OSGI_MANIFEST)) { builder.setInitialMode(Mode.NEVER); UndertowDeploymentService.ContextActivatorImpl activator = new UndertowDeploymentService.ContextActivatorImpl(builder.install()); deploymentUnit.putAttachment(ContextActivator.ATTACHMENT_KEY, activator); } else { builder.setInitialMode(Mode.ACTIVE); builder.install(); } // Process the web related mgmt information final ModelNode node = deploymentUnit.getDeploymentSubsystemModel(UndertowExtension.SUBSYSTEM_NAME); node.get(DeploymentDefinition.CONTEXT_ROOT.getName()).set("".equals(pathName) ? "/" : pathName); node.get(DeploymentDefinition.VIRTUAL_HOST.getName()).set(hostName); processManagement(deploymentUnit, metaData); } static String pathNameOfDeployment(final DeploymentUnit deploymentUnit, final JBossWebMetaData metaData) { String pathName; if (metaData.getContextRoot() == null) { final EEModuleDescription description = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION); if (description != null) { // if there is a EEModuleDescription we need to take into account that the module name may have been overridden pathName = "/" + description.getModuleName(); } else { pathName = "/" + deploymentUnit.getName().substring(0, deploymentUnit.getName().length() - 4); } } else { pathName = metaData.getContextRoot(); if ("/".equals(pathName)) { pathName = ""; } else if (pathName.length() > 0 && pathName.charAt(0) != '/') { pathName = "/" + pathName; } } return pathName; } void processManagement(final DeploymentUnit unit, JBossWebMetaData metaData) { for (final JBossServletMetaData servlet : metaData.getServlets()) { try { final String name = servlet.getName(); final ModelNode node = unit.createDeploymentSubModel(UndertowExtension.SUBSYSTEM_NAME, PathElement.pathElement("servlet", name)); node.get("servlet-class").set(servlet.getServletClass()); node.get("servlet-name").set(servlet.getServletName()); } catch (Exception e) { // Should a failure in creating the mgmt view also make to the deployment to fail? continue; } } } private DeploymentInfo createServletConfig(final JBossWebMetaData mergedMetaData, final DeploymentUnit deploymentUnit, final Module module, final DeploymentClassIndex classReflectionIndex, final WebInjectionContainer injectionContainer, final ComponentRegistry componentRegistry, final ScisMetaData scisMetaData, final VirtualFile deploymentRoot) throws DeploymentUnitProcessingException { try { mergedMetaData.resolveAnnotations(); final DeploymentInfo d = new DeploymentInfo(); d.setContextPath(mergedMetaData.getContextRoot()); if (mergedMetaData.getDescriptionGroup() != null) { d.setDisplayName(mergedMetaData.getDescriptionGroup().getDisplayName()); } d.setDeploymentName(deploymentUnit.getName()); d.setResourceLoader(new DeploymentResourceLoader(deploymentRoot)); d.setClassLoader(module.getClassLoader()); final String servletVersion = mergedMetaData.getServletVersion(); if(servletVersion != null) { d.setMajorVersion(Integer.parseInt(servletVersion.charAt(0) + "")); d.setMinorVersion(Integer.parseInt(servletVersion.charAt(2) + "")); } else { d.setMajorVersion(3); d.setMajorVersion(1); } //for 2.2 apps we do not require a leading / in path mappings boolean is22OrOlder; if (d.getMajorVersion() == 1) { is22OrOlder = true; } else if (d.getMajorVersion() == 2) { is22OrOlder = d.getMinorVersion() < 3; } else { is22OrOlder = false; } HashMap<String, TagLibraryInfo> tldInfo = createTldsInfo(deploymentUnit, classReflectionIndex, componentRegistry, d); HashMap<String, JspPropertyGroup> propertyGroups = createJspConfig(mergedMetaData); JspServletBuilder.setupDeployment(d, propertyGroups, tldInfo, new UndertowJSPInstanceManager(injectionContainer)); d.setJspConfigDescriptor(new JspConfigDescriptorImpl(tldInfo.values(), propertyGroups.values())); d.setDefaultServletConfig(new DefaultServletConfig(true, Collections.<String>emptySet())); //default JSP servlet final ServletInfo jspServlet = new ServletInfo("Default JSP Servlet", JspServlet.class) .addMapping("*.jsp") .addMapping("*.jspx") .addInitParam("development", "false"); //todo: make configurable d.addServlet(jspServlet); final Set<String> jspPropertyGroupMappings = propertyGroups.keySet(); for (final String mapping : jspPropertyGroupMappings) { jspServlet.addMapping(mapping); } //TODO: do this properly d.setClassIntrospecter(new ClassIntrospecter() { @Override public <T> InstanceFactory<T> createInstanceFactory(final Class<T> clazz) { try { return new ConstructorInstanceFactory<>(clazz.getDeclaredConstructor()); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } }); final Map<String, List<ServletMappingMetaData>> servletMappings = new HashMap<>(); if (mergedMetaData.getServletMappings() != null) { for (final ServletMappingMetaData mapping : mergedMetaData.getServletMappings()) { List<ServletMappingMetaData> list = servletMappings.get(mapping.getServletName()); if (list == null) { servletMappings.put(mapping.getServletName(), list = new ArrayList<>()); } list.add(mapping); } } final Set<String> seenMappings = new HashSet<>(jspPropertyGroupMappings); if (mergedMetaData.getServlets() != null) { for (final JBossServletMetaData servlet : mergedMetaData.getServlets()) { final ServletInfo s; if (servlet.getJspFile() != null) { //TODO: real JSP support s = new ServletInfo(servlet.getName(), JspServlet.class); s.addHandlerChainWrapper(new JspFileWrapper(servlet.getJspFile())); } else { Class<? extends Servlet> servletClass = (Class<? extends Servlet>) classReflectionIndex.classIndex(servlet.getServletClass()).getModuleClass(); ComponentRegistry.ComponentManagedReferenceFactory creator = componentRegistry.getComponentsByClass().get(servletClass); if(creator != null) { InstanceFactory<Servlet> factory = createInstanceFactory(creator); s = new ServletInfo(servlet.getName(), servletClass, factory); } else { s = new ServletInfo(servlet.getName(), servletClass); } } s.setAsyncSupported(servlet.isAsyncSupported()) .setJspFile(servlet.getJspFile()) .setEnabled(servlet.isEnabled()); if (servlet.getRunAs() != null) { s.setRunAs(servlet.getRunAs().getRoleName()); } if (servlet.getLoadOnStartupSet()){//todo why not cleanup api and just use int everywhere s.setLoadOnStartup(servlet.getLoadOnStartupInt()); } List<ServletMappingMetaData> mappings = servletMappings.get(servlet.getName()); if (mappings != null) { for (ServletMappingMetaData mapping : mappings) { for (String pattern : mapping.getUrlPatterns()) { if (is22OrOlder && !pattern.startsWith("*") && !pattern.startsWith("/")) { pattern = "/" + pattern; } if (!seenMappings.contains(pattern)) { s.addMapping(pattern); seenMappings.add(pattern); } } } } if (servlet.getInitParam() != null) { for (ParamValueMetaData initParam : servlet.getInitParam()) { if (!s.getInitParams().containsKey(initParam.getParamName())) { s.addInitParam(initParam.getParamName(), initParam.getParamValue()); } } } if (servlet.getServletSecurity() != null) { ServletSecurityInfo securityInfo = new ServletSecurityInfo(); s.setServletSecurityInfo(securityInfo); securityInfo.setEmptyRoleSemantic(servlet.getServletSecurity().getEmptyRoleSemantic() == EmptyRoleSemanticType.PERMIT ? PERMIT : DENY) .setTransportGuaranteeType(transportGuaranteeType(servlet.getServletSecurity().getTransportGuarantee())) .addRolesAllowed(servlet.getServletSecurity().getRolesAllowed()); if (servlet.getServletSecurity().getHttpMethodConstraints() != null) { for (HttpMethodConstraintMetaData method : servlet.getServletSecurity().getHttpMethodConstraints()) { securityInfo.addHttpMethodSecurityInfo( new HttpMethodSecurityInfo() .setEmptyRoleSemantic(method.getEmptyRoleSemantic() == EmptyRoleSemanticType.PERMIT ? PERMIT : DENY) .setTransportGuaranteeType(transportGuaranteeType(method.getTransportGuarantee())) .addRolesAllowed(method.getRolesAllowed()) .setMethod(method.getMethod())); } } } if (servlet.getSecurityRoleRefs() != null) { for (final SecurityRoleRefMetaData ref : servlet.getSecurityRoleRefs()) { s.addSecurityRoleRef(ref.getRoleName(), ref.getRoleLink()); } } d.addServlet(s); } } if (mergedMetaData.getFilters() != null) { for (final FilterMetaData filter : mergedMetaData.getFilters()) { Class<? extends Filter> filterClass = (Class<? extends Filter>) classReflectionIndex.classIndex(filter.getFilterClass()).getModuleClass(); ComponentRegistry.ComponentManagedReferenceFactory creator = componentRegistry.getComponentsByClass().get(filterClass); FilterInfo f; if (creator != null) { InstanceFactory<Filter> instanceFactory = createInstanceFactory(creator); f = new FilterInfo(filter.getName(), filterClass, instanceFactory); } else { f = new FilterInfo(filter.getName(), filterClass); } f.setAsyncSupported(filter.isAsyncSupported()); d.addFilter(f); if (filter.getInitParam() != null) { for (ParamValueMetaData initParam : filter.getInitParam()) { f.addInitParam(initParam.getParamName(), initParam.getParamValue()); } } } } if (mergedMetaData.getFilterMappings() != null) { for (final FilterMappingMetaData mapping : mergedMetaData.getFilterMappings()) { if (mapping.getUrlPatterns() != null) { for (String url : mapping.getUrlPatterns()) { if (is22OrOlder && !url.startsWith("*") && !url.startsWith("/")) { url = "/" + url; } if (mapping.getDispatchers() != null && !mapping.getDispatchers().isEmpty()) { for (DispatcherType dispatcher : mapping.getDispatchers()) { d.addFilterUrlMapping(mapping.getFilterName(), url, javax.servlet.DispatcherType.valueOf(dispatcher.name())); } } else { d.addFilterUrlMapping(mapping.getFilterName(), url, javax.servlet.DispatcherType.REQUEST); } } } if (mapping.getServletNames() != null) { for (String servletName : mapping.getServletNames()) { if (mapping.getDispatchers() != null && !mapping.getDispatchers().isEmpty()) { for (DispatcherType dispatcher : mapping.getDispatchers()) { d.addFilterServletNameMapping(mapping.getFilterName(), servletName, javax.servlet.DispatcherType.valueOf(dispatcher.name())); } } else { d.addFilterServletNameMapping(mapping.getFilterName(), servletName, javax.servlet.DispatcherType.REQUEST); } } } } } if(scisMetaData != null && scisMetaData.getHandlesTypes() != null) { for (final Map.Entry<ServletContainerInitializer, Set<Class<?>>> sci : scisMetaData.getHandlesTypes().entrySet()) { final ImmediateInstanceFactory<ServletContainerInitializer> instanceFactory = new ImmediateInstanceFactory<>(sci.getKey()); d.addServletContainerInitalizer(new ServletContainerInitializerInfo(sci.getKey().getClass(), instanceFactory, sci.getValue())); } } if (mergedMetaData.getListeners() != null) { for (ListenerMetaData listener : mergedMetaData.getListeners()) { addListener(classReflectionIndex, componentRegistry, d, listener); } } if (mergedMetaData.getContextParams() != null) { for (ParamValueMetaData param : mergedMetaData.getContextParams()) { d.addInitParameter(param.getParamName(), param.getParamValue()); } } if (mergedMetaData.getWelcomeFileList() != null && mergedMetaData.getWelcomeFileList().getWelcomeFiles() != null) { d.addWelcomePages(mergedMetaData.getWelcomeFileList().getWelcomeFiles()); } else { d.addWelcomePages("index.html", "index.htm", "index.jsp"); } if (mergedMetaData.getErrorPages() != null) { for (final ErrorPageMetaData page : mergedMetaData.getErrorPages()) { final ErrorPage errorPage; if (page.getExceptionType() == null || page.getExceptionType().isEmpty()) { errorPage = new ErrorPage(page.getLocation(), Integer.parseInt(page.getErrorCode())); } else { errorPage = new ErrorPage(page.getLocation(), (Class<? extends Throwable>) classReflectionIndex.classIndex(page.getExceptionType()).getModuleClass()); } d.addErrorPages(errorPage); } } if (mergedMetaData.getMimeMappings() != null) { for (final MimeMappingMetaData mapping : mergedMetaData.getMimeMappings()) { d.addMimeMapping(new MimeMapping(mapping.getExtension(), mapping.getMimeType())); } } if (mergedMetaData.getSecurityConstraints() != null) { for (SecurityConstraintMetaData constraint : mergedMetaData.getSecurityConstraints()) { SecurityConstraint securityConstraint = new SecurityConstraint() .setTransportGuaranteeType(transportGuaranteeType(constraint.getTransportGuarantee())) .addRolesAllowed(constraint.getRoleNames()); if (constraint.getAuthConstraint() == null) { //no auth constraint means we permit the empty roles securityConstraint.setEmptyRoleSemantic(PERMIT); } if (constraint.getResourceCollections() != null) { for (final WebResourceCollectionMetaData resourceCollection : constraint.getResourceCollections()) { securityConstraint.addWebResourceCollection(new WebResourceCollection() .addHttpMethods(resourceCollection.getHttpMethods()) .addHttpMethodOmissions(resourceCollection.getHttpMethodOmissions()) .addUrlPatterns(resourceCollection.getUrlPatterns())); } } d.addSecurityConstraint(securityConstraint); } } final LoginConfigMetaData loginConfig = mergedMetaData.getLoginConfig(); if (loginConfig != null) { String authMethod = authMethod(loginConfig.getAuthMethod()); if (loginConfig.getFormLoginConfig() != null) { d.setLoginConfig(new LoginConfig(authMethod, loginConfig.getRealmName(), loginConfig.getFormLoginConfig().getLoginPage(), loginConfig.getFormLoginConfig().getErrorPage())); } else { d.setLoginConfig(new LoginConfig(authMethod, loginConfig.getRealmName())); } } d.addSecurityRoles(mergedMetaData.getSecurityRoleNames()); if (mergedMetaData.getSecurityDomain() != null) { String contextId = deploymentUnit.getName(); if (deploymentUnit.getParent() != null) { contextId = deploymentUnit.getParent().getName() + "!" + contextId; } d.addOuterHandlerChainWrapper(SecurityContextCreationHandler.wrapper(mergedMetaData.getSecurityDomain())); d.addDispatchedHandlerChainWrapper(SecurityContextAssociationHandler.wrapper(mergedMetaData.getPrincipalVersusRolesMap(), contextId)); } // Setup an deployer configured ServletContext attributes final List<ServletContextAttribute> attributes = deploymentUnit.getAttachmentList(ServletContextAttribute.ATTACHMENT_KEY); for (ServletContextAttribute attribute : attributes) { d.addServletContextAttribute(attribute.getName(), attribute.getValue()); } if (mergedMetaData.getLocalEncodings() != null && mergedMetaData.getLocalEncodings().getMappings() != null) { for (LocaleEncodingMetaData locale : mergedMetaData.getLocalEncodings().getMappings()) { d.addLocaleCharsetMapping(locale.getLocale(), locale.getEncoding()); } } return d; } catch (ClassNotFoundException e) { throw new DeploymentUnitProcessingException(e); } } /** * Convert the authentication method name from the format specified in the web.xml to the format used by * {@link HttpServletRequest}. * * If the auth method is not recognised then it is returned as-is. * * @return The converted auth method. * @throws NullPointerException if no configuredMethod is supplied. */ private String authMethod(String configuredMethod) { // TODO - Feels like a candidate for an enum but will hold off until configuration of custom methods and chaining is // defined. if (configuredMethod.equals("CLIENT-CERT")) { return HttpServletRequest.CLIENT_CERT_AUTH; } return configuredMethod; } private io.undertow.servlet.api.TransportGuaranteeType transportGuaranteeType(final TransportGuaranteeType type) { if (type == null) { return io.undertow.servlet.api.TransportGuaranteeType.NONE; } switch (type) { case CONFIDENTIAL: return io.undertow.servlet.api.TransportGuaranteeType.CONFIDENTIAL; case INTEGRAL: return io.undertow.servlet.api.TransportGuaranteeType.INTEGRAL; case NONE: return io.undertow.servlet.api.TransportGuaranteeType.NONE; } throw new RuntimeException("UNREACHABLE"); } private HashMap<String, JspPropertyGroup> createJspConfig(JBossWebMetaData metaData) { final HashMap<String, JspPropertyGroup> result = new HashMap<>(); // JSP Config JspConfigMetaData config = metaData.getJspConfig(); if (config != null) { // JSP Property groups List<JspPropertyGroupMetaData> groups = config.getPropertyGroups(); if (groups != null) { for (JspPropertyGroupMetaData group : groups) { org.apache.jasper.deploy.JspPropertyGroup jspPropertyGroup = new org.apache.jasper.deploy.JspPropertyGroup(); for (String pattern : group.getUrlPatterns()) { jspPropertyGroup.addUrlPattern(pattern); } jspPropertyGroup.setElIgnored(group.getElIgnored()); jspPropertyGroup.setPageEncoding(group.getPageEncoding()); jspPropertyGroup.setScriptingInvalid(group.getScriptingInvalid()); jspPropertyGroup.setIsXml(group.getIsXml()); if (group.getIncludePreludes() != null) { for (String includePrelude : group.getIncludePreludes()) { jspPropertyGroup.addIncludePrelude(includePrelude); } } if (group.getIncludeCodas() != null) { for (String includeCoda : group.getIncludeCodas()) { jspPropertyGroup.addIncludeCoda(includeCoda); } } jspPropertyGroup.setDeferredSyntaxAllowedAsLiteral(group.getDeferredSyntaxAllowedAsLiteral()); jspPropertyGroup.setTrimDirectiveWhitespaces(group.getTrimDirectiveWhitespaces()); jspPropertyGroup.setDefaultContentType(group.getDefaultContentType()); jspPropertyGroup.setBuffer(group.getBuffer()); jspPropertyGroup.setErrorOnUndeclaredNamespace(group.getErrorOnUndeclaredNamespace()); for (String pattern : jspPropertyGroup.getUrlPatterns()) { // Split off the groups to individual mappings result.put(pattern, jspPropertyGroup); } } } } //it looks like jasper needs these in order of least specified to most specific final LinkedHashMap<String, JspPropertyGroup> ret = new LinkedHashMap<>(); final ArrayList<String> paths = new ArrayList<>(result.keySet()); Collections.sort(paths, new Comparator<String>() { @Override public int compare(final String o1, final String o2) { return o1.length() - o2.length(); } }); for (String path : paths) { ret.put(path, result.get(path)); } return ret; } private HashMap<String, TagLibraryInfo> createTldsInfo(final DeploymentUnit deploymentUnit, final DeploymentClassIndex classReflectionIndex, final ComponentRegistry components, final DeploymentInfo d) throws ClassNotFoundException { TldsMetaData tldsMetaData = deploymentUnit.getAttachment(TldsMetaData.ATTACHMENT_KEY); final HashMap<String, TagLibraryInfo> ret = new HashMap<>(); if (tldsMetaData != null) { if (tldsMetaData.getTlds() != null) { for (Map.Entry<String, TldMetaData> tld : tldsMetaData.getTlds().entrySet()) { createTldInfo(tld.getKey(), tld.getValue(), ret, classReflectionIndex, components, d); } } List<TldMetaData> sharedTlds = tldsMetaData.getSharedTlds(deploymentUnit); if (sharedTlds != null) { for (TldMetaData metaData : sharedTlds) { createTldInfo(null, metaData, ret, classReflectionIndex, components, d); } } } return ret; } private TagLibraryInfo createTldInfo(final String location, final TldMetaData tldMetaData, final HashMap<String, TagLibraryInfo> ret, final DeploymentClassIndex classReflectionIndex, final ComponentRegistry components, final DeploymentInfo d) throws ClassNotFoundException { String relativeLocation = location; String jarPath = null; if (relativeLocation != null && relativeLocation.startsWith("/WEB-INF/lib/")) { int pos = relativeLocation.indexOf('/', "/WEB-INF/lib/".length()); if (pos > 0) { jarPath = relativeLocation.substring(pos); if (jarPath.startsWith("/")) { jarPath = jarPath.substring(1); } relativeLocation = relativeLocation.substring(0, pos); } } TagLibraryInfo tagLibraryInfo = new TagLibraryInfo(); tagLibraryInfo.setTlibversion(tldMetaData.getTlibVersion()); if (tldMetaData.getJspVersion() == null) { tagLibraryInfo.setJspversion(tldMetaData.getVersion()); } else { tagLibraryInfo.setJspversion(tldMetaData.getJspVersion()); } tagLibraryInfo.setShortname(tldMetaData.getShortName()); tagLibraryInfo.setUri(tldMetaData.getUri()); if (tldMetaData.getDescriptionGroup() != null) { tagLibraryInfo.setInfo(tldMetaData.getDescriptionGroup().getDescription()); } // Validator if (tldMetaData.getValidator() != null) { TagLibraryValidatorInfo tagLibraryValidatorInfo = new TagLibraryValidatorInfo(); tagLibraryValidatorInfo.setValidatorClass(tldMetaData.getValidator().getValidatorClass()); if (tldMetaData.getValidator().getInitParams() != null) { for (ParamValueMetaData paramValueMetaData : tldMetaData.getValidator().getInitParams()) { tagLibraryValidatorInfo.addInitParam(paramValueMetaData.getParamName(), paramValueMetaData.getParamValue()); } } tagLibraryInfo.setValidator(tagLibraryValidatorInfo); } // Tag if (tldMetaData.getTags() != null) { for (TagMetaData tagMetaData : tldMetaData.getTags()) { TagInfo tagInfo = new TagInfo(); tagInfo.setTagName(tagMetaData.getName()); tagInfo.setTagClassName(tagMetaData.getTagClass()); tagInfo.setTagExtraInfo(tagMetaData.getTeiClass()); if (tagMetaData.getBodyContent() != null) { tagInfo.setBodyContent(tagMetaData.getBodyContent().toString()); } tagInfo.setDynamicAttributes(tagMetaData.getDynamicAttributes()); // Description group if (tagMetaData.getDescriptionGroup() != null) { DescriptionGroupMetaData descriptionGroup = tagMetaData.getDescriptionGroup(); if (descriptionGroup.getIcons() != null && descriptionGroup.getIcons().value() != null && (descriptionGroup.getIcons().value().length > 0)) { Icon icon = descriptionGroup.getIcons().value()[0]; tagInfo.setLargeIcon(icon.largeIcon()); tagInfo.setSmallIcon(icon.smallIcon()); } tagInfo.setInfoString(descriptionGroup.getDescription()); tagInfo.setDisplayName(descriptionGroup.getDisplayName()); } // Variable if (tagMetaData.getVariables() != null) { for (VariableMetaData variableMetaData : tagMetaData.getVariables()) { TagVariableInfo tagVariableInfo = new TagVariableInfo(); tagVariableInfo.setNameGiven(variableMetaData.getNameGiven()); tagVariableInfo.setNameFromAttribute(variableMetaData.getNameFromAttribute()); tagVariableInfo.setClassName(variableMetaData.getVariableClass()); tagVariableInfo.setDeclare(variableMetaData.getDeclare()); if (variableMetaData.getScope() != null) { tagVariableInfo.setScope(variableMetaData.getScope().toString()); } tagInfo.addTagVariableInfo(tagVariableInfo); } } // Attribute if (tagMetaData.getAttributes() != null) { for (AttributeMetaData attributeMetaData : tagMetaData.getAttributes()) { TagAttributeInfo tagAttributeInfo = new TagAttributeInfo(); tagAttributeInfo.setName(attributeMetaData.getName()); tagAttributeInfo.setType(attributeMetaData.getType()); tagAttributeInfo.setReqTime(attributeMetaData.getRtexprvalue()); tagAttributeInfo.setRequired(attributeMetaData.getRequired()); tagAttributeInfo.setFragment(attributeMetaData.getFragment()); if (attributeMetaData.getDeferredValue() != null) { tagAttributeInfo.setDeferredValue("true"); tagAttributeInfo.setExpectedTypeName(attributeMetaData.getDeferredValue().getType()); } else { tagAttributeInfo.setDeferredValue("false"); } if (attributeMetaData.getDeferredMethod() != null) { tagAttributeInfo.setDeferredMethod("true"); tagAttributeInfo.setMethodSignature(attributeMetaData.getDeferredMethod().getMethodSignature()); } else { tagAttributeInfo.setDeferredMethod("false"); } tagInfo.addTagAttributeInfo(tagAttributeInfo); } } tagLibraryInfo.addTagInfo(tagInfo); } } // Tag files if (tldMetaData.getTagFiles() != null) { for (TagFileMetaData tagFileMetaData : tldMetaData.getTagFiles()) { TagFileInfo tagFileInfo = new TagFileInfo(); tagFileInfo.setName(tagFileMetaData.getName()); tagFileInfo.setPath(tagFileMetaData.getPath()); tagLibraryInfo.addTagFileInfo(tagFileInfo); } } // Function if (tldMetaData.getFunctions() != null) { for (FunctionMetaData functionMetaData : tldMetaData.getFunctions()) { FunctionInfo functionInfo = new FunctionInfo(); functionInfo.setName(functionMetaData.getName()); functionInfo.setFunctionClass(functionMetaData.getFunctionClass()); functionInfo.setFunctionSignature(functionMetaData.getFunctionSignature()); tagLibraryInfo.addFunctionInfo(functionInfo); } } if (jarPath == null && relativeLocation == null) { if (!ret.containsKey(tagLibraryInfo.getUri())) { ret.put(tagLibraryInfo.getUri(), tagLibraryInfo); } } else if (jarPath == null) { tagLibraryInfo.setLocation(""); tagLibraryInfo.setPath(relativeLocation); if (!ret.containsKey(tagLibraryInfo.getUri())) { ret.put(tagLibraryInfo.getUri(), tagLibraryInfo); } ret.put(relativeLocation, tagLibraryInfo); } else { tagLibraryInfo.setLocation(relativeLocation); tagLibraryInfo.setPath(jarPath); if (!ret.containsKey(tagLibraryInfo.getUri())) { ret.put(tagLibraryInfo.getUri(), tagLibraryInfo); } if (jarPath.equals("META-INF/taglib.tld")) { ret.put(relativeLocation, tagLibraryInfo); } } return tagLibraryInfo; } private void addListener(final DeploymentClassIndex classReflectionIndex, final ComponentRegistry components, final DeploymentInfo d, final ListenerMetaData listener) throws ClassNotFoundException { ListenerInfo l; final Class<? extends EventListener> listenerClass = (Class<? extends EventListener>) classReflectionIndex.classIndex(listener.getListenerClass()).getModuleClass(); ComponentRegistry.ComponentManagedReferenceFactory creator = components.getComponentsByClass().get(listenerClass); if (creator != null) { InstanceFactory<EventListener> factory = createInstanceFactory(creator); l = new ListenerInfo(listenerClass, factory); } else { l = new ListenerInfo(listenerClass); } d.addListener(l); } private <T> InstanceFactory<T> createInstanceFactory(final ComponentRegistry.ComponentManagedReferenceFactory creator) { return new InstanceFactory<T>() { @Override public InstanceHandle<T> createInstance() throws InstantiationException { final ManagedReference instance = creator.getReference(); return new InstanceHandle<T>() { @Override public T getInstance() { return (T) instance.getInstance(); } @Override public void release() { instance.release(); } }; } }; } /** * Try to obtain the security domain configured in jboss-app.xml at the ear level if available */ private String getJBossAppSecurityDomain(final DeploymentUnit deploymentUnit) { String securityDomain = null; DeploymentUnit parent = deploymentUnit.getParent(); if (parent != null) { final EarMetaData jbossAppMetaData = parent.getAttachment(org.jboss.as.ee.structure.Attachments.EAR_METADATA); if (jbossAppMetaData instanceof JBossAppMetaData) { securityDomain = ((JBossAppMetaData) jbossAppMetaData).getSecurityDomain(); } } return securityDomain; } }