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.core.stateful;
017:
018: import java.lang.reflect.Method;
019: import java.rmi.RemoteException;
020: import java.rmi.dgc.VMID;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.List;
024: import java.util.Map;
025:
026: import javax.ejb.EJBAccessException;
027: import javax.ejb.EJBException;
028: import javax.ejb.EJBHome;
029: import javax.ejb.EJBLocalHome;
030: import javax.ejb.RemoveException;
031: import javax.persistence.EntityManager;
032: import javax.persistence.EntityManagerFactory;
033: import javax.transaction.TransactionManager;
034: import javax.transaction.TransactionRequiredException;
035:
036: import org.apache.openejb.ApplicationException;
037: import org.apache.openejb.ContainerType;
038: import org.apache.openejb.DeploymentInfo;
039: import org.apache.openejb.OpenEJBException;
040: import org.apache.openejb.ProxyInfo;
041: import org.apache.openejb.RpcContainer;
042: import org.apache.openejb.InvalidateReferenceException;
043: import org.apache.openejb.InterfaceType;
044: import org.apache.openejb.core.CoreDeploymentInfo;
045: import org.apache.openejb.core.Operation;
046: import org.apache.openejb.core.ThreadContext;
047: import org.apache.openejb.core.ExceptionType;
048: import org.apache.openejb.core.interceptor.InterceptorData;
049: import org.apache.openejb.core.interceptor.InterceptorStack;
050: import org.apache.openejb.core.transaction.TransactionContainer;
051: import org.apache.openejb.core.transaction.TransactionContext;
052: import org.apache.openejb.core.transaction.TransactionPolicy;
053: import org.apache.openejb.loader.SystemInstance;
054: import org.apache.openejb.persistence.EntityManagerAlreadyRegisteredException;
055: import org.apache.openejb.persistence.JtaEntityManagerRegistry;
056: import org.apache.openejb.spi.SecurityService;
057: import org.apache.openejb.util.Index;
058: import org.apache.openejb.util.LogCategory;
059: import org.apache.openejb.util.Logger;
060:
061: /**
062: * @org.apache.xbean.XBean element="statefulContainer"
063: */
064: public class StatefulContainer implements RpcContainer,
065: TransactionContainer {
066: private static final Logger logger = Logger.getInstance(
067: LogCategory.OPENEJB, "org.apache.openejb.util.resources");
068:
069: private final Object containerID;
070: private final TransactionManager transactionManager;
071: private final SecurityService securityService;
072: protected final StatefulInstanceManager instanceManager;
073: // todo this should be part of the constructor
074: protected final JtaEntityManagerRegistry entityManagerRegistry = SystemInstance
075: .get().getComponent(JtaEntityManagerRegistry.class);
076:
077: /**
078: * Index used for getDeployments() and getDeploymentInfo(deploymentId).
079: */
080: protected final Map<Object, DeploymentInfo> deploymentsById = new HashMap<Object, DeploymentInfo>();
081:
082: public StatefulContainer(Object id,
083: TransactionManager transactionManager,
084: SecurityService securityService, Class passivator,
085: int timeOut, int poolSize, int bulkPassivate)
086: throws OpenEJBException {
087: this .containerID = id;
088: this .transactionManager = transactionManager;
089: this .securityService = securityService;
090:
091: instanceManager = newStatefulInstanceManager(
092: transactionManager, securityService, passivator,
093: timeOut, poolSize, bulkPassivate);
094: }
095:
096: protected StatefulInstanceManager newStatefulInstanceManager(
097: TransactionManager transactionManager,
098: SecurityService securityService, Class passivator,
099: int timeOut, int poolSize, int bulkPassivate)
100: throws OpenEJBException {
101: return new StatefulInstanceManager(transactionManager,
102: securityService, entityManagerRegistry, passivator,
103: timeOut, poolSize, bulkPassivate);
104: }
105:
106: private Map<Method, MethodType> getLifecycleMethodsOfInterface(
107: CoreDeploymentInfo deploymentInfo) {
108: Map<Method, MethodType> methods = new HashMap<Method, MethodType>();
109:
110: List<Method> removeMethods = deploymentInfo.getRemoveMethods();
111: for (Method removeMethod : removeMethods) {
112: methods.put(removeMethod, MethodType.REMOVE);
113:
114: for (Class businessLocal : deploymentInfo
115: .getBusinessLocalInterfaces()) {
116: try {
117: Method method = businessLocal
118: .getMethod(removeMethod.getName());
119: methods.put(method, MethodType.REMOVE);
120: } catch (NoSuchMethodException thatsFine) {
121: }
122: }
123:
124: for (Class businessRemote : deploymentInfo
125: .getBusinessRemoteInterfaces()) {
126: try {
127: Method method = businessRemote
128: .getMethod(removeMethod.getName());
129: methods.put(method, MethodType.REMOVE);
130: } catch (NoSuchMethodException thatsFine) {
131: }
132: }
133: }
134:
135: Class legacyRemote = deploymentInfo.getRemoteInterface();
136: if (legacyRemote != null) {
137: try {
138: Method method = legacyRemote.getMethod("remove");
139: methods.put(method, MethodType.REMOVE);
140: } catch (NoSuchMethodException thatsFine) {
141: }
142: }
143:
144: Class legacyLocal = deploymentInfo.getLocalInterface();
145: if (legacyLocal != null) {
146: try {
147: Method method = legacyLocal.getMethod("remove");
148: methods.put(method, MethodType.REMOVE);
149: } catch (NoSuchMethodException thatsFine) {
150: }
151: }
152:
153: Class businessLocalHomeInterface = deploymentInfo
154: .getBusinessLocalInterface();
155: if (businessLocalHomeInterface != null) {
156: for (Method method : DeploymentInfo.BusinessLocalHome.class
157: .getMethods()) {
158: if (method.getName().startsWith("create")) {
159: methods.put(method, MethodType.CREATE);
160: } else if (method.getName().equals("remove")) {
161: methods.put(method, MethodType.REMOVE);
162: }
163: }
164: }
165:
166: Class businessRemoteHomeInterface = deploymentInfo
167: .getBusinessRemoteInterface();
168: if (businessRemoteHomeInterface != null) {
169: for (Method method : DeploymentInfo.BusinessRemoteHome.class
170: .getMethods()) {
171: if (method.getName().startsWith("create")) {
172: methods.put(method, MethodType.CREATE);
173: } else if (method.getName().equals("remove")) {
174: methods.put(method, MethodType.REMOVE);
175: }
176: }
177: }
178:
179: Class homeInterface = deploymentInfo.getHomeInterface();
180: if (homeInterface != null) {
181: for (Method method : homeInterface.getMethods()) {
182: if (method.getName().startsWith("create")) {
183: methods.put(method, MethodType.CREATE);
184: } else if (method.getName().equals("remove")) {
185: methods.put(method, MethodType.REMOVE);
186: }
187: }
188: }
189:
190: Class localHomeInterface = deploymentInfo
191: .getLocalHomeInterface();
192: if (localHomeInterface != null) {
193: for (Method method : localHomeInterface.getMethods()) {
194: if (method.getName().startsWith("create")) {
195: methods.put(method, MethodType.CREATE);
196: } else if (method.getName().equals("remove")) {
197: methods.put(method, MethodType.REMOVE);
198: }
199: }
200: }
201: return methods;
202: }
203:
204: public static enum MethodType {
205: CREATE, REMOVE, BUSINESS
206: }
207:
208: public ContainerType getContainerType() {
209: return ContainerType.STATEFUL;
210: }
211:
212: public Object getContainerID() {
213: return containerID;
214: }
215:
216: public StatefulInstanceManager getInstanceManager() {
217: return instanceManager;
218: }
219:
220: public synchronized DeploymentInfo[] deployments() {
221: return deploymentsById.values().toArray(
222: new DeploymentInfo[deploymentsById.size()]);
223: }
224:
225: public synchronized DeploymentInfo getDeploymentInfo(
226: Object deploymentID) {
227: return deploymentsById.get(deploymentID);
228: }
229:
230: public void deploy(DeploymentInfo deploymentInfo)
231: throws OpenEJBException {
232: deploy((CoreDeploymentInfo) deploymentInfo);
233: }
234:
235: public void undeploy(DeploymentInfo info) throws OpenEJBException {
236: undeploy((CoreDeploymentInfo) info);
237: }
238:
239: private synchronized void undeploy(CoreDeploymentInfo deploymentInfo)
240: throws OpenEJBException {
241: deploymentsById.remove(deploymentInfo.getDeploymentID());
242: deploymentInfo.setContainer(null);
243: deploymentInfo.setContainerData(null);
244: instanceManager.undeploy(deploymentInfo);
245: }
246:
247: private synchronized void deploy(CoreDeploymentInfo deploymentInfo)
248: throws OpenEJBException {
249: Map<Method, MethodType> methods = getLifecycleMethodsOfInterface(deploymentInfo);
250:
251: deploymentsById.put(deploymentInfo.getDeploymentID(),
252: deploymentInfo);
253: deploymentInfo.setContainer(this );
254: instanceManager.deploy(deploymentInfo,
255: new Index<Method, MethodType>(methods));
256: }
257:
258: /**
259: * @deprecated use invoke signature without 'securityIdentity' argument.
260: */
261: public Object invoke(Object deployID, Method callMethod,
262: Object[] args, Object primKey, Object securityIdentity)
263: throws OpenEJBException {
264: return invoke(deployID, callMethod.getDeclaringClass(),
265: callMethod, args, primKey);
266: }
267:
268: public Object invoke(Object deployID, Class callInterface,
269: Method callMethod, Object[] args, Object primKey)
270: throws OpenEJBException {
271: CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) this
272: .getDeploymentInfo(deployID);
273: if (deployInfo == null)
274: throw new OpenEJBException(
275: "Deployment does not exist in this container. Deployment(id='"
276: + deployID + "'), Container(id='"
277: + containerID + "')");
278:
279: MethodType methodType = instanceManager.getMethodIndex(
280: deployInfo).get(callMethod);
281: methodType = (methodType != null) ? methodType
282: : MethodType.BUSINESS;
283:
284: switch (methodType) {
285: case CREATE:
286: return createEJBObject(deployInfo, callInterface,
287: callMethod, args);
288: case REMOVE:
289: return removeEJBObject(deployInfo, primKey, callInterface,
290: callMethod, args);
291: default:
292: return businessMethod(deployInfo, primKey, callInterface,
293: callMethod, args);
294: }
295: }
296:
297: protected ProxyInfo createEJBObject(
298: CoreDeploymentInfo deploymentInfo, Class callInterface,
299: Method callMethod, Object[] args) throws OpenEJBException {
300: // generate a new primary key
301: Object primaryKey = newPrimaryKey();
302:
303: ThreadContext createContext = new ThreadContext(deploymentInfo,
304: primaryKey);
305: createContext.setCurrentOperation(Operation.CREATE);
306: createContext.setCurrentAllowedStates(StatefulContext
307: .getStates());
308: ThreadContext oldCallContext = ThreadContext
309: .enter(createContext);
310:
311: try {
312: checkAuthorization(deploymentInfo, callMethod,
313: callInterface);
314:
315: // create the extended entity managers
316: Index<EntityManagerFactory, EntityManager> entityManagers = createEntityManagers(deploymentInfo);
317: // register them
318: if (entityManagers != null) {
319: try {
320: entityManagerRegistry.addEntityManagers(
321: (String) deploymentInfo.getDeploymentID(),
322: primaryKey, entityManagers);
323: } catch (EntityManagerAlreadyRegisteredException e) {
324: throw new EJBException(e);
325: }
326: }
327:
328: // allocate a new instance
329: Object o = instanceManager.newInstance(primaryKey,
330: deploymentInfo.getBeanClass());
331: StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) o;
332:
333: instanceManager.setEntityManagers(createContext,
334: entityManagers);
335:
336: if (!callMethod.getDeclaringClass().equals(
337: DeploymentInfo.BusinessLocalHome.class)
338: && !callMethod.getDeclaringClass().equals(
339: DeploymentInfo.BusinessRemoteHome.class)) {
340:
341: Method createOrInit = deploymentInfo
342: .getMatchingBeanMethod(callMethod);
343:
344: InterceptorStack interceptorStack = new InterceptorStack(
345: instance.bean, createOrInit, Operation.CREATE,
346: new ArrayList<InterceptorData>(),
347: new HashMap<String, Object>());
348:
349: _invoke(callMethod, interceptorStack, args, instance,
350: createContext);
351: }
352:
353: instanceManager.poolInstance(createContext, instance);
354:
355: return new ProxyInfo(deploymentInfo, primaryKey);
356: } finally {
357: ThreadContext.exit(oldCallContext);
358: }
359: }
360:
361: protected Object newPrimaryKey() {
362: return new VMID();
363: }
364:
365: protected Object removeEJBObject(CoreDeploymentInfo deploymentInfo,
366: Object primKey, Class callInterface, Method callMethod,
367: Object[] args) throws OpenEJBException {
368: ThreadContext callContext = new ThreadContext(deploymentInfo,
369: primKey);
370: ThreadContext oldCallContext = ThreadContext.enter(callContext);
371: try {
372: checkAuthorization(deploymentInfo, callMethod,
373: callInterface);
374:
375: InterfaceType type = deploymentInfo
376: .getInterfaceType(callInterface);
377: if (type.isComponent()
378: && instanceManager.getBeanTransaction(callContext) != null) {
379: throw new ApplicationException(
380: new RemoveException(
381: "A stateful EJB enrolled in a transaction can not be removed"));
382: }
383:
384: Method runMethod = deploymentInfo
385: .getMatchingBeanMethod(callMethod);
386: StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) instanceManager
387: .obtainInstance(primKey, callContext);
388:
389: if (instance == null)
390: throw new ApplicationException(
391: new javax.ejb.NoSuchEJBException());
392:
393: boolean retain = false;
394: try {
395: callContext.setCurrentAllowedStates(StatefulContext
396: .getStates());
397: callContext.setCurrentOperation(Operation.REMOVE);
398: callContext.setInvokedInterface(callInterface);
399:
400: Class<?> declaringClass = callMethod
401: .getDeclaringClass();
402: if (declaringClass.equals(EJBHome.class)
403: || declaringClass.equals(EJBLocalHome.class)) {
404: args = new Object[] {}; // no args to pass on home.remove(remote) calls
405: }
406:
407: List<InterceptorData> interceptors = deploymentInfo
408: .getMethodInterceptors(runMethod);
409: InterceptorStack interceptorStack = new InterceptorStack(
410: instance.bean, runMethod, Operation.REMOVE,
411: interceptors, instance.interceptors);
412: return _invoke(callMethod, interceptorStack, args,
413: instance, callContext);
414:
415: } catch (InvalidateReferenceException e) {
416: throw e;
417: } catch (ApplicationException e) {
418: if (type.isBusiness()) {
419: retain = deploymentInfo.retainIfExeption(runMethod);
420: throw e;
421: } else {
422: return null;
423: }
424: } finally {
425: if (retain) {
426: instanceManager.poolInstance(callContext, instance);
427: } else {
428: callContext
429: .setCurrentOperation(Operation.PRE_DESTROY);
430:
431: try {
432: List<InterceptorData> callbackInterceptors = deploymentInfo
433: .getCallbackInterceptors();
434: InterceptorStack interceptorStack = new InterceptorStack(
435: instance.bean, null,
436: Operation.PRE_DESTROY,
437: callbackInterceptors,
438: instance.interceptors);
439: interceptorStack.invoke();
440: } catch (Throwable callbackException) {
441: String logMessage = "An unexpected exception occured while invoking the preDestroy method on the removed Stateful SessionBean instance; "
442: + callbackException.getClass()
443: .getName()
444: + " "
445: + callbackException.getMessage();
446:
447: /* [1] Log the exception or error */
448: logger.error(logMessage);
449:
450: } finally {
451: callContext
452: .setCurrentOperation(Operation.REMOVE);
453: }
454:
455: // todo destroy extended persistence contexts
456: instanceManager.freeInstance(callContext);
457: }
458: }
459: } finally {
460: ThreadContext.exit(oldCallContext);
461: }
462: }
463:
464: protected Object businessMethod(CoreDeploymentInfo deploymentInfo,
465: Object primKey, Class callInterface, Method callMethod,
466: Object[] args) throws OpenEJBException {
467: ThreadContext callContext = new ThreadContext(deploymentInfo,
468: primKey);
469: ThreadContext oldCallContext = ThreadContext.enter(callContext);
470: try {
471: checkAuthorization(deploymentInfo, callMethod,
472: callInterface);
473:
474: Object bean = instanceManager.obtainInstance(primKey,
475: callContext);
476: callContext.setCurrentOperation(Operation.BUSINESS);
477: callContext.setCurrentAllowedStates(StatefulContext
478: .getStates());
479: callContext.setInvokedInterface(callInterface);
480: Method runMethod = deploymentInfo
481: .getMatchingBeanMethod(callMethod);
482:
483: callContext.set(Method.class, runMethod);
484:
485: StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) bean;
486:
487: List<InterceptorData> interceptors = deploymentInfo
488: .getMethodInterceptors(runMethod);
489: InterceptorStack interceptorStack = new InterceptorStack(
490: instance.bean, runMethod, Operation.BUSINESS,
491: interceptors, instance.interceptors);
492: Object returnValue = _invoke(callMethod, interceptorStack,
493: args, bean, callContext);
494:
495: instanceManager.poolInstance(callContext, bean);
496:
497: return returnValue;
498: } finally {
499: ThreadContext.exit(oldCallContext);
500: }
501: }
502:
503: private void checkAuthorization(CoreDeploymentInfo deployInfo,
504: Method callMethod, Class callInterface)
505: throws ApplicationException {
506: boolean authorized = securityService.isCallerAuthorized(
507: callMethod, deployInfo.getInterfaceType(callInterface));
508: if (!authorized) {
509: throw new ApplicationException(new EJBAccessException(
510: "Unauthorized Access by Principal Denied"));
511: }
512: }
513:
514: protected Object _invoke(Method callMethod,
515: InterceptorStack interceptorStack, Object[] args,
516: Object bean, ThreadContext callContext)
517: throws OpenEJBException {
518:
519: TransactionPolicy txPolicy = callContext.getDeploymentInfo()
520: .getTransactionPolicy(callMethod);
521: TransactionContext txContext = new TransactionContext(
522: callContext, transactionManager);
523: try {
524: txPolicy.beforeInvoke(bean, txContext);
525: } catch (ApplicationException e) {
526: if (e.getRootCause() instanceof TransactionRequiredException
527: || e.getRootCause() instanceof RemoteException) {
528:
529: instanceManager.poolInstance(callContext, bean);
530: }
531: throw e;
532: }
533:
534: Object returnValue = null;
535: try {
536: registerEntityManagers(callContext);
537: if (args == null) {
538: returnValue = interceptorStack.invoke();
539: } else {
540: returnValue = interceptorStack.invoke(args);
541: }
542: } catch (Throwable re) {// handle reflection exception
543: ExceptionType type = callContext.getDeploymentInfo()
544: .getExceptionType(re);
545: if (type == ExceptionType.SYSTEM) {
546: /* System Exception ****************************/
547:
548: txPolicy.handleSystemException(re, bean, txContext);
549: } else {
550: /* Application Exception ***********************/
551: instanceManager.poolInstance(callContext, bean);
552:
553: txPolicy.handleApplicationException(re,
554: type == ExceptionType.APPLICATION_ROLLBACK,
555: txContext);
556: }
557: } finally {
558: unregisterEntityManagers(callContext);
559: txPolicy.afterInvoke(bean, txContext);
560: }
561:
562: return returnValue;
563: }
564:
565: private Index<EntityManagerFactory, EntityManager> createEntityManagers(
566: CoreDeploymentInfo deploymentInfo) {
567: // create the extended entity managers
568: Index<EntityManagerFactory, Map> factories = deploymentInfo
569: .getExtendedEntityManagerFactories();
570: Index<EntityManagerFactory, EntityManager> entityManagers = null;
571: if (factories != null && factories.size() > 0) {
572: entityManagers = new Index<EntityManagerFactory, EntityManager>(
573: new ArrayList<EntityManagerFactory>(factories
574: .keySet()));
575: for (Map.Entry<EntityManagerFactory, Map> entry : factories
576: .entrySet()) {
577: EntityManagerFactory entityManagerFactory = entry
578: .getKey();
579: Map properties = entry.getValue();
580:
581: EntityManager entityManager = entityManagerRegistry
582: .getInheritedEntityManager(entityManagerFactory);
583: if (entityManager == null) {
584: if (properties != null) {
585: entityManager = entityManagerFactory
586: .createEntityManager(properties);
587: } else {
588: entityManager = entityManagerFactory
589: .createEntityManager();
590: }
591: }
592: entityManagers.put(entityManagerFactory, entityManager);
593: }
594: }
595: return entityManagers;
596: }
597:
598: private void registerEntityManagers(ThreadContext callContext)
599: throws OpenEJBException {
600: if (entityManagerRegistry == null)
601: return;
602:
603: CoreDeploymentInfo deploymentInfo = callContext
604: .getDeploymentInfo();
605:
606: // get the factories
607: Index<EntityManagerFactory, Map> factories = deploymentInfo
608: .getExtendedEntityManagerFactories();
609: if (factories == null)
610: return;
611:
612: // get the managers for the factories
613: Object primaryKey = callContext.getPrimaryKey();
614: Map<EntityManagerFactory, EntityManager> entityManagers = instanceManager
615: .getEntityManagers(callContext, factories);
616: if (entityManagers == null)
617: return;
618:
619: // register them
620: try {
621: entityManagerRegistry.addEntityManagers(
622: (String) deploymentInfo.getDeploymentID(),
623: primaryKey, entityManagers);
624: } catch (EntityManagerAlreadyRegisteredException e) {
625: throw new EJBException(e);
626: }
627: }
628:
629: private void unregisterEntityManagers(ThreadContext callContext) {
630: if (entityManagerRegistry == null)
631: return;
632:
633: CoreDeploymentInfo deploymentInfo = callContext
634: .getDeploymentInfo();
635:
636: // get the managers for the factories
637: Object primaryKey = callContext.getPrimaryKey();
638:
639: // register them
640: entityManagerRegistry.removeEntityManagers(
641: (String) deploymentInfo.getDeploymentID(), primaryKey);
642: }
643:
644: public void discardInstance(Object bean, ThreadContext threadContext) {
645: try {
646: instanceManager.freeInstance(threadContext);
647: } catch (Throwable t) {
648: logger.error("", t);
649: }
650: }
651: }
|