/*
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual
* contributors by the @authors tag. See the copyright.txt in the
* distribution for a full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.weld.environment.se;
import static org.jboss.weld.config.ConfigurationKey.EXECUTOR_THREAD_POOL_TYPE;
import static org.jboss.weld.environment.util.URLUtils.JAR_URL_SEPARATOR;
import static org.jboss.weld.environment.util.URLUtils.PROCOTOL_FILE;
import static org.jboss.weld.environment.util.URLUtils.PROCOTOL_JAR;
import static org.jboss.weld.environment.util.URLUtils.PROTOCOL_FILE_PART;
import static org.jboss.weld.executor.ExecutorServicesFactory.ThreadPoolType.COMMON;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.annotation.Priority;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.UnsatisfiedResolutionException;
import javax.enterprise.inject.Vetoed;
import javax.enterprise.inject.se.SeContainerInitializer;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;
import org.jboss.weld.bootstrap.WeldBootstrap;
import org.jboss.weld.bootstrap.api.CDI11Bootstrap;
import org.jboss.weld.bootstrap.api.Environments;
import org.jboss.weld.bootstrap.api.Service;
import org.jboss.weld.bootstrap.api.SingletonProvider;
import org.jboss.weld.bootstrap.api.TypeDiscoveryConfiguration;
import org.jboss.weld.bootstrap.api.helpers.RegistrySingletonProvider;
import org.jboss.weld.bootstrap.spi.BeanDeploymentArchive;
import org.jboss.weld.bootstrap.spi.BeanDiscoveryMode;
import org.jboss.weld.bootstrap.spi.BeansXml;
import org.jboss.weld.bootstrap.spi.Deployment;
import org.jboss.weld.bootstrap.spi.Metadata;
import org.jboss.weld.bootstrap.spi.helpers.MetadataImpl;
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.environment.ContainerInstanceFactory;
import org.jboss.weld.environment.deployment.WeldBeanDeploymentArchive;
import org.jboss.weld.environment.deployment.WeldDeployment;
import org.jboss.weld.environment.deployment.WeldResourceLoader;
import org.jboss.weld.environment.deployment.discovery.ClassPathBeanArchiveScanner;
import org.jboss.weld.environment.deployment.discovery.DiscoveryStrategy;
import org.jboss.weld.environment.deployment.discovery.DiscoveryStrategyFactory;
import org.jboss.weld.environment.deployment.discovery.jandex.Jandex;
import org.jboss.weld.environment.logging.CommonLogger;
import org.jboss.weld.environment.se.ContainerLifecycleObserver.ContainerLifecycleObserverExtension;
import org.jboss.weld.environment.se.contexts.ThreadScoped;
import org.jboss.weld.environment.se.logging.WeldSELogger;
import org.jboss.weld.environment.util.BeanArchives;
import org.jboss.weld.environment.util.DevelopmentMode;
import org.jboss.weld.environment.util.Files;
import org.jboss.weld.metadata.BeansXmlImpl;
import org.jboss.weld.resources.ClassLoaderResourceLoader;
import org.jboss.weld.resources.spi.ClassFileServices;
import org.jboss.weld.resources.spi.ResourceLoader;
import org.jboss.weld.security.GetClassLoaderAction;
import org.jboss.weld.security.GetSystemPropertyAction;
import org.jboss.weld.util.Preconditions;
import org.jboss.weld.util.ServiceLoader;
import org.jboss.weld.util.Services;
import org.jboss.weld.util.collections.ImmutableList;
import org.jboss.weld.util.collections.ImmutableSet;
import org.jboss.weld.util.collections.Iterables;
import org.jboss.weld.util.collections.Multimap;
import org.jboss.weld.util.collections.WeldCollections;
/**
* <p>
* This builder is a preferred method of booting Weld SE container.
* </p>
*
* <p>
* Typical usage looks like this:
* </p>
*
* <pre>
* WeldContainer container = new Weld().initialize();
* container.select(Foo.class).get();
* container.event().select(Bar.class).fire(new Bar());
* container.shutdown();
* </pre>
*
* <p>
* The {@link WeldContainer} implements AutoCloseable:
* </p>
*
* <pre>
* try (WeldContainer container = new Weld().initialize()) {
* container.select(Foo.class).get();
* }
* </pre>
*
* <p>
* By default, the discovery is enabled so that all beans from all discovered bean archives are considered. However, it's possible to define a "synthetic" bean
* archive, or the set of bean classes and enablement respectively:
* </p>
*
* <pre>
* WeldContainer container = new Weld().beanClasses(Foo.class, Bar.class).alternatives(Bar.class).initialize()) {
* </pre>
*
* <p>
* Moreover, it's also possible to disable the discovery completely so that only the "synthetic" bean archive is considered:
* </p>
*
* <pre>
* WeldContainer container = new Weld().disableDiscovery().beanClasses(Foo.class, Bar.class).initialize()) {
* </pre>
*
*
* <p>
* In the same manner, it is possible to explicitly declare interceptors, decorators, extensions and Weld-specific options (such as relaxed construction) using
* the builder.
* </p>
*
* <pre>
* Weld builder = new Weld()
* .disableDiscovery()
* .packages(Main.class, Utils.class)
* .interceptors(TransactionalInterceptor.class)
* .property("org.jboss.weld.construction.relaxed", true);
* WeldContainer container = builder.initialize();
* </pre>
*
* <p>
* The builder is reusable which means that it's possible to initialize multiple Weld containers with one builder. However, note that containers must have a
* unique identifier assigned when running multiple Weld instances at the same time.
* </p>
*
* @author Peter Royle
* @author Pete Muir
* @author Ales Justin
* @author Martin Kouba
* @see WeldContainer
*/
@Vetoed
public class Weld extends SeContainerInitializer implements ContainerInstanceFactory {
/**
* By default, bean archive isolation is enabled. If set to false, Weld will use a "flat" deployment structure - all bean classes share the same bean
* archive and all beans.xml descriptors are automatically merged into one.
* <p>
* This key can be also used through {@link #property(String, Object)}.
*/
public static final String ARCHIVE_ISOLATION_SYSTEM_PROPERTY = "org.jboss.weld.se.archive.isolation";
/**
* By default, the development mode is disabled. If set to true, the development mode is activated
* <p>
* This key can be also used through {@link #property(String, Object)}.
*/
public static final String DEV_MODE_SYSTEM_PROPERTY = "org.jboss.weld.development";
/**
* By default, Weld automatically registers shutdown hook during initialization. If set to false, the registration of a shutdown hook is skipped.
* <p>
* This key can be also used through {@link #property(String, Object)}.
*/
public static final String SHUTDOWN_HOOK_SYSTEM_PROPERTY = "org.jboss.weld.se.shutdownHook";
/**
* By default, Weld SE does not support implicit bean archives without beans.xml. If set to true, Weld scans the class path entries and implicit bean
* archives which don't contain a beans.xml file are also supported.
* <p>
* This key can be also used through {@link #property(String, Object)}.
*/
public static final String SCAN_CLASSPATH_ENTRIES_SYSTEM_PROPERTY = "org.jboss.weld.se.scan.classpath.entries";
/**
* See also the CDI specification, section <b>15.1 Bean archive in Java SE</b>.
*/
public static final String JAVAX_ENTERPRISE_INJECT_SCAN_IMPLICIT = "javax.enterprise.inject.scan.implicit";
private static final String SYNTHETIC_LOCATION_PREFIX = "synthetic:";
static {
if (!(SingletonProvider.instance() instanceof RegistrySingletonProvider)) {
// make sure RegistrySingletonProvider is used (required for supporting multiple parallel Weld instances)
SingletonProvider.reset();
SingletonProvider.initialize(new RegistrySingletonProvider());
}
}
private final Map<String, WeldContainer> initializedContainers;
private String containerId;
private boolean discoveryEnabled = true;
private final Set<Class<?>> beanClasses;
private final List<Metadata<String>> selectedAlternatives;
private final List<Metadata<String>> selectedAlternativeStereotypes;
private final List<Metadata<String>> enabledInterceptors;
private final List<Metadata<String>> enabledDecorators;
private final Set<Metadata<Extension>> extensions;
private final Map<String, Object> properties;
private final Set<PackInfo> packages;
private final List<ContainerLifecycleObserver<?>> containerLifecycleObservers;
private ResourceLoader resourceLoader;
private final Map<Class<? extends Service>, Service> additionalServices;
public Weld() {
this(null);
}
/**
*
* @param containerId The container identifier
* @see Weld#containerId(String)
*/
public Weld(String containerId) {
this.containerId = containerId;
this.initializedContainers = new HashMap<String, WeldContainer>();
this.beanClasses = new HashSet<Class<?>>();
this.selectedAlternatives = new ArrayList<Metadata<String>>();
this.selectedAlternativeStereotypes = new ArrayList<Metadata<String>>();
this.enabledInterceptors = new ArrayList<Metadata<String>>();
this.enabledDecorators = new ArrayList<Metadata<String>>();
this.extensions = new HashSet<Metadata<Extension>>();
this.properties = new HashMap<String, Object>();
this.packages = new HashSet<PackInfo>();
this.containerLifecycleObservers = new LinkedList<>();
this.resourceLoader = new WeldResourceLoader();
this.additionalServices = new HashMap<>();
}
/**
* Containers must have a unique identifier assigned when running multiple Weld instances at the same time.
*
* @param containerId
* @return self
*/
public Weld containerId(String containerId) {
this.containerId = containerId;
return this;
}
/**
*
* @return a container identifier
* @see #containerId(String)
*/
public String getContainerId() {
return containerId;
}
/**
* Define the set of bean classes for the synthetic bean archive.
*
* @param classes
* @return self
*/
public Weld beanClasses(Class<?>... classes) {
beanClasses.clear();
addBeanClasses(classes);
return this;
}
/**
* Add a bean class to the set of bean classes for the synthetic bean archive.
*
* @param beanClass
* @return self
*/
public Weld addBeanClass(Class<?> beanClass) {
beanClasses.add(beanClass);
return this;
}
@Override
public Weld addBeanClasses(Class<?>... classes) {
for (Class<?> aClass : classes) {
addBeanClass(aClass);
}
return this;
}
/**
* All classes from the packages of the specified classes will be added to the set of bean classes for the synthetic bean archive.
*
* <p>
* Note that the scanning possibilities are limited. Therefore, only directories and jar files from the filesystem are supported.
* </p>
*
* <p>
* Scanning may also have negative impact on bootstrap performance.
* </p>
*
* @param classes
* @return self
*/
public Weld packages(Class<?>... packageClasses) {
packages.clear();
addPackages(false, packageClasses);
return this;
}
/**
* Packages of the specified classes will be scanned and found classes will be added to the set of bean classes for the synthetic bean archive.
*
* @param scanRecursively
* @param packageClasses
* @return self
*/
public Weld addPackages(boolean scanRecursively, Class<?>... packageClasses) {
for (Class<?> packageClass : packageClasses) {
addPackage(scanRecursively, packageClass);
}
return this;
}
@Override
public Weld addPackages(Class<?>... packageClasses) {
addPackages(false, packageClasses);
return this;
}
@Override
public Weld addPackages(Package... packages) {
addPackages(false, packages);
return this;
}
@Override
public Weld addPackages(boolean scanRecursively, Package... packages) {
for (Package pack : packages) {
this.packages.add(new PackInfo(pack, scanRecursively));
}
return this;
}
/**
* A package of the specified class will be scanned and found classes will be added to the set of bean classes for the synthetic bean archive.
*
* @param scanRecursively
* @param packageClass
* @return self
*/
public Weld addPackage(boolean scanRecursively, Class<?> packageClass) {
packages.add(new PackInfo(packageClass, scanRecursively));
return this;
}
/**
* Define the set of extensions.
*
* @param extensions
* @return self
*/
public Weld extensions(Extension... extensions) {
this.extensions.clear();
for (Extension extension : extensions) {
addExtension(extension);
}
return this;
}
/**
* Add an extension to the set of extensions.
*
* @param extension an extension
*/
public Weld addExtension(Extension extension) {
extensions.add(new MetadataImpl<Extension>(extension, SYNTHETIC_LOCATION_PREFIX + extension.getClass().getName()));
return this;
}
@Override
public Weld addExtensions(Extension... extensions) {
for (Extension extension : extensions) {
addExtension(extension);
}
return this;
}
@SuppressWarnings("unchecked")
@Override
public Weld addExtensions(Class<? extends Extension>... extensionClasses) {
for (Class<? extends Extension> extensionClass : extensionClasses) {
try {
Extension extension = SecurityActions.newInstance(extensionClass);
addExtension(extension);
} catch (Exception ex) {
CommonLogger.LOG.unableToInstantiate(extensionClass, new Object[] {}, ex);
}
}
return this;
}
/**
* Add a synthetic container lifecycle event observer.
*
* @param observer
* @return self
* @see ContainerLifecycleObserver
*/
public Weld addContainerLifecycleObserver(ContainerLifecycleObserver<?> observer) {
containerLifecycleObservers.add(observer);
return this;
}
/**
* Enable interceptors for the synthetic bean archive, all previous values are removed.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param interceptorClasses
* @return self
*/
public Weld interceptors(Class<?>... interceptorClasses) {
enabledInterceptors.clear();
enableInterceptors(interceptorClasses);
return this;
}
/**
* Add an interceptor class to the list of enabled interceptors for the synthetic bean archive.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param interceptorClass
* @return self
*/
public Weld addInterceptor(Class<?> interceptorClass) {
enabledInterceptors.add(syntheticMetadata(interceptorClass));
return this;
}
@Override
public Weld enableInterceptors(Class<?>... interceptorClasses) {
for (Class<?> interceptorClass : interceptorClasses) {
addInterceptor(interceptorClass);
}
return this;
}
/**
* Enable decorators for the synthetic bean archive, all previous values are removed.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param decoratorClasses
* @return self
*/
public Weld decorators(Class<?>... decoratorClasses) {
enabledDecorators.clear();
enableDecorators(decoratorClasses);
return this;
}
/**
* Add a decorator class to the list of enabled decorators for the synthetic bean archive.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param decoratorClass
* @return self
*/
public Weld addDecorator(Class<?> decoratorClass) {
enabledDecorators.add(syntheticMetadata(decoratorClass));
return this;
}
@Override
public Weld enableDecorators(Class<?>... decoratorClasses) {
for (Class<?> decoratorClass : decoratorClasses) {
addDecorator(decoratorClass);
}
return this;
}
/**
* Select alternatives for the synthetic bean archive, all previous values are removed.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param alternativeClasses
* @return self
*/
public Weld alternatives(Class<?>... alternativeClasses) {
selectedAlternatives.clear();
selectAlternatives(alternativeClasses);
return this;
}
/**
* Add an alternative class to the list of selected alternatives for a synthetic bean archive.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param alternativeClass
* @return self
*/
public Weld addAlternative(Class<?> alternativeClass) {
selectedAlternatives.add(syntheticMetadata(alternativeClass));
return this;
}
@Override
public Weld selectAlternatives(Class<?>... alternativeClasses) {
for (Class<?> alternativeClass : alternativeClasses) {
addAlternative(alternativeClass);
}
return this;
}
/**
* Select alternative stereotypes for the synthetic bean archive, all previous values are removed.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param alternativeStereotypeClasses
* @return self
*/
@SafeVarargs
public final Weld alternativeStereotypes(Class<? extends Annotation>... alternativeStereotypeClasses) {
selectedAlternativeStereotypes.clear();
selectAlternativeStereotypes(alternativeStereotypeClasses);
return this;
}
@SuppressWarnings("unchecked")
@Override
public Weld selectAlternativeStereotypes(Class<? extends Annotation>... alternativeStereotypeClasses) {
for (Class<? extends Annotation> alternativeStereotypeClass : alternativeStereotypeClasses) {
addAlternativeStereotype(alternativeStereotypeClass);
}
return this;
}
/**
* Add an alternative stereotype class to the list of selected alternative stereotypes for a synthetic bean archive.
* <p>
* This method does not add any class to the set of bean classes for the synthetic bean archive. It's purpose is solely to compensate the absence of the
* <code>beans.xml</code> descriptor.
*
* @param alternativeStereotypeClass
* @return self
*/
public Weld addAlternativeStereotype(Class<? extends Annotation> alternativeStereotypeClass) {
selectedAlternativeStereotypes.add(syntheticMetadata(alternativeStereotypeClass));
return this;
}
/**
* Set the configuration property.
*
* @param key
* @param value
* @return self
* @see #ARCHIVE_ISOLATION_SYSTEM_PROPERTY
* @see #SHUTDOWN_HOOK_SYSTEM_PROPERTY
* @see #DEV_MODE_SYSTEM_PROPERTY
* @see ConfigurationKey
*/
public Weld property(String key, Object value) {
properties.put(key, value);
return this;
}
/**
* Set all the configuration properties.
*
* @param properties
* @return self
*/
public Weld properties(Map<String, Object> properties) {
this.properties.putAll(properties);
return this;
}
@Override
public Weld addProperty(String key, Object value) {
property(key, value);
return this;
}
@Override
public Weld setProperties(Map<String, Object> propertiesMap) {
properties.clear();
properties.putAll(propertiesMap);
return this;
}
/**
* Register per-deployment services which are shared across the entire application.
* <p>
* Weld uses services to communicate with its environment, e.g. {@link org.jboss.weld.manager.api.ExecutorServices} or
* {@link org.jboss.weld.transaction.spi.TransactionServices}.
* </p>
* <p>
* Service implementation may specify their priority using {@link Priority}. Services with higher priority have precedence. Services that do not specify
* priority have the default priority of 4500.
* </p>
*
* @param services
* @return self
* @see Service
*/
public Weld addServices(Service... services) {
for (Service service : services) {
for (Class<? extends Service> serviceInterface : Services.identifyServiceInterfaces(service.getClass(), new HashSet<>())) {
additionalServices.put(serviceInterface, service);
}
}
return this;
}
/**
* Reset the synthetic bean archive (bean classes and enablement), explicitly added extensions, services and container lifecycle observers.
*
* @return self
*/
public Weld reset() {
beanClasses.clear();
packages.clear();
selectedAlternatives.clear();
selectedAlternativeStereotypes.clear();
enabledInterceptors.clear();
enabledDecorators.clear();
extensions.clear();
containerLifecycleObservers.clear();
additionalServices.clear();
return this;
}
/**
* Reset all the state, except for initialized containers.
*
* @return self
* @see Weld#reset()
*/
public Weld resetAll() {
reset();
properties.clear();
enableDiscovery();
containerId(null);
return this;
}
/**
*
* @return self
* @see #disableDiscovery()
*/
public Weld enableDiscovery() {
this.discoveryEnabled = true;
return this;
}
/**
* By default, the discovery is enabled. However, it's possible to disable the discovery completely so that only the "synthetic" bean archive is considered.
*
* @return self
*/
public Weld disableDiscovery() {
this.discoveryEnabled = false;
return this;
}
/**
*
* @return <code>true</code> if the discovery is enabled, <code>false</code> otherwise
* @see #disableDiscovery()
*/
public boolean isDiscoveryEnabled() {
return discoveryEnabled;
}
/**
* Bootstraps a new Weld SE container with the current container id (generated value if not set through {@link #containerId(String)}).
* <p/>
* The container must be shut down properly when an application is stopped. Applications are encouraged to use the try-with-resources statement or invoke
* {@link WeldContainer#shutdown()} explicitly.
* <p/>
* However, a shutdown hook is also registered during initialization so that all running containers are shut down automatically when a program exits or VM
* is terminated. This means that it's not necessary to implement the shutdown logic in a class where a main method is used to start the container.
*
* @return the Weld container
* @see #enableDiscovery()
* @see WeldContainer#shutdown()
*/
public WeldContainer initialize() {
// If also building a synthetic bean archive or the implicit scan is enabled, the check for beans.xml is not necessary
if (!isSyntheticBeanArchiveRequired() && !isImplicitScanEnabled() && resourceLoader.getResource(WeldDeployment.BEANS_XML) == null) {
throw CommonLogger.LOG.missingBeansXml();
}
final WeldBootstrap bootstrap = new WeldBootstrap();
final Deployment deployment = createDeployment(resourceLoader, bootstrap);
final ExternalConfigurationBuilder configurationBuilder = new ExternalConfigurationBuilder()
// weld-se uses CommonForkJoinPoolExecutorServices by default
.add(EXECUTOR_THREAD_POOL_TYPE.get(), COMMON.toString())
// weld-se uses relaxed construction by default
.add(ConfigurationKey.RELAXED_CONSTRUCTION.get(), true);
for (Entry<String, Object> property : properties.entrySet()) {
String key = property.getKey();
if (SHUTDOWN_HOOK_SYSTEM_PROPERTY.equals(key) || ARCHIVE_ISOLATION_SYSTEM_PROPERTY.equals(key) || DEV_MODE_SYSTEM_PROPERTY.equals(key)
|| SCAN_CLASSPATH_ENTRIES_SYSTEM_PROPERTY.equals(key) || JAVAX_ENTERPRISE_INJECT_SCAN_IMPLICIT.equals(key)) {
continue;
}
configurationBuilder.add(key, property.getValue());
}
deployment.getServices().add(ExternalConfiguration.class, configurationBuilder.build());
final String containerId = this.containerId != null ? this.containerId : UUID.randomUUID().toString();
bootstrap.startContainer(containerId, Environments.SE, deployment);
final WeldContainer weldContainer = WeldContainer.startInitialization(containerId, deployment, bootstrap);
try {
bootstrap.startInitialization();
bootstrap.deployBeans();
bootstrap.validateBeans();
bootstrap.endInitialization();
WeldContainer.endInitialization(weldContainer, isEnabled(SHUTDOWN_HOOK_SYSTEM_PROPERTY, true));
initializedContainers.put(containerId, weldContainer);
} catch (Throwable e) {
// Discard the container if a bootstrap problem occurs, e.g. validation error
WeldContainer.discard(weldContainer.getId());
throw e;
}
return weldContainer;
}
/**
* Shuts down all the containers initialized by this builder.
*/
public void shutdown() {
if (!initializedContainers.isEmpty()) {
for (WeldContainer container : initializedContainers.values()) {
container.shutdown();
}
}
}
/**
* Set a {@link ClassLoader}. The given {@link ClassLoader} will be scanned automatically for bean archives if scanning is enabled.
*
* @param classLoader
* @return self
*/
public Weld setClassLoader(ClassLoader classLoader) {
Preconditions.checkNotNull(classLoader);
resourceLoader = new ClassLoaderResourceLoader(classLoader);
return this;
}
/**
* Set a {@link ResourceLoader} used to scan the application for bean archives. If you only want to use a specific {@link ClassLoader} for scanning, use
* {@link #setClassLoader(ClassLoader)} instead.
*
* @param resourceLoader
* @return self
* @see #isDiscoveryEnabled()
*/
public Weld setResourceLoader(ResourceLoader resourceLoader) {
Preconditions.checkNotNull(resourceLoader);
this.resourceLoader = resourceLoader;
return this;
}
/**
* <p>
* Extensions to Weld SE can subclass and override this method to customize the deployment before weld boots up. For example, to add a custom
* ResourceLoader, you would subclass Weld like so:
* </p>
*
* <pre>
* public class MyWeld extends Weld {
* protected Deployment createDeployment(ResourceLoader resourceLoader, CDI11Bootstrap bootstrap) {
* return super.createDeployment(new MyResourceLoader(), bootstrap);
* }
* }
* </pre>
*
* <p>
* This could then be used as normal:
* </p>
*
* <pre>
* WeldContainer container = new MyWeld().initialize();
* </pre>
*
* @param resourceLoader
* @param bootstrap
*/
protected Deployment createDeployment(ResourceLoader resourceLoader, CDI11Bootstrap bootstrap) {
final Iterable<Metadata<Extension>> extensions = getExtensions();
final TypeDiscoveryConfiguration typeDiscoveryConfiguration = bootstrap.startExtensions(extensions);
final Deployment deployment;
final Set<WeldBeanDeploymentArchive> beanDeploymentArchives = new HashSet<WeldBeanDeploymentArchive>();
final Map<Class<? extends Service>, Service> additionalServices = new HashMap<>(this.additionalServices);
if (discoveryEnabled) {
DiscoveryStrategy strategy = DiscoveryStrategyFactory.create(resourceLoader, bootstrap, ImmutableSet.<Class<? extends Annotation>> builder().addAll(typeDiscoveryConfiguration.getKnownBeanDefiningAnnotations())
// Add ThreadScoped manually as Weld SE doesn't support implicit bean archives without beans.xml
.add(ThreadScoped.class).build(), isEnabled(Jandex.DISABLE_JANDEX_DISCOVERY_STRATEGY, false));
if (isImplicitScanEnabled()) {
strategy.setScanner(new ClassPathBeanArchiveScanner(bootstrap));
}
beanDeploymentArchives.addAll(strategy.performDiscovery());
ClassFileServices classFileServices = strategy.getClassFileServices();
if (classFileServices != null) {
additionalServices.put(ClassFileServices.class, classFileServices);
}
}
if (isSyntheticBeanArchiveRequired()) {
ImmutableSet.Builder<String> beanClassesBuilder = ImmutableSet.builder();
beanClassesBuilder.addAll(scanPackages());
WeldBeanDeploymentArchive syntheticBeanArchive = new WeldBeanDeploymentArchive(WeldDeployment.SYNTHETIC_BDA_ID, beanClassesBuilder.build(), null,
buildSyntheticBeansXml(), Collections.emptySet(), ImmutableSet.copyOf(beanClasses));
beanDeploymentArchives.add(syntheticBeanArchive);
}
if (beanDeploymentArchives.isEmpty() && this.containerLifecycleObservers.isEmpty() && this.extensions.isEmpty()) {
throw WeldSELogger.LOG.weldContainerCannotBeInitializedNoBeanArchivesFound();
}
Multimap<String, BeanDeploymentArchive> problems = BeanArchives.findBeanClassesDeployedInMultipleBeanArchives(beanDeploymentArchives);
if (!problems.isEmpty()) {
// Right now, we only log a warning for each bean class deployed in multiple bean archives
for (Entry<String, Collection<BeanDeploymentArchive>> entry : problems.entrySet()) {
WeldSELogger.LOG.beanClassDeployedInMultipleBeanArchives(entry.getKey(), WeldCollections.toMultiRowString(entry.getValue()));
}
}
if (isEnabled(ARCHIVE_ISOLATION_SYSTEM_PROPERTY, true)) {
deployment = new WeldDeployment(resourceLoader, bootstrap, beanDeploymentArchives, extensions);
CommonLogger.LOG.archiveIsolationEnabled();
} else {
Set<WeldBeanDeploymentArchive> flatDeployment = new HashSet<WeldBeanDeploymentArchive>();
flatDeployment.add(WeldBeanDeploymentArchive.merge(bootstrap, beanDeploymentArchives));
deployment = new WeldDeployment(resourceLoader, bootstrap, flatDeployment, extensions);
CommonLogger.LOG.archiveIsolationDisabled();
}
// Register additional services if a service with higher priority not present
for (Entry<Class<? extends Service>, Service> entry : additionalServices.entrySet()) {
Services.put(deployment.getServices(), entry.getKey(), entry.getValue());
}
return deployment;
}
/**
* Utility method allowing managed instances of beans to provide entry points for non-managed beans (such as {@link WeldContainer}). Should only called once
* Weld has finished booting.
*
* @param manager the BeanManager to use to access the managed instance
* @param type the type of the Bean
* @param bindings the bean's qualifiers
* @return a managed instance of the bean
* @throws IllegalArgumentException if the given type represents a type variable
* @throws IllegalArgumentException if two instances of the same qualifier type are given
* @throws IllegalArgumentException if an instance of an annotation that is not a qualifier type is given
* @throws UnsatisfiedResolutionException if no beans can be resolved * @throws AmbiguousResolutionException if the ambiguous dependency resolution rules
* fail
* @throws IllegalArgumentException if the given type is not a bean type of the given bean
*/
protected <T> T getInstanceByType(BeanManager manager, Class<T> type, Annotation... bindings) {
final Bean<?> bean = manager.resolve(manager.getBeans(type, bindings));
if (bean == null) {
throw CommonLogger.LOG.unableToResolveBean(type, Arrays.asList(bindings));
}
CreationalContext<?> cc = manager.createCreationalContext(bean);
return type.cast(manager.getReference(bean, type, cc));
}
private boolean isImplicitScanEnabled() {
return isEnabled(SCAN_CLASSPATH_ENTRIES_SYSTEM_PROPERTY, false) || isEnabled(JAVAX_ENTERPRISE_INJECT_SCAN_IMPLICIT, false);
}
private boolean isSyntheticBeanArchiveRequired() {
return !beanClasses.isEmpty() || !packages.isEmpty();
}
private Iterable<Metadata<Extension>> getExtensions() {
Set<Metadata<Extension>> result = new HashSet<Metadata<Extension>>();
if (discoveryEnabled) {
Iterables.addAll(result, loadExtensions(resourceLoader));
}
if (!extensions.isEmpty()) {
result.addAll(extensions);
}
// Ensure that WeldSEBeanRegistrant is present
WeldSEBeanRegistrant weldSEBeanRegistrant = null;
for (Metadata<Extension> metadata : result) {
if (metadata.getValue().getClass().getName().equals(WeldSEBeanRegistrant.class.getName())) {
weldSEBeanRegistrant = (WeldSEBeanRegistrant) metadata.getValue();
break;
}
}
if (weldSEBeanRegistrant == null) {
try {
weldSEBeanRegistrant = SecurityActions.newInstance(WeldSEBeanRegistrant.class);
result.add(new MetadataImpl<Extension>(weldSEBeanRegistrant, SYNTHETIC_LOCATION_PREFIX + WeldSEBeanRegistrant.class.getName()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (isEnabled(DEV_MODE_SYSTEM_PROPERTY, false)) {
// The development mode is enabled - register the Probe extension
result.add(new MetadataImpl<Extension>(DevelopmentMode.getProbeExtension(resourceLoader), "N/A"));
}
if (!containerLifecycleObservers.isEmpty()) {
result.add(new MetadataImpl<Extension>(new ContainerLifecycleObserverExtension(containerLifecycleObservers),
SYNTHETIC_LOCATION_PREFIX + ContainerLifecycleObserver.class.getName()));
}
return result;
}
private Iterable<Metadata<Extension>> loadExtensions(ResourceLoader resourceLoader) {
return ServiceLoader.load(Extension.class, resourceLoader);
}
private BeansXml buildSyntheticBeansXml() {
return new BeansXmlImpl(ImmutableList.copyOf(selectedAlternatives), ImmutableList.copyOf(selectedAlternativeStereotypes),
ImmutableList.copyOf(enabledDecorators), ImmutableList.copyOf(enabledInterceptors), null, null, BeanDiscoveryMode.ALL, null, false);
}
private MetadataImpl<String> syntheticMetadata(Class<?> clazz) {
return new MetadataImpl<String>(clazz.getName(), SYNTHETIC_LOCATION_PREFIX + clazz.getName());
}
private Set<String> scanPackages() {
if (packages.isEmpty()) {
return Collections.emptySet();
}
Set<String> foundClasses = new HashSet<String>();
for (PackInfo packInfo : packages) {
String packName = packInfo.getPackName();
URL resourceUrl = packInfo.getResourceUrl(resourceLoader);
if (resourceUrl != null) {
WeldSELogger.LOG.scanningPackage(packName, resourceUrl);
try {
URI resourceUri = resourceUrl.toURI();
if (PROCOTOL_FILE.equals(resourceUrl.getProtocol())) {
File file = new File(resourceUri);
handleDir(file.isDirectory() ? file : file.getParentFile(), packInfo.isScanRecursively(), packName, foundClasses);
} else if (PROCOTOL_JAR.equals(resourceUrl.getProtocol())) {
handleJar(resourceUri, packInfo.isScanRecursively(), packName, foundClasses);
} else {
WeldSELogger.LOG.resourceUrlProtocolNotSupported(resourceUrl);
}
} catch (URISyntaxException e) {
CommonLogger.LOG.couldNotReadResource(resourceUrl, e);
}
} else {
WeldSELogger.LOG.packageNotFound(packName);
}
}
return foundClasses;
}
private void handleDir(File packDir, boolean scanRecursively, String packName, Set<String> foundClasses) {
if (packDir != null && packDir.exists() && packDir.canRead()) {
for (File file : packDir.listFiles()) {
if (file.isFile()) {
if (file.canRead() && Files.isClass(file.getName())) {
foundClasses.add(Files.filenameToClassname(packName + "." + file.getName()));
}
}
if (file.isDirectory() && scanRecursively) {
handleDir(file, scanRecursively, packName + "." + file.getName(), foundClasses);
}
}
}
}
private void handleJar(URI resourceUri, boolean scanRecursively, String packName, Set<String> foundClasses) {
// Currently we only support jar:file
if (resourceUri.getSchemeSpecificPart().startsWith(PROCOTOL_FILE)) {
// Get the JAR file path, e.g. "jar:file:/home/duke/duke.jar!/com/foo/Bar" becomes "/home/duke/duke.jar"
String path = resourceUri.getSchemeSpecificPart().substring(PROTOCOL_FILE_PART.length());
if (path.lastIndexOf(JAR_URL_SEPARATOR) > 0) {
path = path.substring(0, path.lastIndexOf(JAR_URL_SEPARATOR));
}
JarFile jar = null;
String packNamePath = packName.replace('.', '/');
int expectedPartsLength = splitBySlash(packNamePath).length + 1;
try {
jar = new JarFile(new File(path));
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.getName().endsWith(Files.CLASS_FILE_EXTENSION)) {
continue;
}
if (entry.getName().startsWith(packNamePath)) {
if (scanRecursively) {
foundClasses.add(Files.filenameToClassname(entry.getName()));
} else {
String[] parts = splitBySlash(entry.getName());
if (parts.length == expectedPartsLength) {
foundClasses.add(Files.filenameToClassname(entry.getName()));
}
}
}
}
} catch (IOException e) {
CommonLogger.LOG.couldNotReadResource(resourceUri, e);
} finally {
if (jar != null) {
try {
jar.close();
} catch (IOException ignored) {
}
}
}
}
}
private String[] splitBySlash(String value) {
return value.split("/");
}
private boolean isEnabled(String key, boolean defaultValue) {
Object value = properties.get(key);
if (value != null) {
return Boolean.TRUE.equals(value);
}
String system = AccessController.doPrivileged(new GetSystemPropertyAction(key));
if (system != null) {
return Boolean.valueOf(system);
}
return defaultValue;
}
private static class PackInfo {
private final String packName;
private final String packClassName;
private final boolean scanRecursively;
private final WeakReference<ClassLoader> classLoaderRef;
PackInfo(Class<?> packClass, boolean recursiveScan) {
this.packName = packClass.getPackage().getName();
this.packClassName = packClass.getName();
this.scanRecursively = recursiveScan;
this.classLoaderRef = new WeakReference<ClassLoader>(AccessController.doPrivileged(new GetClassLoaderAction(packClass)));
}
PackInfo(Package pack, boolean recursiveScan) {
this.packName = pack.getName();
this.scanRecursively = recursiveScan;
this.packClassName = null;
this.classLoaderRef = null;
}
public URL getResourceUrl(ResourceLoader resourceLoader) {
if (classLoaderRef != null) {
return classLoaderRef.get().getResource(this.getPackClassName().replace('.', '/') + Files.CLASS_FILE_EXTENSION);
} else {
return resourceLoader.getResource(getPackName().replace('.', '/'));
}
}
public String getPackName() {
return packName;
}
public String getPackClassName() {
return packClassName;
}
public boolean isScanRecursively() {
return scanRecursively;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((packClassName == null) ? 0 : packClassName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PackInfo other = (PackInfo) obj;
if (packClassName == null) {
if (other.packClassName != null) {
return false;
}
} else if (!packClassName.equals(other.packClassName)) {
return false;
}
return true;
}
}
}