/* * 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.server.deployment.module; import java.io.IOException; import java.security.Permission; import java.security.Permissions; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Enumeration; import java.util.List; import java.util.PropertyPermission; import org.jboss.as.server.logging.ServerLogger; 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.SubDeploymentMarker; import org.jboss.as.server.moduleservice.ModuleDefinition; import org.jboss.as.server.moduleservice.ModuleLoadService; import org.jboss.as.server.moduleservice.ModuleResolvePhaseService; import org.jboss.as.server.moduleservice.ServiceModuleLoader; import org.jboss.modules.DependencySpec; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleSpec; import org.jboss.modules.ResourceLoaderSpec; import org.jboss.modules.filter.MultiplePathFilterBuilder; import org.jboss.modules.filter.PathFilter; import org.jboss.modules.filter.PathFilters; import org.jboss.modules.security.FactoryPermissionCollection; import org.jboss.modules.security.ImmediatePermissionFactory; import org.jboss.modules.security.PermissionFactory; import org.jboss.msc.service.ServiceController.Mode; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ValueService; import org.jboss.msc.value.ImmediateValue; import org.jboss.vfs.VirtualFile; import org.jboss.vfs.VirtualFilePermission; /** * Processor responsible for creating the module spec service for this deployment. Once the module spec service is created the * module can be loaded by {@link ServiceModuleLoader}. * * @author John Bailey * @author Stuart Douglas * @author Marius Bogoevici * @author Thomas.Diesler@jboss.com */ public class ModuleSpecProcessor implements DeploymentUnitProcessor { private static final ServerLogger logger = ServerLogger.DEPLOYMENT_LOGGER; @Override public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); if (deploymentUnit.hasAttachment(Attachments.MODULE)) return; // No {@link ModuleSpec} creation for OSGi deployments if (deploymentUnit.hasAttachment(Attachments.OSGI_MANIFEST)) return; deployModuleSpec(phaseContext); } @Override public void undeploy(final DeploymentUnit deploymentUnit) { } private void deployModuleSpec(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final DeploymentUnit topLevelDeployment = deploymentUnit.getParent() == null ? deploymentUnit : deploymentUnit.getParent(); final ResourceRoot mainRoot = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT); if (mainRoot == null) return; // Add internal resource roots final ModuleSpecification moduleSpec = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION); final List<ResourceRoot> resourceRoots = new ArrayList<ResourceRoot>(); if (ModuleRootMarker.isModuleRoot(mainRoot)) { resourceRoots.add(mainRoot); } final List<ResourceRoot> additionalRoots = deploymentUnit.getAttachmentList(Attachments.RESOURCE_ROOTS); for (final ResourceRoot additionalRoot : additionalRoots) { if (ModuleRootMarker.isModuleRoot(additionalRoot) && !SubDeploymentMarker.isSubDeployment(additionalRoot)) { resourceRoots.add(additionalRoot); } } final ModuleIdentifier moduleIdentifier = deploymentUnit.getAttachment(Attachments.MODULE_IDENTIFIER); if (moduleIdentifier == null) { throw ServerLogger.ROOT_LOGGER.noModuleIdentifier(deploymentUnit.getName()); } final List<AdditionalModuleSpecification> additionalModules = topLevelDeployment.getAttachmentList(Attachments.ADDITIONAL_MODULES); // create the module service and set it to attach to the deployment in the next phase final ServiceName moduleServiceName = createModuleService(phaseContext, deploymentUnit, resourceRoots, moduleSpec, moduleIdentifier); phaseContext.addDeploymentDependency(moduleServiceName, Attachments.MODULE); for (final DeploymentUnit subDeployment : deploymentUnit.getAttachmentList(Attachments.SUB_DEPLOYMENTS)) { ModuleIdentifier moduleId = subDeployment.getAttachment(Attachments.MODULE_IDENTIFIER); if (moduleId != null) { phaseContext.addToAttachmentList(Attachments.NEXT_PHASE_DEPS, ServiceModuleLoader.moduleSpecServiceName(moduleId)); } } if (deploymentUnit.getParent() != null) { //they have already been added by the parent return; } for (final AdditionalModuleSpecification module : additionalModules) { addAllDependenciesAndPermissions(moduleSpec, module); List<ResourceRoot> roots = module.getResourceRoots(); ServiceName serviceName = createModuleService(phaseContext, deploymentUnit, roots, module, module.getModuleIdentifier()); phaseContext.addToAttachmentList(Attachments.NEXT_PHASE_DEPS, serviceName); } } /** * Gives any additional modules the same dependencies and permissions as the primary module. * <p/> * This makes sure they can access all API classes etc. * * @param moduleSpecification The primary module spec * @param module The additional module */ private void addAllDependenciesAndPermissions(final ModuleSpecification moduleSpecification, final AdditionalModuleSpecification module) { module.addSystemDependencies(moduleSpecification.getSystemDependencies()); module.addLocalDependencies(moduleSpecification.getLocalDependencies()); for(ModuleDependency dep : moduleSpecification.getUserDependencies()) { if(!dep.getIdentifier().equals(module.getModuleIdentifier())) { module.addUserDependency(dep); } } for(PermissionFactory factory : moduleSpecification.getPermissionFactories()) { module.addPermissionFactory(factory); } } private static final Permissions DEFAULT_PERMISSIONS; static { final Permissions permissions = new Permissions(); permissions.add(new PropertyPermission("file.encoding", "read")); permissions.add(new PropertyPermission("file.separator", "read")); permissions.add(new PropertyPermission("java.class.version", "read")); permissions.add(new PropertyPermission("java.specification.version", "read")); permissions.add(new PropertyPermission("java.specification.vendor", "read")); permissions.add(new PropertyPermission("java.specification.name", "read")); permissions.add(new PropertyPermission("java.vendor", "read")); permissions.add(new PropertyPermission("java.vendor.url", "read")); permissions.add(new PropertyPermission("java.version", "read")); permissions.add(new PropertyPermission("java.vm.name", "read")); permissions.add(new PropertyPermission("java.vm.vendor", "read")); permissions.add(new PropertyPermission("java.vm.version", "read")); permissions.add(new PropertyPermission("line.separator", "read")); permissions.add(new PropertyPermission("os.name", "read")); permissions.add(new PropertyPermission("os.version", "read")); permissions.add(new PropertyPermission("os.arch", "read")); permissions.add(new PropertyPermission("path.separator", "read")); // these permissions are apparently non-standard, but there is no reason not to make them available if the above are permissions.add(new PropertyPermission("java.runtime.name", "read")); permissions.add(new PropertyPermission("java.runtime.version", "read")); permissions.add(new PropertyPermission("java.vendor.url.bug", "read")); permissions.add(new PropertyPermission("java.vm.info", "read")); permissions.add(new PropertyPermission("java.vm.specification.name", "read")); permissions.add(new PropertyPermission("java.vm.specification.vendor", "read")); permissions.add(new PropertyPermission("java.vm.specification.version", "read")); permissions.add(new PropertyPermission("sun.cpu.endian", "read")); permissions.add(new PropertyPermission("sun.cpu.isalist", "read")); permissions.add(new PropertyPermission("sun.management.compiler", "read")); permissions.setReadOnly(); DEFAULT_PERMISSIONS = permissions; } private ServiceName createModuleService(final DeploymentPhaseContext phaseContext, final DeploymentUnit deploymentUnit, final List<ResourceRoot> resourceRoots, final ModuleSpecification moduleSpecification, final ModuleIdentifier moduleIdentifier) throws DeploymentUnitProcessingException { logger.debugf("Creating module: %s", moduleIdentifier); final ModuleSpec.Builder specBuilder = ModuleSpec.build(moduleIdentifier); for (final DependencySpec dep : moduleSpecification.getModuleSystemDependencies()) { specBuilder.addDependency(dep); } final List<ModuleDependency> dependencies = moduleSpecification.getSystemDependencies(); final List<ModuleDependency> localDependencies = moduleSpecification.getLocalDependencies(); final List<ModuleDependency> userDependencies = moduleSpecification.getUserDependencies(); final List<PermissionFactory> permFactories = moduleSpecification.getPermissionFactories(); installAliases(moduleSpecification, moduleIdentifier, deploymentUnit, phaseContext); // add additional resource loaders first for (final ResourceLoaderSpec resourceLoaderSpec : moduleSpecification.getResourceLoaders()) { logger.debugf("Adding resource loader %s to module %s", resourceLoaderSpec, moduleIdentifier); specBuilder.addResourceRoot(resourceLoaderSpec); } for (final ResourceRoot resourceRoot : resourceRoots) { logger.debugf("Adding resource %s to module %s", resourceRoot.getRoot(), moduleIdentifier); addResourceRoot(specBuilder, resourceRoot, permFactories); } createDependencies(specBuilder, dependencies, false); createDependencies(specBuilder, userDependencies, false); if (moduleSpecification.isLocalLast()) { createDependencies(specBuilder, localDependencies, moduleSpecification.isLocalDependenciesTransitive()); specBuilder.addDependency(DependencySpec.createLocalDependencySpec()); } else { specBuilder.addDependency(DependencySpec.createLocalDependencySpec()); createDependencies(specBuilder, localDependencies, moduleSpecification.isLocalDependenciesTransitive()); } final Enumeration<Permission> e = DEFAULT_PERMISSIONS.elements(); while (e.hasMoreElements()) { permFactories.add(new ImmediatePermissionFactory(e.nextElement())); } // TODO: servlet context temp dir FilePermission FactoryPermissionCollection permissionCollection = new FactoryPermissionCollection(permFactories.toArray(new PermissionFactory[permFactories.size()])); specBuilder.setPermissionCollection(permissionCollection); deploymentUnit.putAttachment(Attachments.MODULE_PERMISSIONS, permissionCollection); final DelegatingClassFileTransformer delegatingClassFileTransformer = new DelegatingClassFileTransformer(); specBuilder.setClassFileTransformer(delegatingClassFileTransformer); deploymentUnit.putAttachment(DelegatingClassFileTransformer.ATTACHMENT_KEY, delegatingClassFileTransformer); final ModuleSpec moduleSpec = specBuilder.create(); final ServiceName moduleSpecServiceName = ServiceModuleLoader.moduleSpecServiceName(moduleIdentifier); ModuleDefinition moduleDefinition = new ModuleDefinition(moduleIdentifier, new HashSet<>(moduleSpecification.getAllDependencies()), moduleSpec); final ValueService<ModuleDefinition> moduleSpecService = new ValueService<>(new ImmediateValue<>(moduleDefinition)); phaseContext.getServiceTarget().addService(moduleSpecServiceName, moduleSpecService).addDependencies( deploymentUnit.getServiceName()).addDependencies(phaseContext.getPhaseServiceName()).setInitialMode( Mode.ON_DEMAND).install(); final List<ModuleDependency> allDependencies = new ArrayList<ModuleDependency>(); allDependencies.addAll(dependencies); allDependencies.addAll(localDependencies); allDependencies.addAll(userDependencies); ModuleResolvePhaseService.installService(phaseContext.getServiceTarget(), moduleDefinition); return ModuleLoadService.install(phaseContext.getServiceTarget(), moduleIdentifier, allDependencies); } private void installAliases(final ModuleSpecification moduleSpecification, final ModuleIdentifier moduleIdentifier, final DeploymentUnit deploymentUnit, final DeploymentPhaseContext phaseContext) { for (final ModuleIdentifier alias : moduleSpecification.getAliases()) { final ServiceName moduleSpecServiceName = ServiceModuleLoader.moduleSpecServiceName(alias); final ModuleSpec spec = ModuleSpec.buildAlias(alias, moduleIdentifier).create(); ModuleDefinition moduleDefinition = new ModuleDefinition(alias, new HashSet<>(moduleSpecification.getAllDependencies()), spec); final ValueService<ModuleDefinition> moduleSpecService = new ValueService<>(new ImmediateValue<>(moduleDefinition)); phaseContext.getServiceTarget().addService(moduleSpecServiceName, moduleSpecService).addDependencies( deploymentUnit.getServiceName()).addDependencies(phaseContext.getPhaseServiceName()).setInitialMode( Mode.ON_DEMAND).install(); ModuleLoadService.installService(phaseContext.getServiceTarget(), alias, Collections.singletonList(moduleIdentifier)); ModuleResolvePhaseService.installService(phaseContext.getServiceTarget(), moduleDefinition); } } private void createDependencies(final ModuleSpec.Builder specBuilder, final List<ModuleDependency> apiDependencies, final boolean requireTransitive) { if (apiDependencies != null) { for (final ModuleDependency dependency : apiDependencies) { final boolean export = requireTransitive ? true : dependency.isExport(); final List<FilterSpecification> importFilters = dependency.getImportFilters(); final List<FilterSpecification> exportFilters = dependency.getExportFilters(); final PathFilter importFilter; final PathFilter exportFilter; final MultiplePathFilterBuilder importBuilder = PathFilters.multiplePathFilterBuilder(true); for (final FilterSpecification filter : importFilters) { importBuilder.addFilter(filter.getPathFilter(), filter.isInclude()); } if (dependency.isImportServices()) { importBuilder.addFilter(PathFilters.getMetaInfServicesFilter(), true); } importBuilder.addFilter(PathFilters.getMetaInfSubdirectoriesFilter(), false); importBuilder.addFilter(PathFilters.getMetaInfFilter(), false); importFilter = importBuilder.create(); if (exportFilters.isEmpty()) { if (export) { exportFilter = PathFilters.acceptAll(); } else { exportFilter = PathFilters.rejectAll(); } } else { final MultiplePathFilterBuilder exportBuilder = PathFilters .multiplePathFilterBuilder(export); for (final FilterSpecification filter : exportFilters) { exportBuilder.addFilter(filter.getPathFilter(), filter.isInclude()); } exportFilter = exportBuilder.create(); } final DependencySpec depSpec = DependencySpec.createModuleDependencySpec(importFilter, exportFilter, dependency .getModuleLoader(), dependency.getIdentifier(), dependency.isOptional()); specBuilder.addDependency(depSpec); logger.debugf("Adding dependency %s to module %s", dependency, specBuilder.getIdentifier()); } } } private void addResourceRoot(final ModuleSpec.Builder specBuilder, final ResourceRoot resource, final List<PermissionFactory> permFactories) throws DeploymentUnitProcessingException { try { final VirtualFile root = resource.getRoot(); if (resource.getExportFilters().isEmpty()) { specBuilder.addResourceRoot(ResourceLoaderSpec.createResourceLoaderSpec(new VFSResourceLoader(resource .getRootName(), root, resource.isUsePhysicalCodeSource()))); } else { final MultiplePathFilterBuilder filterBuilder = PathFilters.multiplePathFilterBuilder(true); for (final FilterSpecification filter : resource.getExportFilters()) { filterBuilder.addFilter(filter.getPathFilter(), filter.isInclude()); } specBuilder.addResourceRoot(ResourceLoaderSpec.createResourceLoaderSpec(new VFSResourceLoader(resource .getRootName(), root, resource.isUsePhysicalCodeSource()), filterBuilder.create())); } // start with the root permFactories.add(new ImmediatePermissionFactory( new VirtualFilePermission(root.getPathName(), VirtualFilePermission.FLAG_READ))); // also include all children, recursively permFactories.add(new ImmediatePermissionFactory( new VirtualFilePermission(root.getChild("-").getPathName(), VirtualFilePermission.FLAG_READ))); } catch (IOException e) { throw ServerLogger.ROOT_LOGGER.failedToCreateVFSResourceLoader(resource.getRootName(), e); } } }