
org.jboss.arquillian.container.weld.embedded.Utils Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat Middleware LLC, 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.arquillian.container.weld.embedded;
import jakarta.decorator.Decorator;
import jakarta.ejb.Stateful;
import jakarta.ejb.Stateless;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.ConversationScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.context.NormalScope;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.context.SessionScoped;
import jakarta.enterprise.inject.Model;
import jakarta.enterprise.inject.Stereotype;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.interceptor.Interceptor;
import org.jboss.arquillian.core.spi.Validate;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Filters;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.asset.ArchiveAsset;
import org.jboss.weld.bootstrap.api.Environment;
import org.jboss.weld.bootstrap.api.Environments;
import org.jboss.weld.bootstrap.spi.BeanDiscoveryMode;
import org.jboss.weld.bootstrap.spi.BeansXml;
import org.jboss.weld.bootstrap.spi.Filter;
import org.jboss.weld.bootstrap.spi.Metadata;
import org.jboss.weld.metadata.FilterPredicate;
import org.jboss.weld.resources.spi.ResourceLoader;
import org.jboss.weld.util.collections.ImmutableList;
import org.jboss.weld.util.collections.ImmutableSet;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* BeanUtils
*
* @author Aslak Knutsen
* @version $Revision: $
*/
final class Utils {
private static final String BEANS_XML_REGEX = ".*/beans\\.xml";
// prepared sets of annotations so that we avoid re-creating them multiple times later
private static final List> META_ANNOTATIONS = ImmutableList.of(Stereotype.class, NormalScope.class);
private static final Set> BEAN_DEFINING_ANNOTATIONS = ImmutableSet.of(
// built-in scopes
Dependent.class, RequestScoped.class, ConversationScoped.class, SessionScoped.class, ApplicationScoped.class,
Interceptor.class, Decorator.class,
// built-in stereotype
Model.class,
// meta-annotations
NormalScope.class, Stereotype.class);
private static final Set> BEAN_DEFINING_ANNOTATIONS_WITH_EE_ANNOTATIONS = ImmutableSet.of(
// built-in scopes
Dependent.class, RequestScoped.class, ConversationScoped.class, SessionScoped.class, ApplicationScoped.class,
Interceptor.class, Decorator.class,
// built-in stereotype
Model.class,
// meta-annotations
NormalScope.class, Stereotype.class,
// EJB annotations are to be considered bean defining in annotated discovery mode
jakarta.ejb.Singleton.class, Stateful.class, Stateless.class);
private Utils() {
}
@SuppressWarnings("unchecked")
static T getBeanReference(BeanManager manager, Class type) {
Bean> bean = manager.resolve(manager.getBeans(type));
return (T) manager.getReference(
bean,
type,
manager.createCreationalContext(null));
}
public static String findArchiveId(Archive> archive) {
return archive.getName();
}
public static Collection findBeansXml(Archive> archive) {
Validate.notNull(archive, "Archive must be specified");
List beansXmls = new ArrayList();
Map nestedArchives = archive.getContent(Filters.include(".*\\.jar|.*\\.war"));
for (final Map.Entry nestedArchiveEntry : nestedArchives.entrySet()) {
if (!(nestedArchiveEntry.getValue().getAsset() instanceof ArchiveAsset)) {
continue;
}
ArchiveAsset nestedArchive = (ArchiveAsset) nestedArchiveEntry.getValue().getAsset();
Map classes = nestedArchive.getArchive().getContent(Filters.include(BEANS_XML_REGEX));
for (final Map.Entry entry : classes.entrySet()) {
try {
beansXmls.add(
new URL(null, "archive://" + nestedArchive.getArchive().getName() + entry.getKey().get(), new URLStreamHandler() {
@Override
protected java.net.URLConnection openConnection(URL u) throws java.io.IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {
}
@Override
public InputStream getInputStream()
throws IOException {
return entry.getValue().getAsset().openStream();
}
};
}
}));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Map classes = archive.getContent(Filters.include(BEANS_XML_REGEX));
for (final Map.Entry entry : classes.entrySet()) {
try {
beansXmls.add(
new URL(null, "archive://" + entry.getKey().get(), new URLStreamHandler() {
@Override
protected java.net.URLConnection openConnection(URL u) throws java.io.IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {
}
@Override
public InputStream getInputStream()
throws IOException {
return entry.getValue().getAsset().openStream();
}
};
}
}));
} catch (Exception e) {
e.printStackTrace();
}
}
return beansXmls;
}
public static Collection> findBeanClasses(Archive> archive, ClassLoader classLoader, BeansXml beansXml, ResourceLoader resourceLoader, Environment environment) {
Validate.notNull(archive, "Archive must be specified");
List> beanClasses = new ArrayList>();
try {
Map nestedArchives = archive.getContent(Filters.include(".*\\.jar|.*\\.war|.*\\.rar"));
for (final Map.Entry nestedArchiveEntry : nestedArchives.entrySet()) {
if (!(nestedArchiveEntry.getValue().getAsset() instanceof ArchiveAsset)) {
continue;
}
ArchiveAsset nestedArchive = (ArchiveAsset) nestedArchiveEntry.getValue().getAsset();
Map markerFiles = nestedArchive.getArchive().getContent(Filters.include(BEANS_XML_REGEX));
if (markerFiles.isEmpty()) {
continue;
}
beanClasses.addAll(filterClasses(nestedArchive.getArchive(), classLoader, beansXml, resourceLoader, environment));
}
Map markerFiles = archive.getContent(Filters.include(BEANS_XML_REGEX));
if (!markerFiles.isEmpty()) {
beanClasses.addAll(filterClasses(archive, classLoader, beansXml, resourceLoader, environment));
}
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load class from archive " + archive.getName(), e);
}
return beanClasses;
}
/*
* input: /org/MyClass.class
* output: org.MyClass
*/
public static String findClassName(ArchivePath path) {
String className = path.get();
className = className.replaceAll("/WEB-INF/classes/", "")
.replaceAll("/META-INF/versions/\\d*/", "");
if (className.charAt(0) == '/') {
className = className.substring(1);
}
className = className.replaceAll("\\.class", "");
className = className.replaceAll("/", ".");
return className;
}
private static Collection> filterClasses(Archive> archive, ClassLoader classLoader, BeansXml beansXml, ResourceLoader resourceLoader, Environment environment)
throws ClassNotFoundException {
List> beanClasses = new ArrayList>();
Map classes = archive.getContent(Filters.include(".*\\.class"));
BeanDiscoveryMode discoveryMode = beansXml.getBeanDiscoveryMode();
for (Map.Entry classEntry : classes.entrySet()) {
if (beansXml.getScanning().getExcludes().isEmpty()) {
Class> loadedClass = classLoader.loadClass(
findClassName(classEntry.getKey()));
addBeanClassIfNeeded(loadedClass, beanClasses, discoveryMode, environment);
} else {
boolean isExcluded = false;
for (Metadata filterMetadata : beansXml.getScanning().getExcludes()) {
FilterPredicate excludePredicate = new FilterPredicate(filterMetadata, resourceLoader);
if (excludePredicate.test(findClassName(classEntry.getKey()))) {
isExcluded = true;
break;
}
}
if (!isExcluded) {
Class> loadedClass = classLoader.loadClass(findClassName(classEntry.getKey()));
addBeanClassIfNeeded(loadedClass, beanClasses, discoveryMode, environment);
}
}
}
return beanClasses;
}
private static void addBeanClassIfNeeded(Class> potentialBeanClass, List> knownBeanClasses, BeanDiscoveryMode mode, Environment environment) {
if (mode.equals(BeanDiscoveryMode.ANNOTATED)) {
Set> completeSetOfBeanDefiningAnnotations;
// if we are in EE, we need to consider EJB annotation to be bean defining
if (environment != null && (environment.equals(Environments.EE_INJECT) || environment.equals(Environments.EE))) {
completeSetOfBeanDefiningAnnotations = BEAN_DEFINING_ANNOTATIONS_WITH_EE_ANNOTATIONS;
} else {
completeSetOfBeanDefiningAnnotations = BEAN_DEFINING_ANNOTATIONS;
}
// discovery mode ANNOTATED, only adding classes that have some bean defining annotation
if (hasBeanDefiningAnnotation(potentialBeanClass, completeSetOfBeanDefiningAnnotations)) {
knownBeanClasses.add(potentialBeanClass);
}
} else {
// discovery mode ALL, just add all classes
knownBeanClasses.add(potentialBeanClass);
}
}
/**
* Checks given class for direct presence of any bean-defining annotation; returns true if the class has any of them.
* The set of bean defining annotations is provided as a parameter.
*
* This method is copied from Weld's org.jboss.weld.environment.util.Reflections#hasBeanDefiningAnnotation.
*
* @param clazz Class to check for annotations
* @param initialBeanDefiningAnnotations Set of annotations that are considered bean defining
* @return true if the class contains at least one bean defining annotation, false otherwise
*/
private static boolean hasBeanDefiningAnnotation(Class> clazz, Set> initialBeanDefiningAnnotations) {
for (Class extends Annotation> beanDefiningAnnotation : initialBeanDefiningAnnotations) {
if (clazz.getDeclaredAnnotation(beanDefiningAnnotation) != null) {
return true;
}
}
for (Class extends Annotation> metaAnnotation : META_ANNOTATIONS) {
if (hasBeanDefiningMetaAnnotationSpecified(clazz.getAnnotations(), metaAnnotation)) {
return true;
}
}
return false;
}
/**
* Checks provided array of annotations for presence of directly declared given meta-annotation.
* Returns true if any
*
* @param annotations Annotations to check for presence of meta annotation
* @param metaAnnotationType Meta-annotation (most likely {@code @Stereotype} or {@code @NormalScoped}) to check for
* @return true
if any of the annotations specified has the given meta annotation type specified, false
otherwise
*/
private static boolean hasBeanDefiningMetaAnnotationSpecified(Annotation[] annotations, Class extends Annotation> metaAnnotationType) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().getDeclaredAnnotation(metaAnnotationType) != null) {
return true;
}
}
return false;
}
}