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.entity;
017:
018: import java.lang.reflect.Method;
019: import java.rmi.NoSuchObjectException;
020: import java.util.Collection;
021: import java.util.Enumeration;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.Vector;
026:
027: import javax.ejb.EJBAccessException;
028: import javax.ejb.EJBHome;
029: import javax.ejb.EJBLocalHome;
030: import javax.ejb.EJBLocalObject;
031: import javax.ejb.EJBObject;
032: import javax.ejb.EntityBean;
033: import javax.ejb.NoSuchEntityException;
034: import javax.ejb.Timer;
035: import javax.transaction.Transaction;
036: import javax.transaction.TransactionManager;
037: import javax.transaction.TransactionSynchronizationRegistry;
038:
039: import org.apache.openejb.ApplicationException;
040: import org.apache.openejb.ContainerType;
041: import org.apache.openejb.DeploymentInfo;
042: import org.apache.openejb.OpenEJBException;
043: import org.apache.openejb.ProxyInfo;
044: import org.apache.openejb.SystemException;
045: import org.apache.openejb.loader.SystemInstance;
046: import org.apache.openejb.core.BaseContext;
047: import org.apache.openejb.core.CoreDeploymentInfo;
048: import org.apache.openejb.core.Operation;
049: import org.apache.openejb.core.ThreadContext;
050: import org.apache.openejb.core.ExceptionType;
051: import org.apache.openejb.core.timer.EjbTimerService;
052: import org.apache.openejb.core.timer.EjbTimerServiceImpl;
053: import org.apache.openejb.core.transaction.TransactionContainer;
054: import org.apache.openejb.core.transaction.TransactionContext;
055: import org.apache.openejb.core.transaction.TransactionPolicy;
056: import org.apache.openejb.spi.SecurityService;
057: import org.apache.openejb.util.LogCategory;
058: import org.apache.openejb.util.Logger;
059:
060: /**
061: * @org.apache.xbean.XBean element="bmpContainer"
062: */
063: public class EntityContainer implements
064: org.apache.openejb.RpcContainer, TransactionContainer {
065:
066: private EntityInstanceManager instanceManager;
067:
068: private Map<String, CoreDeploymentInfo> deploymentRegistry = new HashMap<String, CoreDeploymentInfo>();
069:
070: private Object containerID = null;
071:
072: public Logger logger = Logger.getInstance(LogCategory.OPENEJB,
073: "org.apache.openejb.util.resources");
074: private TransactionManager transactionManager;
075: private SecurityService securityService;
076:
077: /**
078: * Tracks entity instances that have been "entered" so we can throw reentrancy exceptions.
079: */
080: protected EntrancyTracker entrancyTracker;
081:
082: public EntityContainer(Object id,
083: TransactionManager transactionManager,
084: SecurityService securityService, int poolSize)
085: throws OpenEJBException {
086: this .containerID = id;
087: this .transactionManager = transactionManager;
088: this .securityService = securityService;
089: entrancyTracker = new EntrancyTracker(SystemInstance.get()
090: .getComponent(TransactionSynchronizationRegistry.class));
091:
092: instanceManager = new EntityInstanceManager(this ,
093: transactionManager, securityService, poolSize);
094: }
095:
096: public synchronized DeploymentInfo[] deployments() {
097: return deploymentRegistry.values().toArray(
098: new DeploymentInfo[deploymentRegistry.size()]);
099: }
100:
101: public synchronized DeploymentInfo getDeploymentInfo(
102: Object deploymentID) {
103: String id = (String) deploymentID;
104: return deploymentRegistry.get(id);
105: }
106:
107: public ContainerType getContainerType() {
108: return ContainerType.BMP_ENTITY;
109: }
110:
111: public Object getContainerID() {
112: return containerID;
113: }
114:
115: public void deploy(DeploymentInfo info) throws OpenEJBException {
116: synchronized (this ) {
117: CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) info;
118: deploymentRegistry.put((String) deploymentInfo
119: .getDeploymentID(), deploymentInfo);
120: deploymentInfo.setContainer(this );
121: }
122: instanceManager.deploy(info);
123:
124: EjbTimerService timerService = info.getEjbTimerService();
125: if (timerService != null) {
126: timerService.start();
127: }
128: }
129:
130: public void undeploy(DeploymentInfo info) throws OpenEJBException {
131: EjbTimerService timerService = info.getEjbTimerService();
132: if (timerService != null) {
133: timerService.stop();
134: }
135:
136: instanceManager.undeploy(info);
137:
138: synchronized (this ) {
139: String id = (String) info.getDeploymentID();
140: deploymentRegistry.remove(id);
141: info.setContainer(null);
142: }
143: }
144:
145: /**
146: * @deprecated use invoke signature without 'securityIdentity' argument.
147: */
148: public Object invoke(Object deployID, Method callMethod,
149: Object[] args, Object primKey, Object securityIdentity)
150: throws OpenEJBException {
151: return invoke(deployID, callMethod.getDeclaringClass(),
152: callMethod, args, primKey);
153: }
154:
155: public Object invoke(Object deployID, Class callInterface,
156: Method callMethod, Object[] args, Object primKey)
157: throws org.apache.openejb.OpenEJBException {
158: CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) this
159: .getDeploymentInfo(deployID);
160: if (deployInfo == null)
161: throw new OpenEJBException(
162: "Deployment does not exist in this container. Deployment(id='"
163: + deployID + "'), Container(id='"
164: + containerID + "')");
165:
166: ThreadContext callContext = new ThreadContext(deployInfo,
167: primKey);
168: ThreadContext oldCallContext = ThreadContext.enter(callContext);
169: try {
170: boolean authorized = getSecurityService()
171: .isCallerAuthorized(callMethod,
172: deployInfo.getInterfaceType(callInterface));
173: if (!authorized)
174: throw new org.apache.openejb.ApplicationException(
175: new EJBAccessException(
176: "Unauthorized Access by Principal Denied"));
177:
178: Class declaringClass = callMethod.getDeclaringClass();
179: String methodName = callMethod.getName();
180:
181: if (EJBHome.class.isAssignableFrom(declaringClass)
182: || EJBLocalHome.class
183: .isAssignableFrom(declaringClass)) {
184: if (declaringClass != EJBHome.class
185: && declaringClass != EJBLocalHome.class) {
186:
187: if (methodName.startsWith("create")) {
188:
189: return createEJBObject(callMethod, args,
190: callContext);
191: } else if (methodName.startsWith("find")) {
192:
193: return findMethod(callMethod, args, callContext);
194: } else {
195:
196: return homeMethod(callMethod, args, callContext);
197: }
198: } else if (methodName.equals("remove")) {
199: removeEJBObject(callMethod, args, callContext);
200: return null;
201: }
202: } else if ((EJBObject.class == declaringClass || EJBLocalObject.class == declaringClass)
203: && methodName.equals("remove")) {
204: removeEJBObject(callMethod, args, callContext);
205: return null;
206: }
207:
208: callContext.setCurrentOperation(Operation.BUSINESS);
209: callContext.setCurrentAllowedStates(EntityContext
210: .getStates());
211: Method runMethod = deployInfo
212: .getMatchingBeanMethod(callMethod);
213:
214: callContext.set(Method.class, runMethod);
215:
216: Object retValue = invoke(callMethod, runMethod, args,
217: callContext);
218:
219: return retValue;
220:
221: } finally {
222: ThreadContext.exit(oldCallContext);
223: }
224: }
225:
226: private SecurityService getSecurityService() {
227: return securityService;
228: }
229:
230: public EntityInstanceManager getInstanceManager() {
231: return instanceManager;
232: }
233:
234: protected Object invoke(Method callMethod, Method runMethod,
235: Object[] args, ThreadContext callContext)
236: throws org.apache.openejb.OpenEJBException {
237:
238: TransactionPolicy txPolicy = callContext.getDeploymentInfo()
239: .getTransactionPolicy(callMethod);
240: TransactionContext txContext = new TransactionContext(
241: callContext, transactionManager);
242: txContext.callContext = callContext;
243:
244: EntityBean bean = null;
245: txPolicy.beforeInvoke(bean, txContext);
246:
247: Object returnValue = null;
248: entrancyTracker.enter(callContext.getDeploymentInfo(),
249: callContext.getPrimaryKey());
250: try {
251: bean = instanceManager.obtainInstance(callContext);
252:
253: ejbLoad_If_No_Transaction(callContext, bean);
254: returnValue = runMethod.invoke(bean, args);
255: ejbStore_If_No_Transaction(callContext, bean);
256: instanceManager.poolInstance(callContext, bean, callContext
257: .getPrimaryKey());
258: } catch (java.lang.reflect.InvocationTargetException ite) {// handle enterprise bean exceptions
259: ExceptionType type = callContext.getDeploymentInfo()
260: .getExceptionType(ite.getTargetException());
261: if (type == ExceptionType.SYSTEM) {
262: /* System Exception ****************************/
263:
264: txPolicy.handleSystemException(
265: ite.getTargetException(), bean, txContext);
266: } else {
267: /* Application Exception ***********************/
268: instanceManager.poolInstance(callContext, bean,
269: callContext.getPrimaryKey());
270: txPolicy.handleApplicationException(ite
271: .getTargetException(),
272: type == ExceptionType.APPLICATION_ROLLBACK,
273: txContext);
274: }
275: } catch (org.apache.openejb.ApplicationException e) {
276: txPolicy.handleApplicationException(e.getRootCause(),
277: false, txContext);
278: } catch (org.apache.openejb.SystemException se) {
279: txPolicy.handleSystemException(se.getRootCause(), bean,
280: txContext);
281: } catch (Throwable iae) {// handle reflection exception
282: /*
283: Any exception thrown by reflection; not by the enterprise bean. Possible
284: Exceptions are:
285: IllegalAccessException - if the underlying method is inaccessible.
286: IllegalArgumentException - if the number of actual and formal parameters differ, or if an unwrapping conversion fails.
287: NullPointerException - if the specified object is null and the method is an instance method.
288: ExceptionInInitializerError - if the initialization provoked by this method fails.
289: */
290: txPolicy.handleSystemException(iae, bean, txContext);
291: } finally {
292: entrancyTracker.exit(callContext.getDeploymentInfo(),
293: callContext.getPrimaryKey());
294: txPolicy.afterInvoke(bean, txContext);
295: }
296:
297: return returnValue;
298: }
299:
300: public void ejbLoad_If_No_Transaction(ThreadContext callContext,
301: EntityBean bean) throws Exception {
302: Operation orginalOperation = callContext.getCurrentOperation();
303: BaseContext.State[] originalAllowedStates = callContext
304: .getCurrentAllowedStates();
305: if (orginalOperation == Operation.BUSINESS
306: || orginalOperation == Operation.REMOVE) {
307:
308: Transaction currentTx = null;
309: try {
310: currentTx = getTransactionManager().getTransaction();
311: } catch (javax.transaction.SystemException se) {
312: throw new org.apache.openejb.SystemException(
313: "Transaction Manager failure", se);
314: }
315:
316: if (currentTx == null) {
317: callContext.setCurrentOperation(Operation.LOAD);
318: callContext.setCurrentAllowedStates(EntityContext
319: .getStates());
320: try {
321: bean.ejbLoad();
322: } catch (NoSuchEntityException e) {
323: instanceManager.discardInstance(callContext, bean);
324: throw new ApplicationException(
325: new NoSuchObjectException(
326: "Entity not found: "
327: + callContext
328: .getPrimaryKey())/*.initCause(e)*/);
329: } catch (Exception e) {
330: instanceManager.discardInstance(callContext, bean);
331: throw e;
332: } finally {
333: callContext.setCurrentOperation(orginalOperation);
334: callContext
335: .setCurrentAllowedStates(originalAllowedStates);
336: }
337: }
338:
339: }
340: }
341:
342: private TransactionManager getTransactionManager() {
343: return transactionManager;
344: }
345:
346: public void ejbStore_If_No_Transaction(ThreadContext callContext,
347: EntityBean bean) throws Exception {
348:
349: Operation currentOp = callContext.getCurrentOperation();
350: BaseContext.State[] originalAllowedStates = callContext
351: .getCurrentAllowedStates();
352: if (currentOp == Operation.BUSINESS) {
353:
354: Transaction currentTx = null;
355: try {
356: currentTx = getTransactionManager().getTransaction();
357: } catch (javax.transaction.SystemException se) {
358: throw new org.apache.openejb.SystemException(
359: "Transaction Manager failure", se);
360: }
361:
362: if (currentTx == null) {
363: callContext.setCurrentOperation(Operation.STORE);
364: callContext.setCurrentAllowedStates(EntityContext
365: .getStates());
366: try {
367: bean.ejbStore();
368: } catch (Exception e) {
369:
370: instanceManager.discardInstance(callContext, bean);
371: throw e;
372: } finally {
373: callContext.setCurrentOperation(currentOp);
374: callContext
375: .setCurrentAllowedStates(originalAllowedStates);
376: }
377: }
378: }
379: }
380:
381: protected void didCreateBean(ThreadContext callContext,
382: EntityBean bean) throws org.apache.openejb.OpenEJBException {
383: }
384:
385: protected ProxyInfo createEJBObject(Method callMethod,
386: Object[] args, ThreadContext callContext)
387: throws OpenEJBException {
388: CoreDeploymentInfo deploymentInfo = callContext
389: .getDeploymentInfo();
390:
391: callContext.setCurrentOperation(Operation.CREATE);
392: callContext.setCurrentAllowedStates(EntityContext.getStates());
393:
394: TransactionPolicy txPolicy = callContext.getDeploymentInfo()
395: .getTransactionPolicy(callMethod);
396: TransactionContext txContext = new TransactionContext(
397: callContext, transactionManager);
398: txContext.callContext = callContext;
399:
400: /*
401: * According to section 9.1.5.1 of the EJB 1.1 specification, the "ejbPostCreate(...)
402: * method executes in the same transaction context as the previous ejbCreate(...) method."
403: *
404: * For this reason the TransactionScopeHandler methods usally preformed by the invoke( )
405: * operation must be handled here along with the call explicitly.
406: * This ensures that the afterInvoke() is not processed between the ejbCreate and ejbPostCreate methods to
407: * ensure that the ejbPostCreate executes in the same transaction context of the ejbCreate.
408: * This would otherwise not be possible if container-managed transactions were used because
409: * the TransactionScopeManager would attempt to commit the transaction immediately after the ejbCreate
410: * and before the ejbPostCreate had a chance to execute. Once the ejbPostCreate method execute the
411: * super classes afterInvoke( ) method will be executed committing the transaction if its a CMT.
412: */
413:
414: txPolicy.beforeInvoke(null, txContext);
415:
416: EntityBean bean = null;
417: Object primaryKey = null;
418: try {
419: // Get new ready instance
420: bean = instanceManager.obtainInstance(callContext);
421:
422: // Obtain the proper ejbCreate() method
423: Method ejbCreateMethod = deploymentInfo
424: .getMatchingBeanMethod(callMethod);
425:
426: // invoke the ejbCreate which returns the primary key
427: primaryKey = ejbCreateMethod.invoke(bean, args);
428:
429: didCreateBean(callContext, bean);
430:
431: // determine post create callback method
432: Method ejbPostCreateMethod = deploymentInfo
433: .getMatchingPostCreateMethod(ejbCreateMethod);
434:
435: // create a new context containing the pk for the post create call
436: ThreadContext postCreateContext = new ThreadContext(
437: deploymentInfo, primaryKey);
438: postCreateContext
439: .setCurrentOperation(Operation.POST_CREATE);
440: postCreateContext.setCurrentAllowedStates(EntityContext
441: .getStates());
442:
443: ThreadContext oldContext = ThreadContext
444: .enter(postCreateContext);
445: try {
446: // Invoke the ejbPostCreate method on the bean instance
447: ejbPostCreateMethod.invoke(bean, args);
448:
449: // According to section 9.1.5.1 of the EJB 1.1 specification, the "ejbPostCreate(...)
450: // method executes in the same transaction context as the previous ejbCreate(...) method."
451: //
452: // The bean is first insterted using db.create( ) and then after ejbPostCreate( ) its
453: // updated using db.update(). This protocol allows for visablity of the bean after ejbCreate
454: // within the current trasnaction.
455: } finally {
456: ThreadContext.exit(oldContext);
457: }
458:
459: // update pool
460: instanceManager.poolInstance(callContext, bean, primaryKey);
461: } catch (java.lang.reflect.InvocationTargetException ite) {// handle enterprise bean exceptions
462: ExceptionType type = callContext.getDeploymentInfo()
463: .getExceptionType(ite.getTargetException());
464: if (type == ExceptionType.SYSTEM) {
465: /* System Exception ****************************/
466: txPolicy.handleSystemException(
467: ite.getTargetException(), bean, txContext);
468: } else {
469: /* Application Exception ***********************/
470: instanceManager.poolInstance(callContext, bean,
471: callContext.getPrimaryKey());
472: txPolicy.handleApplicationException(ite
473: .getTargetException(),
474: type == ExceptionType.APPLICATION_ROLLBACK,
475: txContext);
476: }
477: } catch (OpenEJBException e) {
478: txPolicy.handleSystemException(e.getRootCause(), bean,
479: txContext);
480: } catch (Throwable e) {// handle reflection exception
481: /*
482: Any exception thrown by reflection; not by the enterprise bean. Possible
483: Exceptions are:
484: IllegalAccessException - if the underlying method is inaccessible.
485: IllegalArgumentException - if the number of actual and formal parameters differ, or if an unwrapping conversion fails.
486: NullPointerException - if the specified object is null and the method is an instance method.
487: ExceptionInInitializerError - if the initialization provoked by this method fails.
488: */
489: txPolicy.handleSystemException(e, bean, txContext);
490: } finally {
491: txPolicy.afterInvoke(bean, txContext);
492: }
493:
494: return new ProxyInfo(deploymentInfo, primaryKey);
495:
496: }
497:
498: protected Object findMethod(Method callMethod, Object[] args,
499: ThreadContext callContext) throws OpenEJBException {
500: CoreDeploymentInfo deploymentInfo = callContext
501: .getDeploymentInfo();
502: callContext.setCurrentOperation(Operation.FIND);
503: callContext.setCurrentAllowedStates(EntityContext.getStates());
504: Method runMethod = deploymentInfo
505: .getMatchingBeanMethod(callMethod);
506: Object returnValue = invoke(callMethod, runMethod, args,
507: callContext);
508:
509: /*
510: * Find operations return either a single primary key or a collection of primary keys.
511: * The primary keys are converted to ProxyInfo objects.
512: */
513: if (returnValue instanceof java.util.Collection) {
514: Iterator keys = ((Collection) returnValue).iterator();
515: Vector<ProxyInfo> proxies = new Vector<ProxyInfo>();
516: while (keys.hasNext()) {
517: Object primaryKey = keys.next();
518: proxies.addElement(new ProxyInfo(deploymentInfo,
519: primaryKey));
520: }
521: returnValue = proxies;
522: } else if (returnValue instanceof java.util.Enumeration) {
523: Enumeration keys = (Enumeration) returnValue;
524: Vector<ProxyInfo> proxies = new Vector<ProxyInfo>();
525: while (keys.hasMoreElements()) {
526: Object primaryKey = keys.nextElement();
527: proxies.addElement(new ProxyInfo(deploymentInfo,
528: primaryKey));
529: }
530: returnValue = new org.apache.openejb.util.ArrayEnumeration(
531: proxies);
532: } else
533: returnValue = new ProxyInfo(deploymentInfo, returnValue);
534:
535: return returnValue;
536: }
537:
538: protected Object homeMethod(Method callMethod, Object[] args,
539: ThreadContext callContext) throws OpenEJBException {
540: org.apache.openejb.core.CoreDeploymentInfo deploymentInfo = callContext
541: .getDeploymentInfo();
542: callContext.setCurrentOperation(Operation.HOME);
543: callContext.setCurrentAllowedStates(EntityContext.getStates());
544: Method runMethod = deploymentInfo
545: .getMatchingBeanMethod(callMethod);
546: return invoke(callMethod, runMethod, args, callContext);
547: }
548:
549: protected void didRemove(EntityBean bean,
550: ThreadContext threadContext) throws OpenEJBException {
551: cancelTimers(threadContext);
552: }
553:
554: private void cancelTimers(ThreadContext threadContext) {
555: CoreDeploymentInfo deploymentInfo = threadContext
556: .getDeploymentInfo();
557: Object primaryKey = threadContext.getPrimaryKey();
558:
559: // if we have a real timerservice, stop all timers. Otherwise, ignore...
560: if (primaryKey != null) {
561: EjbTimerService timerService = deploymentInfo
562: .getEjbTimerService();
563: if (timerService != null
564: && timerService instanceof EjbTimerServiceImpl) {
565: for (Timer timer : deploymentInfo.getEjbTimerService()
566: .getTimers(primaryKey)) {
567: timer.cancel();
568: }
569: }
570: }
571: }
572:
573: protected void removeEJBObject(Method callMethod, Object[] args,
574: ThreadContext callContext)
575: throws org.apache.openejb.OpenEJBException {
576: callContext.setCurrentOperation(Operation.REMOVE);
577: callContext.setCurrentAllowedStates(EntityContext.getStates());
578:
579: TransactionPolicy txPolicy = callContext.getDeploymentInfo()
580: .getTransactionPolicy(callMethod);
581: TransactionContext txContext = new TransactionContext(
582: callContext, transactionManager);
583: txContext.callContext = callContext;
584:
585: EntityBean bean = null;
586: txPolicy.beforeInvoke(bean, txContext);
587:
588: try {
589:
590: bean = instanceManager.obtainInstance(callContext);
591:
592: ejbLoad_If_No_Transaction(callContext, bean);
593: bean.ejbRemove();
594: didRemove(bean, callContext);
595: instanceManager.poolInstance(callContext, bean, callContext
596: .getPrimaryKey());
597: } catch (org.apache.openejb.ApplicationException e) {
598: txPolicy.handleApplicationException(e.getRootCause(),
599: false, txContext);
600: } catch (org.apache.openejb.SystemException se) {
601: txPolicy.handleSystemException(se.getRootCause(), bean,
602: txContext);
603: } catch (Exception e) {// handle reflection exception
604: ExceptionType type = callContext.getDeploymentInfo()
605: .getExceptionType(e);
606: if (type == ExceptionType.SYSTEM) {
607: /* System Exception ****************************/
608: txPolicy.handleSystemException(e, bean, txContext);
609: } else {
610: /* Application Exception ***********************/
611: instanceManager.poolInstance(callContext, bean,
612: callContext.getPrimaryKey());
613: txPolicy.handleApplicationException(e,
614: type == ExceptionType.APPLICATION_ROLLBACK,
615: txContext);
616: }
617: } finally {
618: txPolicy.afterInvoke(bean, txContext);
619: }
620: }
621:
622: public void discardInstance(Object bean, ThreadContext threadContext) {
623: if (bean != null) {
624: try {
625: instanceManager.discardInstance(threadContext,
626: (EntityBean) bean);
627: } catch (SystemException e) {
628: logger
629: .error("The instance manager encountered an unkown system exception while trying to discard the entity instance with primary key "
630: + threadContext.getPrimaryKey());
631: }
632: }
633: }
634: }
|