/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt 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.weld.deployment.processors;
import static org.jboss.as.weld.util.Utils.putIfValueNotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import javax.enterprise.inject.spi.Extension;
import org.jboss.as.ee.naming.JavaNamespaceSetup;
import org.jboss.as.ee.weld.WeldDeploymentMarker;
import org.jboss.as.naming.deployment.ContextNames;
import org.jboss.as.naming.deployment.JndiNamingDependencyProcessor;
import org.jboss.as.naming.service.DefaultNamespaceContextSelectorService;
import org.jboss.as.naming.service.NamingService;
import org.jboss.as.server.Services;
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.module.ModuleDependency;
import org.jboss.as.server.deployment.module.ModuleSpecification;
import org.jboss.as.server.deployment.module.ResourceRoot;
import org.jboss.as.weld.ServiceNames;
import org.jboss.as.weld.WeldBootstrapService;
import org.jboss.as.weld.WeldStartService;
import org.jboss.as.weld.deployment.BeanDeploymentArchiveImpl;
import org.jboss.as.weld.deployment.BeanDeploymentModule;
import org.jboss.as.weld.deployment.CdiAnnotationMarker;
import org.jboss.as.weld.deployment.WeldAttachments;
import org.jboss.as.weld.deployment.WeldDeployment;
import org.jboss.as.weld.deployment.WeldPortableExtensions;
import org.jboss.as.weld.logging.WeldLogger;
import org.jboss.as.weld.services.TCCLSingletonService;
import org.jboss.as.weld.services.bootstrap.WeldExecutorServices;
import org.jboss.as.weld.spi.BootstrapDependencyInstaller;
import org.jboss.as.weld.spi.DeploymentUnitDependenciesProvider;
import org.jboss.as.weld.spi.ModuleServicesProvider;
import org.jboss.as.weld.util.Reflections;
import org.jboss.as.weld.util.ServiceLoaders;
import org.jboss.as.weld.util.Utils;
import org.jboss.metadata.ear.spec.EarMetaData;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.weld.bootstrap.api.Service;
import org.jboss.weld.bootstrap.spi.EEModuleDescriptor;
import org.jboss.weld.bootstrap.spi.Metadata;
import org.jboss.weld.config.ConfigurationKey;
import org.jboss.weld.configuration.spi.ExternalConfiguration;
import org.jboss.weld.configuration.spi.helpers.ExternalConfigurationBuilder;
import org.jboss.weld.manager.api.ExecutorServices;
import org.jboss.weld.security.spi.SecurityServices;
import org.jboss.weld.transaction.spi.TransactionServices;
import org.wildfly.security.manager.WildFlySecurityManager;
/**
* Deployment processor that installs the weld services and all other required services
*
* @author Stuart Douglas
* @author <a href="mailto:tadamski@redhat.com">Tomasz Adamski</a>
*/
public class WeldDeploymentProcessor implements DeploymentUnitProcessor {
private final boolean jtsEnabled;
public WeldDeploymentProcessor(final boolean jtsEnabled) {
this.jtsEnabled = jtsEnabled;
}
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
final DeploymentUnit parent = Utils.getRootDeploymentUnit(deploymentUnit);
final ServiceTarget serviceTarget = phaseContext.getServiceTarget();
final ResourceRoot deploymentRoot = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT);
if (!WeldDeploymentMarker.isPartOfWeldDeployment(deploymentUnit)) {
//if there are CDI annotation present and this is the top level deployment we log a warning
if (deploymentUnit.getParent() == null && CdiAnnotationMarker.cdiAnnotationsPresent(deploymentUnit)) {
WeldLogger.DEPLOYMENT_LOGGER.cdiAnnotationsButNotBeanArchive(deploymentUnit.getName());
}
return;
}
//add a dependency on the weld service to web deployments
final ServiceName weldBootstrapServiceName = parent.getServiceName().append(WeldBootstrapService.SERVICE_NAME);
ServiceName weldStartServiceName = parent.getServiceName().append(WeldStartService.SERVICE_NAME);
deploymentUnit.addToAttachmentList(Attachments.WEB_DEPENDENCIES, weldStartServiceName);
final Set<ServiceName> dependencies = new HashSet<ServiceName>();
// we only start weld on top level deployments
if (deploymentUnit.getParent() != null) {
return;
}
WeldLogger.DEPLOYMENT_LOGGER.startingServicesForCDIDeployment(phaseContext.getDeploymentUnit().getName());
final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
final Set<BeanDeploymentArchiveImpl> beanDeploymentArchives = new HashSet<BeanDeploymentArchiveImpl>();
final Map<ModuleIdentifier, BeanDeploymentModule> bdmsByIdentifier = new HashMap<ModuleIdentifier, BeanDeploymentModule>();
final Map<ModuleIdentifier, ModuleSpecification> moduleSpecByIdentifier = new HashMap<ModuleIdentifier, ModuleSpecification>();
final Map<ModuleIdentifier, EEModuleDescriptor> eeModuleDescriptors = new HashMap<>();
// the root module only has access to itself. For most deployments this will be the only module
// for ear deployments this represents the ear/lib directory.
// war and jar deployment visibility will depend on the dependencies that
// exist in the application, and mirror inter module dependencies
final BeanDeploymentModule rootBeanDeploymentModule = deploymentUnit.getAttachment(WeldAttachments.BEAN_DEPLOYMENT_MODULE);
putIfValueNotNull(eeModuleDescriptors, module.getIdentifier(), rootBeanDeploymentModule.getModuleDescriptor());
bdmsByIdentifier.put(module.getIdentifier(), rootBeanDeploymentModule);
moduleSpecByIdentifier.put(module.getIdentifier(), moduleSpecification);
beanDeploymentArchives.addAll(rootBeanDeploymentModule.getBeanDeploymentArchives());
final List<DeploymentUnit> subDeployments = deploymentUnit.getAttachmentList(Attachments.SUB_DEPLOYMENTS);
final Set<ClassLoader> subDeploymentLoaders = new HashSet<ClassLoader>();
final ServiceLoader<DeploymentUnitDependenciesProvider> dependenciesProviders = ServiceLoader.load(DeploymentUnitDependenciesProvider.class,
WildFlySecurityManager.getClassLoaderPrivileged(WeldDeploymentProcessor.class));
final ServiceLoader<ModuleServicesProvider> moduleServicesProviders = ServiceLoader.load(ModuleServicesProvider.class,
WildFlySecurityManager.getClassLoaderPrivileged(WeldDeploymentProcessor.class));
getDependencies(deploymentUnit, dependencies, dependenciesProviders);
for (DeploymentUnit subDeployment : subDeployments) {
getDependencies(subDeployment, dependencies, dependenciesProviders);
final Module subDeploymentModule = subDeployment.getAttachment(Attachments.MODULE);
if (subDeploymentModule == null) {
continue;
}
subDeploymentLoaders.add(subDeploymentModule.getClassLoader());
final ModuleSpecification subDeploymentModuleSpec = subDeployment.getAttachment(Attachments.MODULE_SPECIFICATION);
final BeanDeploymentModule bdm = subDeployment.getAttachment(WeldAttachments.BEAN_DEPLOYMENT_MODULE);
if (bdm == null) {
continue;
}
// add the modules bdas to the global set of bdas
beanDeploymentArchives.addAll(bdm.getBeanDeploymentArchives());
bdmsByIdentifier.put(subDeploymentModule.getIdentifier(), bdm);
moduleSpecByIdentifier.put(subDeploymentModule.getIdentifier(), subDeploymentModuleSpec);
putIfValueNotNull(eeModuleDescriptors, subDeploymentModule.getIdentifier(), bdm.getModuleDescriptor());
//we have to do this here as the aggregate components are not available in earlier phases
final ResourceRoot subDeploymentRoot = subDeployment.getAttachment(Attachments.DEPLOYMENT_ROOT);
// Add module services to bean deployment module
for (Entry<Class<? extends Service>, Service> entry : ServiceLoaders.loadModuleServices(moduleServicesProviders, deploymentUnit, subDeployment, subDeploymentModule, subDeploymentRoot).entrySet()) {
bdm.addService(entry.getKey(), Reflections.cast(entry.getValue()));
}
}
for (Map.Entry<ModuleIdentifier, BeanDeploymentModule> entry : bdmsByIdentifier.entrySet()) {
final ModuleSpecification bdmSpec = moduleSpecByIdentifier.get(entry.getKey());
final BeanDeploymentModule bdm = entry.getValue();
if (bdm == rootBeanDeploymentModule) {
continue; // the root module only has access to itself
}
for (ModuleDependency dependency : bdmSpec.getSystemDependencies()) {
BeanDeploymentModule other = bdmsByIdentifier.get(dependency.getIdentifier());
if (other != null && other != bdm) {
bdm.addBeanDeploymentModule(other);
}
}
}
Map<Class<? extends Service>, Service> rootModuleServices = ServiceLoaders.loadModuleServices(moduleServicesProviders, deploymentUnit, deploymentUnit,
module, deploymentRoot);
// Add root module services to root bean deployment module
for (Entry<Class<? extends Service>, Service> entry : rootModuleServices.entrySet()) {
rootBeanDeploymentModule.addService(entry.getKey(), Reflections.cast(entry.getValue()));
}
// Add root module services to additional bean deployment archives
for (final BeanDeploymentArchiveImpl additional : deploymentUnit.getAttachmentList(WeldAttachments.ADDITIONAL_BEAN_DEPLOYMENT_MODULES)) {
beanDeploymentArchives.add(additional);
for (Entry<Class<? extends Service>, Service> entry : rootModuleServices.entrySet()) {
additional.getServices().add(entry.getKey(), Reflections.cast(entry.getValue()));
}
}
final Collection<Metadata<Extension>> extensions = WeldPortableExtensions.getPortableExtensions(deploymentUnit).getExtensions();
final WeldDeployment deployment = new WeldDeployment(beanDeploymentArchives, extensions, module, subDeploymentLoaders, deploymentUnit, rootBeanDeploymentModule, eeModuleDescriptors);
final WeldBootstrapService weldBootstrapService = new WeldBootstrapService(deployment, WildFlyWeldEnvironment.INSTANCE, deploymentUnit.getName());
installBootstrapConfigurationService(deployment, parent);
// Add root module services to WeldDeployment
for (Entry<Class<? extends Service>, Service> entry : rootModuleServices.entrySet()) {
weldBootstrapService.addWeldService(entry.getKey(), Reflections.cast(entry.getValue()));
}
// add the weld service
final ServiceBuilder<WeldBootstrapService> weldBootstrapServiceBuilder = serviceTarget.addService(weldBootstrapServiceName, weldBootstrapService);
weldBootstrapServiceBuilder.addDependencies(TCCLSingletonService.SERVICE_NAME);
weldBootstrapServiceBuilder.addDependency(WeldExecutorServices.SERVICE_NAME, ExecutorServices.class, weldBootstrapService.getExecutorServices());
weldBootstrapServiceBuilder.addDependency(Services.JBOSS_SERVER_EXECUTOR, ExecutorService.class, weldBootstrapService.getServerExecutor());
// Install additional services
final ServiceLoader<BootstrapDependencyInstaller> installers = ServiceLoader.load(BootstrapDependencyInstaller.class,
WildFlySecurityManager.getClassLoaderPrivileged(WeldDeploymentProcessor.class));
for (BootstrapDependencyInstaller installer : installers) {
ServiceName serviceName = installer.install(serviceTarget, deploymentUnit, jtsEnabled);
// Add dependency for recognized services
if (ServiceNames.WELD_SECURITY_SERVICES_SERVICE_NAME.getSimpleName().equals(serviceName.getSimpleName())) {
weldBootstrapServiceBuilder.addDependency(serviceName, SecurityServices.class, weldBootstrapService.getSecurityServices());
} else if (ServiceNames.WELD_TRANSACTION_SERVICES_SERVICE_NAME.getSimpleName().equals(serviceName.getSimpleName())) {
weldBootstrapServiceBuilder.addDependency(serviceName, TransactionServices.class, weldBootstrapService.getWeldTransactionServices());
}
}
weldBootstrapServiceBuilder.install();
final List<SetupAction> setupActions = new ArrayList<SetupAction>();
JavaNamespaceSetup naming = deploymentUnit.getAttachment(org.jboss.as.ee.naming.Attachments.JAVA_NAMESPACE_SETUP_ACTION);
if (naming != null) {
setupActions.add(naming);
}
final WeldStartService weldStartService = new WeldStartService(setupActions, module.getClassLoader(), Utils.getRootDeploymentUnit(deploymentUnit).getServiceName());
ServiceBuilder<WeldStartService> startService = serviceTarget.addService(weldStartServiceName, weldStartService)
.addDependency(weldBootstrapServiceName, WeldBootstrapService.class, weldStartService.getBootstrap())
.addDependencies(dependencies);
// make sure JNDI bindings are up
startService.addDependency(JndiNamingDependencyProcessor.serviceName(deploymentUnit));
// [WFLY-5232]
startService.addDependencies(getJNDISubsytemDependencies());
final EarMetaData earConfig = deploymentUnit.getAttachment(org.jboss.as.ee.structure.Attachments.EAR_METADATA);
if (earConfig == null || !earConfig.getInitializeInOrder()) {
// in-order install of sub-deployments may result in service dependencies deadlocks if the jndi dependency services of subdeployments are added as dependencies
for (DeploymentUnit sub : subDeployments) {
startService.addDependency(JndiNamingDependencyProcessor.serviceName(sub));
}
}
startService.install();
}
private List<ServiceName> getJNDISubsytemDependencies() {
List<ServiceName> dependencies = new ArrayList<>();
dependencies.add(ContextNames.JBOSS_CONTEXT_SERVICE_NAME.append(ServiceName.of("UserTransaction")));
dependencies.add(ContextNames.JBOSS_CONTEXT_SERVICE_NAME.append(ServiceName.of("TransactionSynchronizationRegistry")));
dependencies.add(NamingService.SERVICE_NAME);
dependencies.add(DefaultNamespaceContextSelectorService.SERVICE_NAME);
return dependencies;
}
private void installBootstrapConfigurationService(WeldDeployment deployment, DeploymentUnit parentDeploymentUnit) {
final boolean nonPortableMode = parentDeploymentUnit.getAttachment(WeldConfiguration.ATTACHMENT_KEY).isNonPortableMode();
final ExternalConfiguration configuration = new ExternalConfigurationBuilder()
.add(ConfigurationKey.NON_PORTABLE_MODE.get(), nonPortableMode)
.build();
deployment.getServices().add(ExternalConfiguration.class, configuration);
}
private void getDependencies(DeploymentUnit deploymentUnit, Set<ServiceName> dependencies, ServiceLoader<DeploymentUnitDependenciesProvider> providers) {
providers.forEach(provider -> dependencies.addAll(provider.getDependencies(deploymentUnit)));
}
@Override
public void undeploy(DeploymentUnit context) {
final ServiceName weldTransactionServiceName = context.getServiceName().append(ServiceNames.WELD_TRANSACTION_SERVICES_SERVICE_NAME);
final ServiceController<?> serviceController = context.getServiceRegistry().getService(weldTransactionServiceName);
if (serviceController != null) {
serviceController.setMode(ServiceController.Mode.REMOVE);
}
}
}