
com.oneandone.ejbcdiunit.internal.EjbExtensionExtended Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ejb-cdi-unit Show documentation
Show all versions of ejb-cdi-unit Show documentation
A module that can be used together with cdiunit to build en ejb-test-environment.
The newest version!
/*
* Copyright 2014 Bryn Cooke 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 com.oneandone.ejbcdiunit.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import javax.ejb.Asynchronous;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.Schedule;
import javax.ejb.Schedules;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessInjectionTarget;
import javax.enterprise.inject.spi.ProcessManagedBean;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import javax.persistence.Entity;
import javax.persistence.PersistenceContext;
import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
import org.jboss.weld.bean.proxy.InterceptionDecorationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.oneandone.ejbcdiunit.ResourceQualifier;
import com.oneandone.ejbcdiunit.SupportEjbExtended;
import com.oneandone.ejbcdiunit.cdiunit.EjbName;
import com.oneandone.ejbcdiunit.persistence.SimulatedTransactionManager;
import com.oneandone.ejbcdiunit.resourcesimulators.SessionContextSimulation;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
/**
* CDI-Extension used to handle @Resource, @PersistenceContext...
* normally it just adds @Inject to the declarations.
* This was originally checked in at cdi-unit and has been adapted.
*/
@SupportEjbExtended
@ApplicationScoped
public class EjbExtensionExtended implements Extension {
Logger logger = LoggerFactory.getLogger("CDI-Unit EJB-ExtensionExtended");
private List> timerClasses = new ArrayList<>();
private List> entityClasses = new ArrayList<>();
private List> startupSingletons = new ArrayList<>();
private static AnnotationLiteral createDefaultAnnotation() {
return new AnnotationLiteral() {
private static final long serialVersionUID = 1L;
};
}
private static AnnotationLiteral createDependentAnnotation() {
return new AnnotationLiteral() {
private static final long serialVersionUID = 1L;
};
}
private static AnnotationLiteral createApplicationScopedAnnotation() {
return new AnnotationLiteral() {
private static final long serialVersionUID = 1L;
};
}
public List> getEntityClasses() {
return entityClasses;
}
public List> getTimerClasses() {
return timerClasses;
}
public List> getStartupSingletons() {
return startupSingletons;
}
/**
* use this event to initialise static contents in SimulatedTransactionManager
*
* @param bbd not used
* @param not used
*/
public void processBeforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd) {
new SimulatedTransactionManager().init();
}
private void processClass(AnnotatedTypeBuilder builder, String name, boolean isSingleton, boolean scopeIsPresent) {
logger.trace("processing class: {} singleton: {} scopeIsPresent: {}", name, isSingleton, scopeIsPresent);
if (!scopeIsPresent) {
if (!isSingleton || builder.getJavaClass().getFields().length > 0) {
builder.addToClass(createDependentAnnotation());
} else {
builder.addToClass(createApplicationScopedAnnotation()); // For Singleton normally only ApplicationScoped
}
}
builder.addToClass(createDefaultAnnotation());
if (!name.isEmpty()) {
builder.addToClass(new EjbName.EjbNameLiteral(name));
} else {
builder.addToClass(DefaultLiteral.INSTANCE);
}
}
String beanNameOrName(EJB ejb) {
if (!ejb.name().isEmpty()) {
return ejb.name();
} else {
return ejb.beanName();
}
}
T findAnnotation(Class> annotatedType, Class annotation) {
if (annotatedType.equals(Object.class)) {
return null;
}
return annotatedType.getAnnotation(annotation);
}
boolean isAnnotationPresent(Class> annotatedType, Class annotation) {
if (annotatedType.equals(Object.class)) {
return false;
}
return annotatedType.isAnnotationPresent(annotation);
}
boolean isAnnotationPresent(ProcessAnnotatedType pat, Class annotation) {
return isAnnotationPresent(pat.getAnnotatedType().getJavaClass(), annotation);
}
/**
* Handle Bean classes, if EJB-Annotations are recognized change, add, remove as fitting.
*
* @param pat the description of the beanclass
* @param The type
*/
public void processAnnotatedType(@Observes ProcessAnnotatedType pat) {
logger.trace("processing annotated Type: " + pat.getAnnotatedType().getJavaClass().getName());
boolean modified = false;
AnnotatedType annotatedType = pat.getAnnotatedType();
AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder().readFromType(annotatedType);
boolean scopeIsPresent =
annotatedType.isAnnotationPresent(ApplicationScoped.class)
|| annotatedType.isAnnotationPresent(Dependent.class)
|| annotatedType.isAnnotationPresent(RequestScoped.class)
|| annotatedType.isAnnotationPresent(SessionScoped.class);
Entity entity = annotatedType.getAnnotation(Entity.class);
if (entity != null) {
entityClasses.add(annotatedType.getJavaClass());
}
Stateless stateless = findAnnotation(annotatedType.getJavaClass(), Stateless.class);
// Stateless stateless = annotatedType.getJavaClass().getAnnotation(Stateless.class);
if (stateless != null) {
processClass(builder, stateless.name(), false, scopeIsPresent);
modified = true;
}
Stateful stateful = findAnnotation(annotatedType.getJavaClass(),Stateful.class);
if (stateful != null) {
processClass(builder, stateful.name(), false, scopeIsPresent);
modified = true;
}
try {
Singleton singleton = findAnnotation(annotatedType.getJavaClass(),Singleton.class);
if (singleton != null) {
processClass(builder, singleton.name(), true, scopeIsPresent);
modified = true;
if (annotatedType.getAnnotation(Startup.class) != null) {
startupSingletons.add(annotatedType.getJavaClass());
}
}
} catch (NoClassDefFoundError e) {
// EJB 3.0
}
for (AnnotatedMethod super T> method : annotatedType.getMethods()) {
EJB ejb = method.getAnnotation(EJB.class);
if (ejb != null) {
builder.removeFromMethod(method, EJB.class);
modified = true;
if (!beanNameOrName(ejb).isEmpty()) {
builder.addToMethod(method, new EjbName.EjbNameLiteral(beanNameOrName(ejb)));
} else {
builder.addToMethod(method, DefaultLiteral.INSTANCE);
}
}
}
for (AnnotatedField super T> field : annotatedType.getFields()) {
boolean addInject = false;
Produces produces = field.getAnnotation(Produces.class);
EJB ejb = field.getAnnotation(EJB.class);
if (ejb != null) {
modified = true;
addInject = true;
if (field.getJavaMember().getType().equals(annotatedType.getJavaClass())) {
logger.error("Self injection of EJB Type {} in field {} of Class {} can't get simulated by ejb-cdi-unit",
field.getJavaMember().getType().getName(), field.getJavaMember().getName(),
field.getJavaMember().getDeclaringClass().getName());
}
builder.removeFromField(field, EJB.class);
if (!beanNameOrName(ejb).isEmpty()) {
builder.addToField(field, new EjbName.EjbNameLiteral(beanNameOrName(ejb)));
} else {
builder.addToField(field, DefaultLiteral.INSTANCE);
}
}
Resource resource = field.getAnnotation(Resource.class);
if (resource != null) { // all Resources will be set injected. The Tester must provide anything for them.
// this means that MessageDrivenContexts, SessionContext and JMS-Resources will be expected to be injected.
addInject = true;
}
if (field.getAnnotation(PersistenceContext.class) != null) {
addInject = true;
builder.removeFromField(field, PersistenceContext.class);
}
if (addInject) {
modified = true;
builder.addToField(field, new AnnotationLiteral() {
private static final long serialVersionUID = 1L;
});
if (produces != null) {
builder.removeFromField(field, Produces.class);
}
if (field.getBaseType().equals(String.class)) {
builder.addToField(field, new ResourceQualifier.ResourceQualifierLiteral(resource.name(), resource.lookup(), resource.mappedName()) {
private static final long serialVersionUID = 1L;
});
}
}
}
if (modified) {
pat.setAnnotatedType(builder.create());
}
}
/**
* create EJB-Wrapper, Interceptors to specific annotated types if necessary.
*
* @param pat the description of the type
* @param the type
*/
public void processInjectionTarget(
@Observes ProcessAnnotatedType pat) {
if (isAnnotationPresent(pat, Stateless.class) || isAnnotationPresent(pat, Stateful.class)
|| isAnnotationPresent(pat, Singleton.class)
|| isAnnotationPresent(pat, MessageDriven.class)) {
createEJBWrapper(pat, pat.getAnnotatedType());
} else {
if (possiblyAsynchronous(pat.getAnnotatedType())) {
logger.error("Non Ejb with Asynchronous-Annotation {}", pat);
}
}
}
public void initializeSelfInit(@Observes ProcessInjectionTarget pit) {
boolean needToWrap = false;
for (AnnotatedField super T> f : pit.getAnnotatedType().getFields()) {
if (f.getJavaMember().getType().equals(pit.getAnnotatedType().getJavaClass())) {
needToWrap = true;
break;
}
}
if (needToWrap) {
final InjectionTarget it = pit.getInjectionTarget();
final Set> annotatedTypeFields = pit.getAnnotatedType().getFields();
final Class> annotatedTypeJavaClass = pit.getAnnotatedType().getJavaClass();
InjectionTarget wrapped = new InjectionTarget() {
@Override
public void inject(final T instance, CreationalContext ctx) {
HashMap, Object> orgValues = fetchOriginalValuesOfSelfFields(instance);
it.inject(instance, ctx);
// After injection replace all fields of self-type by enhanced ones which make sure interception is handled.
wrapDifferingValuesOfSelfFields(instance, orgValues);
}
@Override
public void postConstruct(T instance) {
it.postConstruct(instance);
}
@Override
public void preDestroy(T instance) {
it.dispose(instance);
}
@Override
public void dispose(T instance) {
it.dispose(instance);
}
@Override
public Set getInjectionPoints() {
return it.getInjectionPoints();
}
@Override
public T produce(CreationalContext ctx) {
return it.produce(ctx);
}
private void wrapDifferingValuesOfSelfFields(T instance, HashMap, Object> orgValues) {
for (AnnotatedField super T> f : annotatedTypeFields) {
if (f.getJavaMember().getType().equals(annotatedTypeJavaClass)) {
try {
final Field javaMember = f.getJavaMember();
javaMember.setAccessible(true);
final Object currentInstance = javaMember.get(instance);
if (currentInstance != null && currentInstance != orgValues.get(f)) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(currentInstance.getClass());
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
SessionContextSimulation.startInterceptionDecorationContext();
try {
return method.invoke(currentInstance, objects);
} catch (Throwable thw) {
if (thw instanceof InvocationTargetException) {
throw thw.getCause();
} else {
throw thw;
}
} finally {
InterceptionDecorationContext.endInterceptorContext();
}
}
});
javaMember.setAccessible(true);
javaMember.set(instance, enhancer.create());
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
private HashMap, Object> fetchOriginalValuesOfSelfFields(T instance) {
HashMap, Object> orgValues = new HashMap<>();
for (AnnotatedField super T> f : annotatedTypeFields) {
if (f.getJavaMember().getType().equals(annotatedTypeJavaClass)) {
final Field javaMember = f.getJavaMember();
javaMember.setAccessible(true);
try {
orgValues.put(f, javaMember.get(instance));
} catch (IllegalAccessException e) {
new RuntimeException(e);
}
}
}
return orgValues;
}
};
pit.setInjectionTarget(wrapped);
}
}
private boolean possiblyAsynchronous(final AnnotatedType at) {
boolean isTimer = false;
boolean isAsynch = false;
if (at.isAnnotationPresent(Asynchronous.class)) {
return true;
}
for (AnnotatedMethod super X> m: at.getMethods()) {
if (!isTimer && (m.isAnnotationPresent(Timeout.class)
|| m.isAnnotationPresent(Schedule.class)
|| m.isAnnotationPresent(Schedules.class)
)) {
timerClasses.add(m.getJavaMember().getDeclaringClass());
isTimer = true;
}
if (!isAsynch && m.isAnnotationPresent(Asynchronous.class)) {
isAsynch = true;
}
}
return isAsynch;
}
private void createEJBWrapper(ProcessAnnotatedType pat,
final AnnotatedType at) {
EjbAsynchronous ejbAsynchronous = AnnotationInstanceProvider.of(EjbAsynchronous.class);
EjbTransactional transactionalAnnotation =
AnnotationInstanceProvider.of(EjbTransactional.class);
AnnotatedTypeBuilder builder =
new AnnotatedTypeBuilder().readFromType(at);
builder.addToClass(transactionalAnnotation);
if (possiblyAsynchronous(at)) {
builder.addToClass(ejbAsynchronous);
}
// by annotating let CDI set Wrapper to this Bean
pat.setAnnotatedType(builder.create());
}
void processManagedBean(@Observes ProcessManagedBean> event) {
// LOGGER.fine("Handling ProcessManagedBean event for " + event.getBean().getBeanClass().getName());
// TODO - here we should check that all the rules have been followed
// and call addDefinitionError for each problem we encountered
Bean> bean = event.getBean();
for (InjectionPoint injectionPoint : bean.getInjectionPoints()) {
StringBuilder sb = new StringBuilder();
sb.append(" Found injection point ");
sb.append(injectionPoint.getType());
if (injectionPoint.getMember() != null && injectionPoint.getMember().getName() != null) {
sb.append(": ");
sb.append(injectionPoint.getMember().getName());
}
for (Annotation annotation : injectionPoint.getQualifiers()) {
sb.append(" ");
sb.append(annotation);
}
logger.trace(sb.toString());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy