/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc., 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.bootstrap;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.util.Collections;
import java.util.Set;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import org.jboss.weld.annotated.slim.SlimAnnotatedType;
import org.jboss.weld.annotated.slim.SlimAnnotatedTypeContext;
import org.jboss.weld.bootstrap.events.ContainerLifecycleEvents;
import org.jboss.weld.event.ContainerLifecycleEventObserverMethod;
import org.jboss.weld.logging.BootstrapLogger;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.resources.ClassLoaderResourceLoader;
import org.jboss.weld.resources.ClassTransformer;
import org.jboss.weld.resources.spi.ClassFileInfo;
import org.jboss.weld.resources.spi.ClassFileInfoException;
import org.jboss.weld.resources.spi.ClassFileServices;
import org.jboss.weld.resources.spi.ResourceLoadingException;
import org.jboss.weld.security.GetDeclaredMethodAction;
import org.jboss.weld.util.Beans;
import org.jboss.weld.util.bytecode.BytecodeUtils;
import org.jboss.weld.util.reflection.Reflections;
/**
* Specialized version of {@link AnnotatedTypeLoader}. This implementation uses {@link ClassFileServices} to avoid loading application classes that are not
* needed for Weld. In addition, this implementation feeds {@link SlimAnnotatedTypeContext} with {@link ClassFileInfo} and resolved {@link ProcessAnnotatedType}
* observer methods. If {@link ClassFileServices} is not sufficient to load an annotated type (e.g. superclass information missing from the index) then a fall back
* to {@link AnnotatedTypeLoader} is performed.
*
* @author Jozef Hartinger
*
*/
class FastAnnotatedTypeLoader extends AnnotatedTypeLoader {
private final ClassFileServices classFileServices;
private final FastProcessAnnotatedTypeResolver resolver;
private final AnnotatedTypeLoader fallback;
private final boolean checkTypeModifiers;
private static final String CLASSINFO_CLASS_NAME = "org.jboss.jandex.ClassInfo";
FastAnnotatedTypeLoader(BeanManagerImpl manager, ClassTransformer transformer, ClassFileServices classFileServices,
ContainerLifecycleEvents events, FastProcessAnnotatedTypeResolver resolver) {
super(manager, transformer, events);
this.fallback = new AnnotatedTypeLoader(manager, transformer, events);
this.classFileServices = classFileServices;
this.resolver = resolver;
this.checkTypeModifiers = initCheckTypeModifiers();
}
@Override
public <T> SlimAnnotatedTypeContext<T> loadAnnotatedType(String className, String bdaId) {
try {
final ClassFileInfo classFileInfo = classFileServices.getClassFileInfo(className);
// firstly, check if this class is an annotation
if ((classFileInfo.getModifiers() & BytecodeUtils.ANNOTATION) != 0) {
// This is an annotation - an annotation may not end up as a managed bean nor be observer by PAT observer. Skip it.
return null;
}
if (classFileInfo.isVetoed()) {
return null;
}
// secondly, let's resolve PAT observers for this class
Set<ContainerLifecycleEventObserverMethod<?>> observerMethods = Collections.emptySet();
if (containerLifecycleEvents.isProcessAnnotatedTypeObserved()) {
observerMethods = resolver.resolveProcessAnnotatedTypeObservers(classFileServices, className);
if (!observerMethods.isEmpty()) {
// there are PAT observers for this class, register the class now
return createContext(className, classFileInfo, observerMethods, bdaId);
}
}
if (Beans.isDecoratorDeclaringInAppropriateConstructor(classFileInfo)) {
BootstrapLogger.LOG.decoratorWithNonCdiConstructor(classFileInfo.getClassName());
}
// lastly, check if this class fulfills CDI managed bean requirements - if it does, add the class
if (Beans.isTypeManagedBeanOrDecoratorOrInterceptor(classFileInfo, checkTypeModifiers)) {
return createContext(className, classFileInfo, observerMethods, bdaId);
}
return null;
} catch (ClassFileInfoException e) {
BootstrapLogger.LOG.exceptionLoadingAnnotatedType(e.getMessage());
return fallback.loadAnnotatedType(className, bdaId);
}
}
private <T> SlimAnnotatedTypeContext<T> createContext(String className, ClassFileInfo classFileInfo,
Set<ContainerLifecycleEventObserverMethod<?>> observerMethods, String bdaId) {
final SlimAnnotatedType<T> type = loadSlimAnnotatedType(this.<T> loadClass(className), bdaId);
if (type != null) {
return SlimAnnotatedTypeContext.of(type, classFileInfo, observerMethods);
}
return null;
}
private <T> SlimAnnotatedType<T> loadSlimAnnotatedType(Class<T> clazz, String bdaId) {
if (clazz != null) {
try {
return classTransformer.getBackedAnnotatedType(clazz, bdaId);
} catch (ResourceLoadingException e) {
missingDependenciesRegistry.handleResourceLoadingException(clazz.getName(), e);
}
}
return null;
}
// checking availability of ClassInfo.setFlags method is just workaround for JANDEX-37
private boolean initCheckTypeModifiers() {
Class<?> classInfoclass = Reflections.loadClass(CLASSINFO_CLASS_NAME, new ClassLoaderResourceLoader(classFileServices.getClass().getClassLoader()));
if (classInfoclass != null) {
try {
Method setFlags = AccessController.doPrivileged(GetDeclaredMethodAction.of(classInfoclass, "setFlags", short.class));
return setFlags != null;
} catch (Exception exceptionIgnored) {
BootstrapLogger.LOG.usingOldJandexVersion();
return false;
}
} else {
return true;
}
}
}