1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.ejb.plugins;
23
24 import java.lang.reflect.Method;
25 import java.lang.reflect.InvocationTargetException;
26 import java.rmi.RemoteException;
27 import java.util.Collection;
28 import java.util.HashMap;
29
30 import javax.ejb.EntityBean;
31 import javax.ejb.RemoveException;
32 import javax.ejb.EJBException;
33
34 import org.jboss.ejb.Container;
35 import org.jboss.ejb.EntityContainer;
36 import org.jboss.ejb.EntityPersistenceManager;
37 import org.jboss.ejb.EntityEnterpriseContext;
38 import org.jboss.ejb.EntityCache;
39 import org.jboss.ejb.EntityPersistenceStore;
40 import org.jboss.ejb.AllowedOperationsAssociation;
41 import org.jboss.ejb.GenericEntityObjectFactory;
42 import org.jboss.metadata.ConfigurationMetaData;
43
44 /**
45 * The CMP Persistence Manager implements the semantics of the CMP
46 * EJB 1.1 call back specification.
47 * <p/>
48 * This Manager works with a "EntityPersistenceStore" that takes care of the
49 * physical storing of instances (JAWS, JDBC O/R, FILE, Object).
50 *
51 * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
52 * @author <a href="mailto:danch@nvisia.com">Dan Christopherson</a>
53 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
54 * @author <a href="mailto:andreas.schaefer@madplanet.com">Andreas Schaefer</a>
55 * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
56 * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
57 * @version $Revision: 37459 $
58 */
59 public class CMPPersistenceManager
60 implements EntityPersistenceManager
61 {
62 // Constants -----------------------------------------------------
63
64 // Attributes ----------------------------------------------------
65 EntityContainer con;
66 // Physical persistence implementation
67 EntityPersistenceStore store;
68
69
70 HashMap createMethods = new HashMap();
71 HashMap postCreateMethods = new HashMap();
72 private boolean insertAfterEjbPostCreate;
73 private boolean ejbStoreForClean;
74
75 // Static --------------------------------------------------------
76
77 // Constructors --------------------------------------------------
78
79 // Public --------------------------------------------------------
80 public void setContainer(Container c)
81 {
82 con = (EntityContainer) c;
83
84 if(store != null)
85 {
86 store.setContainer(c);
87 }
88
89 if(con != null)
90 {
91 ConfigurationMetaData configuration = con.getBeanMetaData().getContainerConfiguration();
92 ejbStoreForClean = configuration.isEjbStoreForClean();
93 }
94 }
95
96 /**
97 * Gets the entity persistence store.
98 */
99 public EntityPersistenceStore getPersistenceStore()
100 {
101 return store;
102 }
103
104 public void setPersistenceStore(EntityPersistenceStore store)
105 {
106 this.store = store;
107
108 //Give it the container
109 if(con != null) store.setContainer(con);
110 }
111
112 public void create()
113 throws Exception
114 {
115 if(con.getHomeClass() != null)
116 {
117 Method[] methods = con.getHomeClass().getMethods();
118 createMethodCache(methods);
119 }
120
121 if(con.getLocalHomeClass() != null)
122 {
123 Method[] methods = con.getLocalHomeClass().getMethods();
124 createMethodCache(methods);
125 }
126
127 insertAfterEjbPostCreate = con.getBeanMetaData().getContainerConfiguration().isInsertAfterEjbPostCreate();
128
129 store.create();
130 }
131
132 /**
133 * Returns a new instance of the bean class or a subclass of the bean class.
134 *
135 * @return the new instance
136 */
137 public Object createBeanClassInstance() throws Exception
138 {
139 return store.createBeanClassInstance();
140 }
141
142 private void createMethodCache(Method[] methods)
143 throws NoSuchMethodException
144 {
145 // Create cache of create methods
146 Class beanClass = con.getBeanClass();
147 for(int i = 0; i < methods.length; i++)
148 {
149 String name = methods[i].getName();
150 if(name.startsWith("create"))
151 {
152 Class[] types = methods[i].getParameterTypes();
153 try
154 {
155 String nameSuffix = name.substring(0, 1).toUpperCase() + name.substring(1);
156 Method beanMethod = beanClass.getMethod("ejb" + nameSuffix, types);
157 createMethods.put(methods[i], beanMethod);
158 beanMethod = beanClass.getMethod("ejbPost" + nameSuffix, types);
159 postCreateMethods.put(methods[i], beanMethod);
160 }
161 catch(NoSuchMethodException nsme)
162 {
163 throw new NoSuchMethodException("Can't find ejb[Post]Create in " + beanClass.getName());
164 }
165 }
166 }
167 }
168
169 public void start()
170 throws Exception
171 {
172 store.start();
173 }
174
175 public void stop()
176 {
177 store.stop();
178 }
179
180 public void destroy()
181 {
182 store.destroy();
183 }
184
185 public void createEntity(Method m, Object[] args, EntityEnterpriseContext ctx)
186 throws Exception
187 {
188 // Deligate initialization of bean to persistence store
189 store.initEntity(ctx);
190
191 // Call ejbCreate on the target bean
192 try
193 {
194 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_CREATE);
195 Method createMethod = (Method) createMethods.get(m);
196 createMethod.invoke(ctx.getInstance(), args);
197 }
198 catch(IllegalAccessException e)
199 {
200 // Throw this as a bean exception...(?)
201 throw new EJBException(e);
202 }
203 catch(InvocationTargetException ite)
204 {
205 Throwable e = ite.getTargetException();
206 if(e instanceof EJBException)
207 {
208 // Rethrow exception
209 throw (EJBException) e;
210 }
211 else if(e instanceof RuntimeException)
212 {
213 // Wrap runtime exceptions
214 throw new EJBException((Exception) e);
215 }
216 else if(e instanceof Exception)
217 {
218 // Remote, Create, or custom app. exception
219 throw (Exception) e;
220 }
221 else
222 {
223 throw (Error) e;
224 }
225 }
226 finally
227 {
228 AllowedOperationsAssociation.popInMethodFlag();
229 }
230
231 // if insertAfterEjbPostCreate == true, this will INSERT entity
232 // otherwise, primary key is extracted from the context and returned
233 Object id;
234 try
235 {
236 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_CREATE);
237 id = store.createEntity(m, args, ctx);
238 }
239 finally
240 {
241 AllowedOperationsAssociation.popInMethodFlag();
242 }
243
244 // Set the key on the target context
245 ctx.setId(id);
246
247 // Create a new CacheKey
248 Object cacheKey = ((EntityCache) con.getInstanceCache()).createCacheKey(id);
249
250 // Give it to the context
251 ctx.setCacheKey(cacheKey);
252 }
253
254 public void postCreateEntity(Method m, Object[] args, EntityEnterpriseContext ctx)
255 throws Exception
256 {
257 // this call should go first as it sets up relationships
258 // for fk fields mapped to pk fields
259 store.postCreateEntity(m, args, ctx);
260
261 try
262 {
263 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_POST_CREATE);
264
265 Method postCreateMethod = (Method) postCreateMethods.get(m);
266 postCreateMethod.invoke(ctx.getInstance(), args);
267 if(insertAfterEjbPostCreate)
268 {
269 store.createEntity(m, args, ctx);
270 }
271 }
272 catch(IllegalAccessException e)
273 {
274 // Throw this as a bean exception...(?)
275 throw new EJBException(e);
276 }
277 catch(InvocationTargetException ite)
278 {
279 Throwable e = ite.getTargetException();
280 if(e instanceof EJBException)
281 {
282 // Rethrow exception
283 throw (EJBException) e;
284 }
285 else if(e instanceof RuntimeException)
286 {
287 // Wrap runtime exceptions
288 throw new EJBException((Exception) e);
289 }
290 else if(e instanceof Exception)
291 {
292 // Remote, Create, or custom app. exception
293 throw (Exception) e;
294 }
295 else
296 {
297 throw (Error) e;
298 }
299 }
300 finally
301 {
302 AllowedOperationsAssociation.popInMethodFlag();
303 }
304 }
305
306 public Object findEntity(Method finderMethod,
307 Object[] args,
308 EntityEnterpriseContext ctx,
309 GenericEntityObjectFactory factory)
310 throws Exception
311 {
312 try
313 {
314 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_FIND);
315 return store.findEntity(finderMethod, args, ctx, factory);
316 }
317 finally
318 {
319 AllowedOperationsAssociation.popInMethodFlag();
320 }
321 }
322
323 /**
324 * find multiple entities
325 */
326 public Collection findEntities(Method finderMethod,
327 Object[] args,
328 EntityEnterpriseContext ctx,
329 GenericEntityObjectFactory factory)
330 throws Exception
331 {
332 try
333 {
334 // return the finderResults so that the invoker layer can extend this back
335 // giving the client an OO 'cursor'
336 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_FIND);
337 return store.findEntities(finderMethod, args, ctx, factory);
338 }
339 finally
340 {
341 AllowedOperationsAssociation.popInMethodFlag();
342 }
343 }
344
345 /*
346 * activateEntity(EnterpriseContext ctx)
347 *
348 * The method calls the target beans for spec compliant callbacks.
349 * Since these are pure EJB calls it is not obvious that the store should
350 * expose the interfaces. In case of jaws however we found that store specific
351 * contexts could be set in the activateEntity calls and hence a propagation of
352 * the call made sense. The persistence store is called for "extension" purposes.
353 *
354 * @see activateEntity on EntityPersistenceStore.java
355 */
356 public void activateEntity(EntityEnterpriseContext ctx)
357 throws RemoteException
358 {
359 // Create a new CacheKey
360 Object id = ctx.getId();
361 Object cacheKey = ((EntityCache) con.getInstanceCache()).createCacheKey(id);
362
363 // Give it to the context
364 ctx.setCacheKey(cacheKey);
365
366 try
367 {
368 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_ACTIVATE);
369 EntityBean eb = (EntityBean) ctx.getInstance();
370 eb.ejbActivate();
371 }
372 catch(Exception e)
373 {
374 if(e instanceof RemoteException)
375 {
376 // Rethrow exception
377 throw (RemoteException) e;
378 }
379 else if(e instanceof EJBException)
380 {
381 // Rethrow exception
382 throw (EJBException) e;
383 }
384 else
385 {
386 // Wrap runtime exceptions
387 throw new EJBException((Exception) e);
388 }
389 }
390 finally
391 {
392 AllowedOperationsAssociation.popInMethodFlag();
393 }
394
395 // The implementation of the call can be left absolutely empty, the
396 // propagation of the call is just a notification for stores that would
397 // need to know that an instance is being activated
398 store.activateEntity(ctx);
399 }
400
401 public void loadEntity(EntityEnterpriseContext ctx)
402 throws RemoteException
403 {
404 //long lStart = System.currentTimeMillis();
405 // Have the store load the fields of the instance
406 store.loadEntity(ctx);
407 //mLoad.add( System.currentTimeMillis() - lStart );
408
409 invokeLoad(ctx);
410 }
411
412 public boolean isStoreRequired(EntityEnterpriseContext ctx) throws Exception
413 {
414 return store.isStoreRequired(ctx);
415 }
416
417 public boolean isModified(EntityEnterpriseContext ctx) throws Exception
418 {
419 return store.isModified(ctx);
420 }
421
422 public void storeEntity(EntityEnterpriseContext ctx)
423 throws RemoteException
424 {
425 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_STORE);
426 try
427 {
428 store.storeEntity(ctx);
429 }
430 finally
431 {
432 AllowedOperationsAssociation.popInMethodFlag();
433 }
434 }
435
436 public void invokeEjbStore(EntityEnterpriseContext ctx) throws RemoteException
437 {
438 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_STORE);
439
440 try
441 {
442 // if call-ejb-store-for-clean=true then invoke ejbStore first (the last chance to modify the instance)
443 if(ejbStoreForClean)
444 {
445 ejbStore(ctx);
446 }
447 else
448 {
449 // else check whether the instance is dirty and invoke ejbStore only if it is really dirty
450 boolean modified = false;
451 try
452 {
453 modified = isStoreRequired(ctx);
454 }
455 catch(Exception e)
456 {
457 throwRemoteException(e);
458 }
459
460 if(modified)
461 {
462 ejbStore(ctx);
463 }
464 }
465 }
466 finally
467 {
468 AllowedOperationsAssociation.popInMethodFlag();
469 }
470 }
471
472 public void passivateEntity(EntityEnterpriseContext ctx)
473 throws RemoteException
474 {
475 try
476 {
477 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_PASSIVATE);
478 EntityBean eb = (EntityBean) ctx.getInstance();
479 eb.ejbPassivate();
480 }
481 catch(Exception e)
482 {
483 throwRemoteException(e);
484 }
485 finally
486 {
487 AllowedOperationsAssociation.popInMethodFlag();
488 }
489
490 store.passivateEntity(ctx);
491 ctx.setEJBObject(null);
492 ctx.setEJBLocalObject(null);
493 }
494
495 public void removeEntity(EntityEnterpriseContext ctx)
496 throws RemoteException, RemoveException
497 {
498 try
499 {
500 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_REMOVE);
501 EntityBean eb = (EntityBean) ctx.getInstance();
502 eb.ejbRemove();
503 }
504 catch(Exception e)
505 {
506 if(e instanceof RemoveException)
507 {
508 // Rethrow exception
509 throw (RemoveException) e;
510 }
511 else
512 {
513 throwRemoteException(e);
514 }
515 }
516 finally
517 {
518 AllowedOperationsAssociation.popInMethodFlag();
519 }
520
521 store.removeEntity(ctx);
522 }
523
524 protected void invokeLoad(EntityEnterpriseContext ctx) throws RemoteException
525 {
526 try
527 {
528 AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_LOAD);
529
530 EntityBean eb = (EntityBean) ctx.getInstance();
531 eb.ejbLoad();
532 }
533 catch(Exception e)
534 {
535 throwRemoteException(e);
536 }
537 finally
538 {
539 AllowedOperationsAssociation.popInMethodFlag();
540 }
541 }
542
543 // Private
544
545 private void ejbStore(EntityEnterpriseContext ctx)
546 throws RemoteException
547 {
548 try
549 {
550 EntityBean eb = (EntityBean) ctx.getInstance();
551 eb.ejbStore();
552 }
553 catch(Exception e)
554 {
555 throwRemoteException(e);
556 }
557 }
558
559 private void throwRemoteException(Exception e)
560 throws RemoteException
561 {
562 if(e instanceof RemoteException)
563 {
564 // Rethrow exception
565 throw (RemoteException) e;
566 }
567 else if(e instanceof EJBException)
568 {
569 // Rethrow exception
570 throw (EJBException) e;
571 }
572 else
573 {
574 // Wrap runtime exceptions
575 throw new EJBException((Exception) e);
576 }
577 }
578 }