blob: 1c4dad1179351806787f3fad03f00989d2aeb147 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.openejb.assembler.classic;
import org.apache.openejb.BeanContext;
import org.apache.openejb.BeanType;
import org.apache.openejb.Injection;
import org.apache.openejb.ModuleContext;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.core.cmp.CmpUtil;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.spi.ContainerSystem;
import org.apache.openejb.util.Duration;
import org.apache.openejb.util.Index;
import org.apache.openejb.util.Messages;
import org.apache.openejb.util.SafeToolkit;
import javax.ejb.TimedObject;
import javax.ejb.Timer;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.persistence.EntityManagerFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
class EnterpriseBeanBuilder {
protected static final Messages messages = new Messages("org.apache.openejb.util.resources");
private final EnterpriseBeanInfo bean;
private final BeanType ejbType;
private final List<Exception> warnings = new ArrayList<Exception>();
private final ModuleContext moduleContext;
private final List<Injection> moduleInjections;
public EnterpriseBeanBuilder(EnterpriseBeanInfo bean, ModuleContext moduleContext, List<Injection> moduleInjections) {
this.moduleContext = moduleContext;
this.bean = bean;
this.moduleInjections = moduleInjections;
if (bean.type == EnterpriseBeanInfo.STATEFUL) {
ejbType = BeanType.STATEFUL;
} else if (bean.type == EnterpriseBeanInfo.STATELESS) {
ejbType = BeanType.STATELESS;
} else if (bean.type == EnterpriseBeanInfo.SINGLETON) {
ejbType = BeanType.SINGLETON;
} else if (bean.type == EnterpriseBeanInfo.MANAGED) {
ejbType = BeanType.MANAGED;
} else if (bean.type == EnterpriseBeanInfo.MESSAGE) {
ejbType = BeanType.MESSAGE_DRIVEN;
} else if (bean.type == EnterpriseBeanInfo.ENTITY) {
String persistenceType = ((EntityBeanInfo) bean).persistenceType;
ejbType = (persistenceType.equalsIgnoreCase("Container")) ? BeanType.CMP_ENTITY : BeanType.BMP_ENTITY;
} else {
throw new UnsupportedOperationException("No building support for bean type: " + bean);
}
}
public BeanContext build() throws OpenEJBException {
Class ejbClass = loadClass(bean.ejbClass, "classNotFound.ejbClass");
Class home = null;
Class remote = null;
if (bean.home != null) {
home = loadClass(bean.home, "classNotFound.home");
remote = loadClass(bean.remote, "classNotFound.remote");
}
Class<?> proxy = null;
if (bean.proxy != null) {
proxy = loadClass(bean.proxy, "classNotFound.proxy");
}
Class<?> localhome = null;
Class<?> local = null;
if (bean.localHome != null) {
localhome = loadClass(bean.localHome, "classNotFound.localHome");
local = loadClass(bean.local, "classNotFound.local");
}
List<Class> businessLocals = new ArrayList<Class>();
for (String businessLocal : bean.businessLocal) {
businessLocals.add(loadClass(businessLocal, "classNotFound.businessLocal"));
}
List<Class> businessRemotes = new ArrayList<Class>();
for (String businessRemote : bean.businessRemote) {
businessRemotes.add(loadClass(businessRemote, "classNotFound.businessRemote"));
}
Class serviceEndpoint = null;
if (BeanType.STATELESS == ejbType || BeanType.SINGLETON == ejbType ){
if(bean.serviceEndpoint != null){
serviceEndpoint = loadClass(bean.serviceEndpoint, "classNotFound.sei");
}
}
Class primaryKey = null;
if (ejbType.isEntity() && ((EntityBeanInfo) bean).primKeyClass != null) {
String className = ((EntityBeanInfo) bean).primKeyClass;
primaryKey = loadClass(className, "classNotFound.primaryKey");
}
final String transactionType = bean.transactionType;
// determine the injections
InjectionBuilder injectionBuilder = new InjectionBuilder(moduleContext.getClassLoader());
List<Injection> injections = injectionBuilder.buildInjections(bean.jndiEnc);
Set<Class<?>> relevantClasses = new HashSet<Class<?>>();
Class c = ejbClass;
do {
relevantClasses.add(c);
c = c.getSuperclass();
} while (c != null && c != Object.class);
for (Injection injection: moduleInjections) {
if (relevantClasses.contains(injection.getTarget())) {
injections.add(injection);
}
}
// build the enc
JndiEncBuilder jndiEncBuilder = new JndiEncBuilder(bean.jndiEnc, injections, transactionType, moduleContext.getId(), null, moduleContext.getUniqueId(), moduleContext.getClassLoader());
Context compJndiContext = jndiEncBuilder.build(JndiEncBuilder.JndiScope.comp);
bind(compJndiContext, "module", moduleContext.getModuleJndiContext());
bind(compJndiContext, "app", moduleContext.getAppContext().getAppJndiContext());
bind(compJndiContext, "global", moduleContext.getAppContext().getGlobalJndiContext());
BeanContext deployment;
if (BeanType.MESSAGE_DRIVEN != ejbType) {
deployment = new BeanContext(bean.ejbDeploymentId, compJndiContext, moduleContext, ejbClass, home, remote, localhome, local, proxy, serviceEndpoint, businessLocals, businessRemotes, primaryKey, ejbType, bean.localbean && ejbType.isSession());
if (bean instanceof ManagedBeanInfo) {
deployment.setHidden(((ManagedBeanInfo) bean).hidden);
}
} else {
MessageDrivenBeanInfo messageDrivenBeanInfo = (MessageDrivenBeanInfo) bean;
Class mdbInterface = loadClass(messageDrivenBeanInfo.mdbInterface, "classNotFound.mdbInterface");
deployment = new BeanContext(bean.ejbDeploymentId, compJndiContext, moduleContext, ejbClass, mdbInterface, messageDrivenBeanInfo.activationProperties);
deployment.setDestinationId(messageDrivenBeanInfo.destinationId);
}
deployment.getProperties().putAll(bean.properties);
deployment.setEjbName(bean.ejbName);
deployment.setRunAs(bean.runAs);
deployment.getInjections().addAll(injections);
// ejbTimeout
deployment.setEjbTimeout(getTimeout(ejbClass, bean.timeoutMethod));
if (bean.statefulTimeout != null) {
deployment.setStatefulTimeout(new Duration(bean.statefulTimeout.time, TimeUnit.valueOf(bean.statefulTimeout.unit)));
}
if (bean instanceof StatefulBeanInfo) {
StatefulBeanInfo statefulBeanInfo = (StatefulBeanInfo) bean;
for (InitMethodInfo init : statefulBeanInfo.initMethods) {
Method beanMethod = MethodInfoUtil.toMethod(ejbClass, init.beanMethod);
List<Method> methods = new ArrayList<Method>();
if (home != null) methods.addAll(Arrays.asList(home.getMethods()));
if (localhome != null) methods.addAll(Arrays.asList(localhome.getMethods()));
for (Method homeMethod : methods) {
if (init.createMethod != null && !init.createMethod.methodName.equals(homeMethod.getName())) continue;
if (!homeMethod.getName().startsWith("create")) continue;
if (paramsMatch(beanMethod, homeMethod)){
deployment.mapMethods(homeMethod, beanMethod);
}
}
}
for (RemoveMethodInfo removeMethod : statefulBeanInfo.removeMethods) {
if (removeMethod.beanMethod.methodParams == null) {
MethodInfo methodInfo = new MethodInfo();
methodInfo.methodName = removeMethod.beanMethod.methodName;
methodInfo.methodParams = removeMethod.beanMethod.methodParams;
methodInfo.className = removeMethod.beanMethod.className;
List<Method> methods = MethodInfoUtil.matchingMethods(methodInfo, ejbClass);
for (Method method : methods) {
deployment.getRemoveMethods().add(method);
deployment.setRetainIfExeption(method, removeMethod.retainIfException);
}
} else {
Method method = MethodInfoUtil.toMethod(ejbClass, removeMethod.beanMethod);
deployment.getRemoveMethods().add(method);
deployment.setRetainIfExeption(method, removeMethod.retainIfException);
}
}
String moduleId = moduleContext.getId();
Map<EntityManagerFactory, Map> extendedEntityManagerFactories = new HashMap<EntityManagerFactory, Map>();
for (PersistenceContextReferenceInfo info : statefulBeanInfo.jndiEnc.persistenceContextRefs) {
if (info.extended) {
// EntityManagerFactory entityManagerFactory = emfLinkResolver.resolveLink(info.persistenceUnitName, moduleId);
// extendedEntityManagerFactories.put(entityManagerFactory, info.properties);
try {
ContainerSystem containerSystem = SystemInstance.get().getComponent(ContainerSystem.class);
Object o = containerSystem.getJNDIContext().lookup(PersistenceBuilder.getOpenEJBJndiName(info.unitId));
extendedEntityManagerFactories.put((EntityManagerFactory) o, info.properties);
} catch (NamingException e) {
throw new OpenEJBException("PersistenceUnit '" + info.unitId + "' not found for EXTENDED ref '" + info.referenceName + "'");
}
}
}
deployment.setExtendedEntityManagerFactories(new Index<EntityManagerFactory, Map>(extendedEntityManagerFactories));
}
if (ejbType.isSession() || ejbType.isMessageDriven()) {
deployment.setBeanManagedTransaction("Bean".equalsIgnoreCase(bean.transactionType));
}
if (ejbType.isSession()) {
// Allow dependsOn to work for all session beans
deployment.getDependsOn().addAll(bean.dependsOn);
}
if (ejbType == BeanType.SINGLETON) {
deployment.setBeanManagedConcurrency("Bean".equalsIgnoreCase(bean.concurrencyType));
deployment.setLoadOnStartup(bean.loadOnStartup);
}
if (ejbType.isEntity()) {
EntityBeanInfo entity = (EntityBeanInfo) bean;
deployment.setCmp2(entity.cmpVersion == 2);
deployment.setIsReentrant(entity.reentrant.equalsIgnoreCase("true"));
if (ejbType == BeanType.CMP_ENTITY) {
Class cmpImplClass = null;
String cmpImplClassName = CmpUtil.getCmpImplClassName(entity.abstractSchemaName, entity.ejbClass);
cmpImplClass = loadClass(cmpImplClassName, "classNotFound.cmpImplClass");
deployment.setCmpImplClass(cmpImplClass);
deployment.setAbstractSchemaName(entity.abstractSchemaName);
for (QueryInfo query : entity.queries) {
if (query.remoteResultType) {
StringBuilder methodSignature = new StringBuilder();
methodSignature.append(query.method.methodName);
if (query.method.methodParams != null && !query.method.methodParams.isEmpty()) {
methodSignature.append('(');
boolean first = true;
for (String methodParam : query.method.methodParams) {
if (!first) methodSignature.append(",");
methodSignature.append(methodParam);
first = false;
}
methodSignature.append(')');
}
deployment.setRemoteQueryResults(methodSignature.toString());
}
}
if (entity.primKeyField != null) {
deployment.setPrimaryKeyField(entity.primKeyField);
}
}
}
deployment.createMethodMap();
//Configure asynchronous tag after the method map is created, so while we check whether the method is asynchronous,
//we could directly check the matching bean method.
if (ejbType == BeanType.STATELESS || ejbType == BeanType.SINGLETON || ejbType == BeanType.STATEFUL) {
for (NamedMethodInfo methodInfo : bean.asynchronous) {
Method method = MethodInfoUtil.toMethod(ejbClass, methodInfo);
deployment.getMethodContext(deployment.getMatchingBeanMethod(method)).setAsynchronous(true);
}
for (String className : bean.asynchronousClasses) {
deployment.getAsynchronousClasses().add(loadClass(className, "classNotFound.ejbClass"));
}
deployment.createAsynchronousMethodSet();
}
return deployment;
}
private void bind(Context compJndiContext, String s, Context moduleJndiContext) throws OpenEJBException {
Context c;
try {
c = (Context) moduleJndiContext.lookup(s);
} catch (NamingException e) {
//ok, nothing there....
return;
}
try {
compJndiContext.bind(s, c);
} catch (NamingException e) {
throw new OpenEJBException("Could not bind context at " + s, e);
}
}
public static boolean paramsMatch(Method methodA, Method methodB) {
if (methodA.getParameterTypes().length != methodB.getParameterTypes().length){
return false;
}
for (int i = 0; i < methodA.getParameterTypes().length; i++) {
Class<?> a = methodA.getParameterTypes()[i];
Class<?> b = methodB.getParameterTypes()[i];
if (!a.equals(b)) return false;
}
return true;
}
public List<Exception> getWarnings() {
return warnings;
}
private Method getCallback(Class ejbClass, List<CallbackInfo> callbackInfos) {
Method callback = null;
for (CallbackInfo info : callbackInfos) {
try {
if (ejbClass.getName().equals(info.className)) {
if (callback != null) {
throw new IllegalStateException("Spec requirements only allow one callback method of a given type per class. The following callback will be ignored: " + info.className + "." + info.method);
}
try {
callback = ejbClass.getMethod(info.method);
} catch (NoSuchMethodException e) {
throw (IllegalStateException) new IllegalStateException("Callback method does not exist: " + info.className + "." + info.method).initCause(e);
}
} else {
throw new UnsupportedOperationException("Callback: " + info.className + "." + info.method + " -- We currently do not support callbacks where the callback class is not the bean class. If you need this feature, please let us know and we will complete it asap.");
}
} catch (Exception e) {
warnings.add(e);
}
}
return callback;
}
private Method getTimeout(Class ejbClass, NamedMethodInfo info) {
Method timeout = null;
try {
if (TimedObject.class.isAssignableFrom(ejbClass)) {
timeout = ejbClass.getMethod("ejbTimeout", Timer.class);
} else if (info != null){
try {
timeout = MethodInfoUtil.toMethod(ejbClass, info);
} catch (IllegalStateException e) {
//Spec 18.2.5.3 [102] For the compatibility of timeout method signature, if method-params is not set, it is also required to search the method signaure below :
//void <METHOD> (Timer timer)
// TODO Lets move this fallback searching into the config side so people do
// not get 'Callback method does not exist' runtime exceptions and instead
// get a validation failure. Then we can explicitly add the (Timer) param
// if the fallback method does exist.
if (info.methodParams == null) {
NamedMethodInfo candidateInfo = new NamedMethodInfo();
candidateInfo.className = info.className;
candidateInfo.id = info.id;
candidateInfo.methodName = info.methodName;
candidateInfo.methodParams = Arrays.asList(Timer.class.getName());
timeout = MethodInfoUtil.toMethod(ejbClass, candidateInfo);
}
}
}
} catch (Exception e) {
warnings.add(e);
}
return timeout;
}
private Class loadClass(String className, String messageCode) throws OpenEJBException {
Class clazz = load(className, messageCode);
try {
// clazz.getDeclaredMethods();
// clazz.getDeclaredFields();
// clazz.getDeclaredConstructors();
// clazz.getInterfaces();
return clazz;
} catch (NoClassDefFoundError e) {
if (clazz.getClassLoader() != moduleContext.getClassLoader()) {
String message = SafeToolkit.messages.format("cl0008", className, clazz.getClassLoader(), moduleContext.getClassLoader(), e.getMessage());
throw new OpenEJBException(AssemblerTool.messages.format(messageCode, className, bean.ejbDeploymentId, message), e);
} else {
String message = SafeToolkit.messages.format("cl0009", className, clazz.getClassLoader(), e.getMessage());
throw new OpenEJBException(AssemblerTool.messages.format(messageCode, className, bean.ejbDeploymentId, message), e);
}
}
}
private Class load(String className, String messageCode) throws OpenEJBException {
try {
return Class.forName(className, true, moduleContext.getClassLoader());
} catch (ClassNotFoundException e) {
String message = SafeToolkit.messages.format("cl0007", className, bean.codebase);
throw new OpenEJBException(AssemblerTool.messages.format(messageCode, className, bean.ejbDeploymentId, message));
}
}
}