001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.assembler.classic;
017:
018: import static org.apache.openejb.assembler.classic.MethodInfoUtil.matchingMethods;
019: import org.apache.openejb.BeanType;
020: import org.apache.openejb.Injection;
021: import org.apache.openejb.OpenEJBException;
022: import org.apache.openejb.loader.SystemInstance;
023: import org.apache.openejb.spi.ContainerSystem;
024: import org.apache.openejb.core.CoreDeploymentInfo;
025: import org.apache.openejb.core.DeploymentContext;
026: import org.apache.openejb.core.cmp.CmpUtil;
027: import org.apache.openejb.util.Index;
028: import org.apache.openejb.util.Messages;
029: import org.apache.openejb.util.SafeToolkit;
030: import org.apache.openejb.util.Classes;
031:
032: import javax.naming.Context;
033: import javax.naming.NamingException;
034: import javax.persistence.EntityManagerFactory;
035: import javax.ejb.TimedObject;
036: import javax.ejb.Timer;
037: import java.lang.reflect.Method;
038: import java.util.ArrayList;
039: import java.util.HashMap;
040: import java.util.List;
041: import java.util.Map;
042: import java.util.Arrays;
043:
044: class EnterpriseBeanBuilder {
045: protected static final Messages messages = new Messages(
046: "org.apache.openejb.util.resources");
047: private final EnterpriseBeanInfo bean;
048: private final String moduleId;
049: private final List<String> defaultInterceptors;
050: private final BeanType ejbType;
051: private final ClassLoader cl;
052: private List<Exception> warnings = new ArrayList<Exception>();
053:
054: public EnterpriseBeanBuilder(ClassLoader cl,
055: EnterpriseBeanInfo bean, String moduleId,
056: List<String> defaultInterceptors) {
057: this .bean = bean;
058: this .moduleId = moduleId;
059: this .defaultInterceptors = defaultInterceptors;
060:
061: if (bean.type == EnterpriseBeanInfo.STATEFUL) {
062: ejbType = BeanType.STATEFUL;
063: } else if (bean.type == EnterpriseBeanInfo.STATELESS) {
064: ejbType = BeanType.STATELESS;
065: } else if (bean.type == EnterpriseBeanInfo.MESSAGE) {
066: ejbType = BeanType.MESSAGE_DRIVEN;
067: } else if (bean.type == EnterpriseBeanInfo.ENTITY) {
068: String persistenceType = ((EntityBeanInfo) bean).persistenceType;
069: ejbType = (persistenceType.equalsIgnoreCase("Container")) ? BeanType.CMP_ENTITY
070: : BeanType.BMP_ENTITY;
071: } else {
072: throw new UnsupportedOperationException(
073: "No building support for bean type: " + bean);
074: }
075: this .cl = cl;
076: }
077:
078: public Object build() throws OpenEJBException {
079: Class ejbClass = loadClass(bean.ejbClass,
080: "classNotFound.ejbClass");
081:
082: Class home = null;
083: Class remote = null;
084: if (bean.home != null) {
085: home = loadClass(bean.home, "classNotFound.home");
086: remote = loadClass(bean.remote, "classNotFound.remote");
087: }
088:
089: Class localhome = null;
090: Class local = null;
091: if (bean.localHome != null) {
092: localhome = loadClass(bean.localHome,
093: "classNotFound.localHome");
094: local = loadClass(bean.local, "classNotFound.local");
095: }
096:
097: List<Class> businessLocals = new ArrayList<Class>();
098: for (String businessLocal : bean.businessLocal) {
099: businessLocals.add(loadClass(businessLocal,
100: "classNotFound.businessLocal"));
101: }
102:
103: List<Class> businessRemotes = new ArrayList<Class>();
104: for (String businessRemote : bean.businessRemote) {
105: businessRemotes.add(loadClass(businessRemote,
106: "classNotFound.businessRemote"));
107: }
108:
109: Class serviceEndpoint = null;
110: if (BeanType.STATELESS == ejbType) {
111: if (bean.serviceEndpoint != null) {
112: serviceEndpoint = loadClass(bean.serviceEndpoint,
113: "classNotFound.sei");
114: }
115: }
116:
117: Class primaryKey = null;
118: if (ejbType.isEntity()
119: && ((EntityBeanInfo) bean).primKeyClass != null) {
120: String className = ((EntityBeanInfo) bean).primKeyClass;
121: primaryKey = loadClass(className,
122: "classNotFound.primaryKey");
123: }
124:
125: final String transactionType = bean.transactionType;
126:
127: // determind the injections
128: InjectionBuilder injectionBuilder = new InjectionBuilder(cl);
129: List<Injection> injections = injectionBuilder
130: .buildInjections(bean.jndiEnc);
131:
132: // build the enc
133: JndiEncBuilder jndiEncBuilder = new JndiEncBuilder(
134: bean.jndiEnc, injections, transactionType, moduleId, cl);
135: Context root = jndiEncBuilder.build();
136:
137: DeploymentContext deploymentContext = new DeploymentContext(
138: bean.ejbDeploymentId, cl, root);
139: CoreDeploymentInfo deployment;
140: if (BeanType.MESSAGE_DRIVEN != ejbType) {
141: deployment = new CoreDeploymentInfo(deploymentContext,
142: ejbClass, home, remote, localhome, local,
143: serviceEndpoint, businessLocals, businessRemotes,
144: primaryKey, ejbType);
145: } else {
146: MessageDrivenBeanInfo messageDrivenBeanInfo = (MessageDrivenBeanInfo) bean;
147: Class mdbInterface = loadClass(
148: messageDrivenBeanInfo.mdbInterface,
149: "classNotFound.mdbInterface");
150: deployment = new CoreDeploymentInfo(deploymentContext,
151: ejbClass, mdbInterface,
152: messageDrivenBeanInfo.activationProperties);
153: deployment
154: .setDestinationId(messageDrivenBeanInfo.destinationId);
155: }
156:
157: deployment.setEjbName(bean.ejbName);
158:
159: deployment.setModuleId(moduleId);
160:
161: deployment.setRunAs(bean.runAs);
162:
163: for (SecurityRoleReferenceInfo roleReferenceInfo : bean.securityRoleReferences) {
164: String alias = roleReferenceInfo.roleName;
165: String actualName = roleReferenceInfo.roleLink;
166:
167: // EJB 3.0 - 17.2.5.3
168: // In the absence of this linking step, any security role name as used in the code will be assumed to
169: // correspond to a security role of the same name.
170: if (actualName == null) {
171: actualName = alias;
172: }
173:
174: deployment.addSecurityRoleReference(alias, actualName);
175: }
176:
177: deployment.getInjections().addAll(injections);
178:
179: // ejbTimeout
180: deployment.setEjbTimeout(getTimeout(ejbClass,
181: bean.timeoutMethod));
182:
183: if (bean instanceof StatefulBeanInfo) {
184: StatefulBeanInfo statefulBeanInfo = (StatefulBeanInfo) bean;
185:
186: for (InitMethodInfo init : statefulBeanInfo.initMethods) {
187: Method beanMethod = toMethod(ejbClass, init.beanMethod);
188: List<Method> methods = new ArrayList<Method>();
189:
190: if (home != null)
191: methods.addAll(Arrays.asList(home.getMethods()));
192: if (localhome != null)
193: methods.addAll(Arrays
194: .asList(localhome.getMethods()));
195:
196: for (Method homeMethod : methods) {
197: if (init.createMethod != null
198: && !init.createMethod.methodName
199: .equals(homeMethod.getName()))
200: continue;
201:
202: if (!homeMethod.getName().startsWith("create"))
203: continue;
204:
205: if (paramsMatch(beanMethod, homeMethod)) {
206: deployment.mapMethods(homeMethod, beanMethod);
207: }
208: }
209: }
210:
211: for (RemoveMethodInfo removeMethod : statefulBeanInfo.removeMethods) {
212: Method method = toMethod(ejbClass,
213: removeMethod.beanMethod);
214: deployment.getRemoveMethods().add(method);
215: deployment.setRetainIfExeption(method,
216: removeMethod.retainIfException);
217: }
218:
219: Map<EntityManagerFactory, Map> extendedEntityManagerFactories = new HashMap<EntityManagerFactory, Map>();
220: for (PersistenceContextReferenceInfo info : statefulBeanInfo.jndiEnc.persistenceContextRefs) {
221: if (info.extended) {
222: // EntityManagerFactory entityManagerFactory = emfLinkResolver.resolveLink(info.persistenceUnitName, moduleId);
223: // extendedEntityManagerFactories.put(entityManagerFactory, info.properties);
224:
225: try {
226: ContainerSystem containerSystem = SystemInstance
227: .get().getComponent(
228: ContainerSystem.class);
229: Object o = containerSystem.getJNDIContext()
230: .lookup(
231: "openejb/PersistenceUnit/"
232: + info.unitId);
233: extendedEntityManagerFactories.put(
234: (EntityManagerFactory) o,
235: info.properties);
236: } catch (NamingException e) {
237: throw new OpenEJBException("PersistenceUnit '"
238: + info.unitId
239: + "' not found for EXTENDED ref '"
240: + info.referenceName + "'");
241: }
242:
243: }
244: }
245: deployment
246: .setExtendedEntityManagerFactories(new Index<EntityManagerFactory, Map>(
247: extendedEntityManagerFactories));
248: }
249:
250: if (ejbType.isSession() || ejbType.isMessageDriven()) {
251: deployment.setBeanManagedTransaction("Bean"
252: .equalsIgnoreCase(bean.transactionType));
253: }
254:
255: if (ejbType.isEntity()) {
256: EntityBeanInfo entity = (EntityBeanInfo) bean;
257:
258: deployment.setCmp2(entity.cmpVersion == 2);
259: deployment.setIsReentrant(entity.reentrant
260: .equalsIgnoreCase("true"));
261:
262: if (ejbType == BeanType.CMP_ENTITY) {
263: Class cmpImplClass = null;
264: String cmpImplClassName = CmpUtil.getCmpImplClassName(
265: entity.abstractSchemaName, entity.ejbClass);
266: cmpImplClass = loadClass(cmpImplClassName,
267: "classNotFound.cmpImplClass");
268: deployment.setCmpImplClass(cmpImplClass);
269: deployment
270: .setAbstractSchemaName(entity.abstractSchemaName);
271:
272: for (QueryInfo query : entity.queries) {
273: List<Method> finderMethods = new ArrayList<Method>();
274:
275: if (home != null) {
276: finderMethods.addAll(matchingMethods(
277: query.method, home));
278: }
279: if (localhome != null) {
280: finderMethods.addAll(matchingMethods(
281: query.method, localhome));
282: }
283:
284: for (Method method : finderMethods) {
285: deployment.addQuery(method,
286: query.queryStatement);
287: }
288:
289: if (query.remoteResultType) {
290: StringBuilder methodSignature = new StringBuilder();
291: methodSignature.append(query.method.methodName);
292: if (query.method.methodParams != null
293: && !query.method.methodParams.isEmpty()) {
294: methodSignature.append('(');
295: boolean first = true;
296: for (String methodParam : query.method.methodParams) {
297: if (!first)
298: methodSignature.append(",");
299: methodSignature.append(methodParam);
300: first = false;
301: }
302: methodSignature.append(')');
303: }
304: deployment
305: .setRemoteQueryResults(methodSignature
306: .toString());
307: }
308:
309: }
310: deployment.setCmrFields(entity.cmpFieldNames
311: .toArray(new String[] {}));
312:
313: if (entity.primKeyField != null) {
314: deployment.setPrimaryKeyField(entity.primKeyField);
315: }
316: }
317: }
318:
319: deployment.createMethodMap();
320:
321: return deployment;
322: }
323:
324: public static boolean paramsMatch(Method methodA, Method methodB) {
325: if (methodA.getParameterTypes().length != methodB
326: .getParameterTypes().length) {
327: return false;
328: }
329:
330: for (int i = 0; i < methodA.getParameterTypes().length; i++) {
331: Class<?> a = methodA.getParameterTypes()[i];
332: Class<?> b = methodB.getParameterTypes()[i];
333: if (!a.equals(b))
334: return false;
335: }
336: return true;
337: }
338:
339: public List<Exception> getWarnings() {
340: return warnings;
341: }
342:
343: private Method getCallback(Class ejbClass,
344: List<CallbackInfo> callbackInfos) {
345: Method callback = null;
346: for (CallbackInfo info : callbackInfos) {
347: try {
348: if (ejbClass.getName().equals(info.className)) {
349: if (callback != null) {
350: throw new IllegalStateException(
351: "Spec requirements only allow one callback method of a given type per class. The following callback will be ignored: "
352: + info.className
353: + "."
354: + info.method);
355: }
356: try {
357: callback = ejbClass.getMethod(info.method);
358: } catch (NoSuchMethodException e) {
359: throw (IllegalStateException) new IllegalStateException(
360: "Callback method does not exist: "
361: + info.className + "."
362: + info.method).initCause(e);
363: }
364:
365: } else {
366: throw new UnsupportedOperationException(
367: "Callback: "
368: + info.className
369: + "."
370: + info.method
371: + " -- 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.");
372: }
373: } catch (Exception e) {
374: warnings.add(e);
375: }
376: }
377: return callback;
378: }
379:
380: private Method getTimeout(Class ejbClass, NamedMethodInfo info) {
381: Method timeout = null;
382: try {
383: if (TimedObject.class.isAssignableFrom(ejbClass)) {
384: timeout = ejbClass.getMethod("ejbTimeout", Timer.class);
385: } else if (info.methodParams != null) {
386: timeout = toMethod(ejbClass, info);
387: }
388: } catch (Exception e) {
389: warnings.add(e);
390: }
391:
392: return timeout;
393: }
394:
395: private Method toMethod(Class clazz, NamedMethodInfo info) {
396: Method method = null;
397: List<Class> parameterTypes = new ArrayList<Class>();
398:
399: if (info.methodParams != null) {
400: for (String paramType : info.methodParams) {
401: try {
402: parameterTypes.add(Classes.forName(paramType, clazz
403: .getClassLoader()));
404: } catch (ClassNotFoundException cnfe) {
405: throw new IllegalStateException(
406: "Parameter class could not be loaded for type "
407: + paramType, cnfe);
408: }
409: }
410: }
411:
412: try {
413: method = clazz.getDeclaredMethod(info.methodName,
414: parameterTypes.toArray(new Class[parameterTypes
415: .size()]));
416: } catch (NoSuchMethodException e) {
417: throw new IllegalStateException(
418: "Callback method does not exist: "
419: + clazz.getName() + "." + info.methodName,
420: e);
421: }
422: return method;
423: }
424:
425: private Class loadClass(String className, String messageCode)
426: throws OpenEJBException {
427: Class clazz = load(className, messageCode);
428: try {
429: // clazz.getDeclaredMethods();
430: // clazz.getDeclaredFields();
431: // clazz.getDeclaredConstructors();
432: // clazz.getInterfaces();
433: return clazz;
434: } catch (NoClassDefFoundError e) {
435: if (clazz.getClassLoader() != cl) {
436: String message = SafeToolkit.messages.format("cl0008",
437: className, clazz.getClassLoader(), cl, e
438: .getMessage());
439: throw new OpenEJBException(AssemblerTool.messages
440: .format(messageCode, className,
441: bean.ejbDeploymentId, message), e);
442: } else {
443: String message = SafeToolkit.messages.format("cl0009",
444: className, clazz.getClassLoader(), e
445: .getMessage());
446: throw new OpenEJBException(AssemblerTool.messages
447: .format(messageCode, className,
448: bean.ejbDeploymentId, message), e);
449: }
450: }
451: }
452:
453: private Class load(String className, String messageCode)
454: throws OpenEJBException {
455: try {
456: return Class.forName(className, true, cl);
457: } catch (ClassNotFoundException e) {
458: String message = SafeToolkit.messages.format("cl0007",
459: className, bean.codebase);
460: throw new OpenEJBException(AssemblerTool.messages.format(
461: messageCode, className, bean.ejbDeploymentId,
462: message));
463: }
464: }
465: }
|