/*
* 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 java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.ServiceLoader;
import java.util.Set;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.weld.WeldDeploymentMarker;
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.weld.deployment.BeanDeploymentArchiveImpl;
import org.jboss.as.weld.deployment.BeanDeploymentArchiveImpl.BeanArchiveType;
import org.jboss.as.weld.deployment.ExplicitBeanArchiveMetadata;
import org.jboss.as.weld.deployment.ExplicitBeanArchiveMetadataContainer;
import org.jboss.as.weld.deployment.PropertyReplacingBeansXmlParser;
import org.jboss.as.weld.deployment.UrlScanner;
import org.jboss.as.weld.deployment.WeldAttachments;
import org.jboss.as.weld.logging.WeldLogger;
import org.jboss.as.weld.spi.ComponentSupport;
import org.jboss.as.weld.spi.ModuleServicesProvider;
import org.jboss.as.weld.util.Reflections;
import org.jboss.as.weld.util.ServiceLoaders;
import org.jboss.modules.DependencySpec;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleDependencySpec;
import org.jboss.modules.ModuleLoadException;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.Resource;
import org.jboss.weld.bootstrap.api.Service;
import org.jboss.weld.bootstrap.spi.BeansXml;
import org.jboss.weld.xml.BeansXmlParser;
import org.wildfly.security.manager.WildFlySecurityManager;
/**
* Deployment processor that builds bean archives from external deployments.
* <p/>
* This is only run at the top level, as multiple sub deployments can reference the same
* beans.xml information, so we have to iterate through all bean deployment archives in this processor, to prevent
* beans.xml from being potentially parsed twice.
* <p/>
*
* @author Stuart Douglas
* @author Jozef Hartinger
*/
public class ExternalBeanArchiveProcessor implements DeploymentUnitProcessor {
private static final String META_INF_BEANS_XML = "META-INF/beans.xml";
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
if (!WeldDeploymentMarker.isPartOfWeldDeployment(deploymentUnit)) {
return;
}
if (deploymentUnit.getParent() != null) {
return;
}
final Set<String> componentClassNames = new HashSet<>();
final ServiceLoader<ComponentSupport> supportServices = ServiceLoader.load(ComponentSupport.class,
WildFlySecurityManager.getClassLoaderPrivileged(ExternalBeanArchiveProcessor.class));
final String beanArchiveIdPrefix = deploymentUnit.getName() + ".external.";
final List<DeploymentUnit> deploymentUnits = new ArrayList<DeploymentUnit>();
deploymentUnits.add(deploymentUnit);
deploymentUnits.addAll(deploymentUnit.getAttachmentList(Attachments.SUB_DEPLOYMENTS));
PropertyReplacingBeansXmlParser parser = new PropertyReplacingBeansXmlParser(deploymentUnit);
final HashSet<URL> existing = new HashSet<URL>();
for (DeploymentUnit deployment : deploymentUnits) {
try {
final ExplicitBeanArchiveMetadataContainer weldDeploymentMetadata = deployment.getAttachment(ExplicitBeanArchiveMetadataContainer.ATTACHMENT_KEY);
if (weldDeploymentMetadata != null) {
for (ExplicitBeanArchiveMetadata md : weldDeploymentMetadata.getBeanArchiveMetadata().values()) {
existing.add(md.getBeansXmlFile().toURL());
if (md.getAdditionalBeansXmlFile() != null) {
existing.add(md.getAdditionalBeansXmlFile().toURL());
}
}
}
} catch (MalformedURLException e) {
throw new DeploymentUnitProcessingException(e);
}
EEModuleDescription moduleDesc = deployment.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
if(moduleDesc != null) {
for(ComponentDescription component : moduleDesc.getComponentDescriptions()) {
for (ComponentSupport support : supportServices) {
if (!support.isDiscoveredExternalType(component)) {
componentClassNames.add(component.getComponentClassName());
break;
}
}
}
}
}
final ServiceLoader<ModuleServicesProvider> moduleServicesProviders = ServiceLoader.load(ModuleServicesProvider.class,
WildFlySecurityManager.getClassLoaderPrivileged(WeldDeploymentProcessor.class));
for (DeploymentUnit deployment : deploymentUnits) {
final Module module = deployment.getAttachment(Attachments.MODULE);
if (module == null) {
return;
}
for (DependencySpec dep : module.getDependencies()) {
final Module dependency = loadModuleDependency(dep);
if (dependency == null) {
continue;
}
Set<URL> urls = findExportedLocalBeansXml(dependency);
if (urls != null) {
List<BeanDeploymentArchiveImpl> moduleBdas = new ArrayList<>();
for (URL url : urls) {
if (existing.contains(url)) {
continue;
}
/*
* Workaround for http://java.net/jira/browse/JAVASERVERFACES-2837
*/
if (url.toString().contains("jsf-impl-2.2")) {
continue;
}
/*
* Workaround for resteasy-cdi bundling beans.xml
*/
if (url.toString().contains("resteasy-cdi")) {
continue;
}
WeldLogger.DEPLOYMENT_LOGGER.debugf("Found external beans.xml: %s", url.toString());
final BeansXml beansXml = parseBeansXml(url, parser, deploymentUnit);
final UrlScanner urlScanner = new UrlScanner();
final List<String> discoveredClasses = new ArrayList<String>();
if (!urlScanner.handleBeansXml(url, discoveredClasses)) {
continue;
}
discoveredClasses.removeAll(componentClassNames);
final BeanDeploymentArchiveImpl bda = new BeanDeploymentArchiveImpl(new HashSet<String>(discoveredClasses), beansXml, dependency, beanArchiveIdPrefix + url.toExternalForm(), BeanArchiveType.EXTERNAL);
WeldLogger.DEPLOYMENT_LOGGER.beanArchiveDiscovered(bda);
// Add module services to external bean deployment archive
for (Entry<Class<? extends Service>, Service> entry : ServiceLoaders
.loadModuleServices(moduleServicesProviders, deploymentUnit, deployment, module, null).entrySet()) {
bda.getServices().add(entry.getKey(), Reflections.cast(entry.getValue()));
}
deploymentUnit.addToAttachmentList(WeldAttachments.ADDITIONAL_BEAN_DEPLOYMENT_MODULES, bda);
moduleBdas.add(bda);
// make sure that if this beans.xml is seen by some other module, it is not processed twice
existing.add(url);
}
//BDA's from inside the same module have visibility on each other
for(BeanDeploymentArchiveImpl i : moduleBdas) {
for(BeanDeploymentArchiveImpl j : moduleBdas) {
if(i != j) {
i.addBeanDeploymentArchive(j);
}
}
}
}
}
}
}
private Set<URL> findExportedLocalBeansXml(Module dependencyModule) {
HashSet<URL> ret = new HashSet<>();
Enumeration<URL> exported = dependencyModule.getExportedResources(META_INF_BEANS_XML);
if (exported.hasMoreElements()) {
Set<URL> exportedSet = new HashSet<>(Collections.list(exported));
Collection<Resource> locals = dependencyModule.getClassLoader().loadResourceLocal(META_INF_BEANS_XML);
if (!locals.isEmpty()) {
for(Resource local: locals) {
URL url = local.getURL();
if (exportedSet.contains(url)) {
ret.add(url);
}
}
return ret;
}
}
return null;
}
private Module loadModuleDependency(DependencySpec dep) {
if (dep instanceof ModuleDependencySpec) {
ModuleDependencySpec dependency = (ModuleDependencySpec) dep;
final ModuleLoader loader = dependency.getModuleLoader();
if (loader != null) {
try {
return dependency.getModuleLoader().loadModule(dependency.getIdentifier());
} catch (ModuleLoadException e) {
return null;
}
}
}
return null;
}
@Override
public void undeploy(DeploymentUnit context) {
}
private BeansXml parseBeansXml(URL beansXmlFile, BeansXmlParser parser, final DeploymentUnit deploymentUnit) throws DeploymentUnitProcessingException {
return parser.parse(beansXmlFile);
}
}