001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins;
023:
024: import java.lang.reflect.Method;
025: import java.lang.reflect.InvocationTargetException;
026: import java.rmi.RemoteException;
027: import java.util.Collection;
028: import java.util.HashMap;
029:
030: import javax.ejb.EntityBean;
031: import javax.ejb.RemoveException;
032: import javax.ejb.EJBException;
033:
034: import org.jboss.ejb.Container;
035: import org.jboss.ejb.EntityContainer;
036: import org.jboss.ejb.EntityPersistenceManager;
037: import org.jboss.ejb.EntityEnterpriseContext;
038: import org.jboss.ejb.EntityCache;
039: import org.jboss.ejb.EntityPersistenceStore;
040: import org.jboss.ejb.AllowedOperationsAssociation;
041: import org.jboss.ejb.GenericEntityObjectFactory;
042: import org.jboss.metadata.ConfigurationMetaData;
043:
044: /**
045: * The CMP Persistence Manager implements the semantics of the CMP
046: * EJB 1.1 call back specification.
047: * <p/>
048: * This Manager works with a "EntityPersistenceStore" that takes care of the
049: * physical storing of instances (JAWS, JDBC O/R, FILE, Object).
050: *
051: * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
052: * @author <a href="mailto:danch@nvisia.com">Dan Christopherson</a>
053: * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
054: * @author <a href="mailto:andreas.schaefer@madplanet.com">Andreas Schaefer</a>
055: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
056: * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
057: * @version $Revision: 57209 $
058: */
059: public class CMPPersistenceManager implements EntityPersistenceManager {
060: // Constants -----------------------------------------------------
061:
062: // Attributes ----------------------------------------------------
063: EntityContainer con;
064: // Physical persistence implementation
065: EntityPersistenceStore store;
066:
067: HashMap createMethods = new HashMap();
068: HashMap postCreateMethods = new HashMap();
069: private boolean insertAfterEjbPostCreate;
070: private boolean ejbStoreForClean;
071:
072: // Static --------------------------------------------------------
073:
074: // Constructors --------------------------------------------------
075:
076: // Public --------------------------------------------------------
077: public void setContainer(Container c) {
078: con = (EntityContainer) c;
079:
080: if (store != null) {
081: store.setContainer(c);
082: }
083:
084: if (con != null) {
085: ConfigurationMetaData configuration = con.getBeanMetaData()
086: .getContainerConfiguration();
087: ejbStoreForClean = configuration.isEjbStoreForClean();
088: }
089: }
090:
091: /**
092: * Gets the entity persistence store.
093: */
094: public EntityPersistenceStore getPersistenceStore() {
095: return store;
096: }
097:
098: public void setPersistenceStore(EntityPersistenceStore store) {
099: this .store = store;
100:
101: //Give it the container
102: if (con != null)
103: store.setContainer(con);
104: }
105:
106: public void create() throws Exception {
107: if (con.getHomeClass() != null) {
108: Method[] methods = con.getHomeClass().getMethods();
109: createMethodCache(methods);
110: }
111:
112: if (con.getLocalHomeClass() != null) {
113: Method[] methods = con.getLocalHomeClass().getMethods();
114: createMethodCache(methods);
115: }
116:
117: insertAfterEjbPostCreate = con.getBeanMetaData()
118: .getContainerConfiguration()
119: .isInsertAfterEjbPostCreate();
120:
121: store.create();
122: }
123:
124: /**
125: * Returns a new instance of the bean class or a subclass of the bean class.
126: *
127: * @return the new instance
128: */
129: public Object createBeanClassInstance() throws Exception {
130: return store.createBeanClassInstance();
131: }
132:
133: private void createMethodCache(Method[] methods)
134: throws NoSuchMethodException {
135: // Create cache of create methods
136: Class beanClass = con.getBeanClass();
137: for (int i = 0; i < methods.length; i++) {
138: String name = methods[i].getName();
139: if (name.startsWith("create")) {
140: Class[] types = methods[i].getParameterTypes();
141: try {
142: String nameSuffix = name.substring(0, 1)
143: .toUpperCase()
144: + name.substring(1);
145: Method beanMethod = beanClass.getMethod("ejb"
146: + nameSuffix, types);
147: createMethods.put(methods[i], beanMethod);
148: beanMethod = beanClass.getMethod("ejbPost"
149: + nameSuffix, types);
150: postCreateMethods.put(methods[i], beanMethod);
151: } catch (NoSuchMethodException nsme) {
152: throw new NoSuchMethodException(
153: "Can't find ejb[Post]Create in "
154: + beanClass.getName());
155: }
156: }
157: }
158: }
159:
160: public void start() throws Exception {
161: store.start();
162: }
163:
164: public void stop() {
165: store.stop();
166: }
167:
168: public void destroy() {
169: store.destroy();
170: }
171:
172: public void createEntity(Method m, Object[] args,
173: EntityEnterpriseContext ctx) throws Exception {
174: // Deligate initialization of bean to persistence store
175: store.initEntity(ctx);
176:
177: // Call ejbCreate on the target bean
178: try {
179: AllowedOperationsAssociation
180: .pushInMethodFlag(IN_EJB_CREATE);
181: Method createMethod = (Method) createMethods.get(m);
182: createMethod.invoke(ctx.getInstance(), args);
183: } catch (IllegalAccessException e) {
184: // Throw this as a bean exception...(?)
185: throw new EJBException(e);
186: } catch (InvocationTargetException ite) {
187: Throwable e = ite.getTargetException();
188: if (e instanceof EJBException) {
189: // Rethrow exception
190: throw (EJBException) e;
191: } else if (e instanceof RuntimeException) {
192: // Wrap runtime exceptions
193: throw new EJBException((Exception) e);
194: } else if (e instanceof Exception) {
195: // Remote, Create, or custom app. exception
196: throw (Exception) e;
197: } else {
198: throw (Error) e;
199: }
200: } finally {
201: AllowedOperationsAssociation.popInMethodFlag();
202: }
203:
204: // if insertAfterEjbPostCreate == true, this will INSERT entity
205: // otherwise, primary key is extracted from the context and returned
206: Object id;
207: try {
208: AllowedOperationsAssociation
209: .pushInMethodFlag(IN_EJB_CREATE);
210: id = store.createEntity(m, args, ctx);
211: } finally {
212: AllowedOperationsAssociation.popInMethodFlag();
213: }
214:
215: // Set the key on the target context
216: ctx.setId(id);
217:
218: // Create a new CacheKey
219: Object cacheKey = ((EntityCache) con.getInstanceCache())
220: .createCacheKey(id);
221:
222: // Give it to the context
223: ctx.setCacheKey(cacheKey);
224: }
225:
226: public void postCreateEntity(Method m, Object[] args,
227: EntityEnterpriseContext ctx) throws Exception {
228: // this call should go first as it sets up relationships
229: // for fk fields mapped to pk fields
230: store.postCreateEntity(m, args, ctx);
231:
232: try {
233: AllowedOperationsAssociation
234: .pushInMethodFlag(IN_EJB_POST_CREATE);
235:
236: Method postCreateMethod = (Method) postCreateMethods.get(m);
237: postCreateMethod.invoke(ctx.getInstance(), args);
238: if (insertAfterEjbPostCreate) {
239: store.createEntity(m, args, ctx);
240: }
241: } catch (IllegalAccessException e) {
242: // Throw this as a bean exception...(?)
243: throw new EJBException(e);
244: } catch (InvocationTargetException ite) {
245: Throwable e = ite.getTargetException();
246: if (e instanceof EJBException) {
247: // Rethrow exception
248: throw (EJBException) e;
249: } else if (e instanceof RuntimeException) {
250: // Wrap runtime exceptions
251: throw new EJBException((Exception) e);
252: } else if (e instanceof Exception) {
253: // Remote, Create, or custom app. exception
254: throw (Exception) e;
255: } else {
256: throw (Error) e;
257: }
258: } finally {
259: AllowedOperationsAssociation.popInMethodFlag();
260: }
261: }
262:
263: public Object findEntity(Method finderMethod, Object[] args,
264: EntityEnterpriseContext ctx,
265: GenericEntityObjectFactory factory) throws Exception {
266: try {
267: AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_FIND);
268: return store.findEntity(finderMethod, args, ctx, factory);
269: } finally {
270: AllowedOperationsAssociation.popInMethodFlag();
271: }
272: }
273:
274: /**
275: * find multiple entities
276: */
277: public Collection findEntities(Method finderMethod, Object[] args,
278: EntityEnterpriseContext ctx,
279: GenericEntityObjectFactory factory) throws Exception {
280: try {
281: // return the finderResults so that the invoker layer can extend this back
282: // giving the client an OO 'cursor'
283: AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_FIND);
284: return store.findEntities(finderMethod, args, ctx, factory);
285: } finally {
286: AllowedOperationsAssociation.popInMethodFlag();
287: }
288: }
289:
290: /*
291: * activateEntity(EnterpriseContext ctx)
292: *
293: * The method calls the target beans for spec compliant callbacks.
294: * Since these are pure EJB calls it is not obvious that the store should
295: * expose the interfaces. In case of jaws however we found that store specific
296: * contexts could be set in the activateEntity calls and hence a propagation of
297: * the call made sense. The persistence store is called for "extension" purposes.
298: *
299: * @see activateEntity on EntityPersistenceStore.java
300: */
301: public void activateEntity(EntityEnterpriseContext ctx)
302: throws RemoteException {
303: // Create a new CacheKey
304: Object id = ctx.getId();
305: Object cacheKey = ((EntityCache) con.getInstanceCache())
306: .createCacheKey(id);
307:
308: // Give it to the context
309: ctx.setCacheKey(cacheKey);
310:
311: try {
312: AllowedOperationsAssociation
313: .pushInMethodFlag(IN_EJB_ACTIVATE);
314: EntityBean eb = (EntityBean) ctx.getInstance();
315: eb.ejbActivate();
316: } catch (Exception e) {
317: if (e instanceof RemoteException) {
318: // Rethrow exception
319: throw (RemoteException) e;
320: } else if (e instanceof EJBException) {
321: // Rethrow exception
322: throw (EJBException) e;
323: } else {
324: // Wrap runtime exceptions
325: throw new EJBException((Exception) e);
326: }
327: } finally {
328: AllowedOperationsAssociation.popInMethodFlag();
329: }
330:
331: // The implementation of the call can be left absolutely empty, the
332: // propagation of the call is just a notification for stores that would
333: // need to know that an instance is being activated
334: store.activateEntity(ctx);
335: }
336:
337: public void loadEntity(EntityEnterpriseContext ctx)
338: throws RemoteException {
339: //long lStart = System.currentTimeMillis();
340: // Have the store load the fields of the instance
341: store.loadEntity(ctx);
342: //mLoad.add( System.currentTimeMillis() - lStart );
343:
344: invokeLoad(ctx);
345: }
346:
347: public boolean isStoreRequired(EntityEnterpriseContext ctx)
348: throws Exception {
349: return store.isStoreRequired(ctx);
350: }
351:
352: public boolean isModified(EntityEnterpriseContext ctx)
353: throws Exception {
354: return store.isModified(ctx);
355: }
356:
357: public void storeEntity(EntityEnterpriseContext ctx)
358: throws RemoteException {
359: AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_STORE);
360: try {
361: store.storeEntity(ctx);
362: } finally {
363: AllowedOperationsAssociation.popInMethodFlag();
364: }
365: }
366:
367: public void invokeEjbStore(EntityEnterpriseContext ctx)
368: throws RemoteException {
369: AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_STORE);
370:
371: try {
372: // if call-ejb-store-for-clean=true then invoke ejbStore first (the last chance to modify the instance)
373: if (ejbStoreForClean) {
374: ejbStore(ctx);
375: } else {
376: // else check whether the instance is dirty and invoke ejbStore only if it is really dirty
377: boolean modified = false;
378: try {
379: modified = isStoreRequired(ctx);
380: } catch (Exception e) {
381: throwRemoteException(e);
382: }
383:
384: if (modified) {
385: ejbStore(ctx);
386: }
387: }
388: } finally {
389: AllowedOperationsAssociation.popInMethodFlag();
390: }
391: }
392:
393: public void passivateEntity(EntityEnterpriseContext ctx)
394: throws RemoteException {
395: try {
396: AllowedOperationsAssociation
397: .pushInMethodFlag(IN_EJB_PASSIVATE);
398: EntityBean eb = (EntityBean) ctx.getInstance();
399: eb.ejbPassivate();
400: } catch (Exception e) {
401: throwRemoteException(e);
402: } finally {
403: AllowedOperationsAssociation.popInMethodFlag();
404: }
405:
406: store.passivateEntity(ctx);
407: ctx.setEJBObject(null);
408: ctx.setEJBLocalObject(null);
409: }
410:
411: public void removeEntity(EntityEnterpriseContext ctx)
412: throws RemoteException, RemoveException {
413: try {
414: AllowedOperationsAssociation
415: .pushInMethodFlag(IN_EJB_REMOVE);
416: EntityBean eb = (EntityBean) ctx.getInstance();
417: eb.ejbRemove();
418: } catch (Exception e) {
419: if (e instanceof RemoveException) {
420: // Rethrow exception
421: throw (RemoveException) e;
422: } else {
423: throwRemoteException(e);
424: }
425: } finally {
426: AllowedOperationsAssociation.popInMethodFlag();
427: }
428:
429: store.removeEntity(ctx);
430: }
431:
432: protected void invokeLoad(EntityEnterpriseContext ctx)
433: throws RemoteException {
434: try {
435: AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_LOAD);
436:
437: EntityBean eb = (EntityBean) ctx.getInstance();
438: eb.ejbLoad();
439: } catch (Exception e) {
440: throwRemoteException(e);
441: } finally {
442: AllowedOperationsAssociation.popInMethodFlag();
443: }
444: }
445:
446: // Private
447:
448: private void ejbStore(EntityEnterpriseContext ctx)
449: throws RemoteException {
450: try {
451: EntityBean eb = (EntityBean) ctx.getInstance();
452: eb.ejbStore();
453: } catch (Exception e) {
454: throwRemoteException(e);
455: }
456: }
457:
458: private void throwRemoteException(Exception e)
459: throws RemoteException {
460: if (e instanceof RemoteException) {
461: // Rethrow exception
462: throw (RemoteException) e;
463: } else if (e instanceof EJBException) {
464: // Rethrow exception
465: throw (EJBException) e;
466: } else {
467: // Wrap runtime exceptions
468: throw new EJBException((Exception) e);
469: }
470: }
471: }
|