KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openejb > alt > containers > castor_cmp11 > CastorCMP11_EntityContainer


1 /**
2  * Redistribution and use of this software and associated documentation
3  * ("Software"), with or without modification, are permitted provided
4  * that the following conditions are met:
5  *
6  * 1. Redistributions of source code must retain copyright
7  * statements and notices. Redistributions must also contain a
8  * copy of this document.
9  *
10  * 2. Redistributions in binary form must reproduce the
11  * above copyright notice, this list of conditions and the
12  * following disclaimer in the documentation and/or other
13  * materials provided with the distribution.
14  *
15  * 3. The name "Exolab" must not be used to endorse or promote
16  * products derived from this Software without prior written
17  * permission of Exoffice Technologies. For written permission,
18  * please contact info@exolab.org.
19  *
20  * 4. Products derived from this Software may not be called "Exolab"
21  * nor may "Exolab" appear in their names without prior written
22  * permission of Exoffice Technologies. Exolab is a registered
23  * trademark of Exoffice Technologies.
24  *
25  * 5. Due credit should be given to the Exolab Project
26  * (http://www.exolab.org/).
27  *
28  * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32  * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39  * OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved.
42  *
43  * $Id: CastorCMP11_EntityContainer.java 2167 2005-09-19 22:15:13Z jcscoobyrs $
44  */

45 package org.openejb.alt.containers.castor_cmp11;
46
47 import java.io.File JavaDoc;
48 import java.lang.reflect.Field JavaDoc;
49 import java.lang.reflect.InvocationTargetException JavaDoc;
50 import java.lang.reflect.Method JavaDoc;
51 import java.rmi.RemoteException JavaDoc;
52 import java.util.HashMap JavaDoc;
53 import java.util.Hashtable JavaDoc;
54 import java.util.Properties JavaDoc;
55
56 import javax.ejb.EJBHome JavaDoc;
57 import javax.ejb.EJBLocalHome JavaDoc;
58 import javax.ejb.EJBLocalObject JavaDoc;
59 import javax.ejb.EJBObject JavaDoc;
60 import javax.ejb.EnterpriseBean JavaDoc;
61 import javax.ejb.EntityBean JavaDoc;
62 import javax.transaction.Status JavaDoc;
63 import javax.transaction.Transaction JavaDoc;
64
65 import org.exolab.castor.jdo.Database;
66 import org.exolab.castor.jdo.JDO;
67 import org.exolab.castor.jdo.OQLQuery;
68 import org.exolab.castor.jdo.QueryResults;
69 import org.exolab.castor.mapping.AccessMode;
70 import org.exolab.castor.persist.spi.CallbackInterceptor;
71 import org.exolab.castor.persist.spi.Complex;
72 import org.exolab.castor.persist.spi.InstanceFactory;
73 import org.openejb.Container;
74 import org.openejb.DeploymentInfo;
75 import org.openejb.OpenEJB;
76 import org.openejb.OpenEJBException;
77 import org.openejb.ProxyInfo;
78 import org.openejb.RpcContainer;
79 import org.openejb.core.EnvProps;
80 import org.openejb.core.Operations;
81 import org.openejb.core.ThreadContext;
82 import org.openejb.core.transaction.TransactionContainer;
83 import org.openejb.core.transaction.TransactionContext;
84 import org.openejb.core.transaction.TransactionPolicy;
85 import org.openejb.loader.SystemInstance;
86 import org.openejb.util.LinkedListStack;
87 import org.openejb.util.Logger;
88 import org.openejb.util.SafeProperties;
89 import org.openejb.util.SafeToolkit;
90 import org.openejb.util.Stack;
91
92 /**
93  * Container-Managed Persistence EntityBean container based on Castor
94  *
95  * @author <a HREF="mailto:Richard@Monson-Haefel.com">Richard Monson-Haefel</a>
96  * @author <a HREF="mailto:david.blevins@visi.com">David Blevins</a>
97  * @version $Revision: 2167 $ $Date: 2005-09-19 15:15:13 -0700 (Mon, 19 Sep 2005) $
98  */

99 public class CastorCMP11_EntityContainer
100 implements RpcContainer, TransactionContainer, CallbackInterceptor, InstanceFactory {
101
102
103     /*
104      * Bean instances that are currently in use are placed in the txReadyPoolMap indexed
105      * by their object instance with a reference to deployment's methodReadyPoolMap entry
106      * as the value.
107      *
108      * A bean instance is added to the txReadyPool when the fetchFreeInstance( ) method is invoked.
109      *
110      * When a bean is released from a transaction the entry is removed from the hashtable.
111      * This can occur in the CallbackInterceptor.releasing( ) method implemented by this class
112      * which is called when Castor has either committed or rollback a transaction involving the bean
113      * instance OR in the TransactionScopeHandler.discardBeanInstance(), which is called when a
114      * transaction fails due to a runtime exception.
115      */

116     protected Hashtable JavaDoc txReadyPoolMap = new Hashtable JavaDoc();
117
118     //DMB: The actual stacks of instances should be kept in the DeploymentInfo also
119
protected Hashtable JavaDoc pooledInstancesMap = new Hashtable JavaDoc();
120     protected Hashtable JavaDoc readyInstancesMap = new Hashtable JavaDoc();
121
122     /*
123      * Contains all the KeyGenerator objects for each Deployment, indexed by deployment id.
124      * The KeyGenerator objects provide quick extraction of primary keys from entity bean
125      * classes and conversion between a primary key and a Castor Complex identity.
126         DMB: Instead of looking up an KeyGenerator for the deployment, we could attach it
127         to the DeploymentInfo, or a new DeploymentInfo subclass for the CMP container.
128      */

129 // protected HashMap keyGeneratorMap = new HashMap();
130

131     /*
132      * contains a collection of LinkListStacks indexed by deployment id. Each
133      * indexed stack represents the method ready pool of for that class.
134      */

135     protected HashMap JavaDoc methodReadyPoolMap = new HashMap JavaDoc();
136
137     /* The default size of the method ready bean pools. Every bean class gets its own pool of this size */
138     protected int poolsize = 0;
139
140     /*
141      * The javax.ejb.EntityBean.setEntityContext(...) method is used for
142      * processing bean instances returing to the method ready pool
143      * This variable is esbalished in the contructor so that it doesn't
144      * have to be re-obtained every time we want to passivate an entity instance.
145      */

146     protected static Method JavaDoc SET_ENTITY_CONTEXT_METHOD;
147
148     /*
149      * The javax.ejb.EntityBean.unsetEntityContext(...) method is used for
150      * processing bean instances that are being evicted from memory.
151      * This variable is esbalished in the contructor so that it doesn't
152      * have to be re-obtained every time we want to passivate an entity instance.
153      * DMB: This isn't being called anywhere.
154      */

155     protected static Method JavaDoc UNSET_ENTITY_CONTEXT_METHOD;
156
157     /*
158      * The javax.ejb.EntityBean.ejbRemove() method is used for processing bean
159      * instances that are about to be deleted from the database. This variable
160      * is esbalished in the contructor so that it doesn't have to be re-obtained
161      * every time we want to passivate an entity instance.
162      */

163     protected static Method JavaDoc EJB_REMOVE_METHOD;
164
165     /*
166      * This static block sets up the EJB_PASSIVATE_METHOD, EJB_LOAD_METHOD, SET_ENTITY_CONTEXT_METHOD static methods, which are used
167      * in the poolInstance() and obtainInstance() methods of this type. Saves method lookup cycles at runtime.
168      */

169     static {
170         try {
171             SET_ENTITY_CONTEXT_METHOD = javax.ejb.EntityBean JavaDoc.class.getMethod( "setEntityContext", new Class JavaDoc[]{javax.ejb.EntityContext JavaDoc.class} );
172             UNSET_ENTITY_CONTEXT_METHOD = javax.ejb.EntityBean JavaDoc.class.getMethod( "unsetEntityContext", null );
173             EJB_REMOVE_METHOD = javax.ejb.EntityBean JavaDoc.class.getMethod( "ejbRemove", null );
174         } catch ( NoSuchMethodException JavaDoc nse ) {
175         }
176     }
177
178
179     public Logger logger = Logger.getInstance( "OpenEJB", "org.openejb.alt.util.resources" );
180
181     // contains deployment information for each by deployed to this container
182
HashMap JavaDoc deploymentRegistry;
183     // the unique id for this container
184
Object JavaDoc containerID = null;
185
186     /**
187      * The name of the database.xml file that is used for global or container managed transactions.
188      * This will be used when the TransactionManager is managing the transaction, such as when the
189      * tx attribute is Supports (client has tx), RequiresNew, Required or Manditory.
190      * specifies the configuration for obtaining a database connections and the mapping.xml
191      * schema which describes how beans map to the database.
192      */

193     protected String JavaDoc Global_TX_Database = null;
194
195     /**
196      * The name of the database.xml file that is used for local or unspecified transaction contexts.
197      * This will be used when the TransactionManager is not managing the transaction, such as when the
198      * tx attribute is Supports (no client tx), NotSupported, or Never.
199      * specifies the configuration for obtaining a database connections and the mapping.xml
200      * schema which describes how beans map to the database.
201      */

202     protected String JavaDoc Local_TX_Database = null;
203
204     /**
205      * This is a handle into a specific Castor JDO instance for a specific
206      * database mapping that has been configured to work with the transaciton manager.
207      */

208     protected JDO jdo_ForGlobalTransaction;
209
210     /**
211      * This is a handle into a specific Castor JDO instance for a specific
212      * database mapping that has been configured to manage its own transactions.
213      */

214     protected JDO jdo_ForLocalTransaction;
215
216     // manages the transactional scope according to the bean's transaction attributes
217
//CastorTransactionScopeHandler txScopeHandle;
218

219     // Manages the synchronization wrappers
220
java.util.Hashtable JavaDoc syncWrappers = new java.util.Hashtable JavaDoc();
221
222     // this map contains the Java language initial values for all all data types
223
protected HashMap JavaDoc resetMap;
224
225     private Properties JavaDoc props;
226
227     //DMB:TODO:1: make logger for life cycle info.
228

229     /**
230      * Construct this container with the specified container id, deployments,
231      * container manager and properties. The properties can include the class
232      * name of the preferred InstanceManager,
233      * org.openejb.core.entity.EntityInstanceManager is the default. The
234      * properties should also include the properties for the instance manager.
235      *
236      * @param id the unique id to identify this container in the ContainerSystem
237      * @param registry a hashMap of bean delpoyments that this container will be responsible for
238      * @param properties the properties this container needs to initialize and run
239      * @exception OpenEJBException
240      * if there is a problem constructing the container
241      * @exception org.openejb.OpenEJBException
242      * @see org.openejb.Container
243      */

244     public void init( Object JavaDoc id, HashMap JavaDoc registry, Properties JavaDoc properties ) throws org.openejb.OpenEJBException {
245         containerID = id;
246         deploymentRegistry = registry;
247
248         this.props = properties;
249
250         SafeToolkit toolkit = SafeToolkit.getToolkit( "CastorCMP11_EntityContainer" );
251         SafeProperties safeProps = toolkit.getSafeProperties( properties );
252
253         poolsize = safeProps.getPropertyAsInt(EnvProps.IM_POOL_SIZE, 100 );
254         Global_TX_Database = safeProps.getProperty(EnvProps.GLOBAL_TX_DATABASE);
255         Local_TX_Database = safeProps.getProperty(EnvProps.LOCAL_TX_DATABASE);
256
257         File JavaDoc gTxDb = null;
258         File JavaDoc lTxDb = null;
259         try {
260             gTxDb = SystemInstance.get().getBase().getFile( Global_TX_Database );
261         } catch ( Exception JavaDoc e ) {
262             throw new OpenEJBException("Cannot locate the " + EnvProps.GLOBAL_TX_DATABASE + " file. " + e.getMessage());
263         }
264
265         try {
266             lTxDb = SystemInstance.get().getBase().getFile( Local_TX_Database );
267         } catch ( Exception JavaDoc e ) {
268             throw new OpenEJBException("Cannot locate the " + EnvProps.LOCAL_TX_DATABASE + " file. " + e.getMessage());
269         }
270
271         if (!gTxDb.exists()) {
272             throw new OpenEJBException(id+": The "+EnvProps.GLOBAL_TX_DATABASE+" file does not exit: "+gTxDb.getAbsolutePath());
273         }
274         if (!lTxDb.exists()) {
275             throw new OpenEJBException(id+": The "+EnvProps.LOCAL_TX_DATABASE+" file does not exit: "+lTxDb.getAbsolutePath());
276         }
277         if (gTxDb.equals(lTxDb)) {
278             throw new OpenEJBException(id+": The "+EnvProps.LOCAL_TX_DATABASE+" and "+EnvProps.GLOBAL_TX_DATABASE+" files cannot be the same: "+lTxDb.getAbsolutePath());
279         }
280
281         /*
282          * Castor JDO obtains a reference to the TransactionManager throught the InitialContext.
283          * The new InitialContext will use the deployment's JNDI Context, which is normal inside
284          * the container system, so we need to bind the TransactionManager to the deployment's name space
285          * The biggest problem with this is that the bean itself may access the TransactionManager if it
286          * knows the JNDI name, so we bind the TransactionManager into dynamically created transient name
287          * space based every time the container starts. It nearly impossible for the bean to anticipate
288          * and use the binding directly. It may be possible, however, to locate it using a Context listing method.
289          */

290
291         String JavaDoc transactionManagerJndiName = "java:openejb/TransactionManager";
292
293         /*
294          * This container uses two different JDO objects. One whose transactions are managed by a tx manager
295          * and which is not. The following code configures both.
296          */

297         jdo_ForGlobalTransaction = new JDO();
298
299         jdo_ForGlobalTransaction.setDatabasePooling( true );
300         jdo_ForGlobalTransaction.setConfiguration( gTxDb.getAbsolutePath() );
301         jdo_ForGlobalTransaction.setDatabaseName(EnvProps.GLOBAL_TX_DATABASE);
302         jdo_ForGlobalTransaction.setCallbackInterceptor( this );
303         jdo_ForGlobalTransaction.setInstanceFactory( this );
304         jdo_ForGlobalTransaction.setLogInterceptor( new CMPLogger(EnvProps.GLOBAL_TX_DATABASE) );
305
306         // Make sure the DB is registered as a as synchronization object before the transaction begins.
307
jdo_ForLocalTransaction = new JDO();
308
309
310         jdo_ForLocalTransaction.setConfiguration( lTxDb.getAbsolutePath() );
311         jdo_ForLocalTransaction.setDatabaseName(EnvProps.LOCAL_TX_DATABASE);
312         jdo_ForLocalTransaction.setCallbackInterceptor( this );
313         jdo_ForLocalTransaction.setInstanceFactory( this );
314         jdo_ForLocalTransaction.setLogInterceptor( new CMPLogger(EnvProps.LOCAL_TX_DATABASE) );
315
316
317         /*
318          * This block of code is necessary to avoid a chicken and egg problem.
319          * The DeploymentInfo objects must have a reference to their container
320          * during this assembly process, but the container is created after the
321          * DeploymentInfo necessitating this loop to assign all deployment info
322          * object's their containers.
323          *
324          * In addition the loop is leveraged for other oprations like creating
325          * the method ready pool and the keyGenerator pool.
326          */

327         org.openejb.DeploymentInfo[] deploys = this.deployments();
328
329         /*
330          * the JndiTxReference will dynamically obtian a reference to the TransactionManger the first
331          * time it used. The same Reference is shared by all deployments, which is not a problem.
332          */

333         JndiTxReference txReference = new JndiTxReference();
334         for ( int x = 0; x < deploys.length; x++ ) {
335             org.openejb.core.DeploymentInfo di = ( org.openejb.core.DeploymentInfo ) deploys[x];
336             di.setContainer( this );
337
338             // also added this line to create the Method Ready Pool for each deployment
339
methodReadyPoolMap.put( di.getDeploymentID(), new LinkedListStack( poolsize / 2 ) );
340             KeyGenerator kg = null;
341             try {
342                 kg = KeyGeneratorFactory.createKeyGenerator( di );
343                 di.setKeyGenerator( kg );
344             } catch ( Exception JavaDoc e ) {
345                 logger.error( "Unable to create KeyGenerator for deployment id = " + di.getDeploymentID(), e );
346                 throw new org.openejb.SystemException( "Unable to create KeyGenerator for deployment id = " + di.getDeploymentID(), e );
347             }
348
349             // bind the TransactionManager to the dynamically generated JNDI name
350
try {
351                 di.getJndiEnc().bind( transactionManagerJndiName, txReference );
352             } catch ( Exception JavaDoc e ) {
353                 logger.error( "Unable to bind TransactionManager to deployment id = " + di.getDeploymentID() + " using JNDI name = \"" + transactionManagerJndiName + "\"", e );
354                 throw new org.openejb.SystemException( "Unable to bind TransactionManager to deployment id = " + di.getDeploymentID() + " using JNDI name = \"" + transactionManagerJndiName + "\"", e );
355             }
356
357             try {
358                 /**
359                  * The following code adds a findByPrimaryKey query-statement to the list of queries
360                  * held by the deployment descriptor. The container is required to generate this query
361                  * automatically, which is what this code does.
362                  */

363                 StringBuffer JavaDoc findByPrimarKeyQuery = new StringBuffer JavaDoc("SELECT e FROM " + di.getBeanClass().getName() + " e WHERE ");
364
365                 if ( kg.isKeyComplex() ) {
366
367                     Field JavaDoc[] pkFields = di.getPrimaryKeyClass().getFields();
368                     for ( int i = 1; i <= pkFields.length; i++ ) {
369                         findByPrimarKeyQuery.append("e." + pkFields[i - 1].getName() + " = $" + i);
370                         if ( ( i + 1 ) <= pkFields.length )
371                             findByPrimarKeyQuery.append(" AND ");
372                     }
373
374                 } else {
375                     findByPrimarKeyQuery.append("e." + di.getPrimaryKeyField().getName() + " = $1");
376                 }
377
378                 if (di.getHomeInterface() != null) {
379                     Method JavaDoc findByPrimaryKeyMethod = di.getHomeInterface().getMethod( "findByPrimaryKey", new Class JavaDoc[]{di.getPrimaryKeyClass()} );
380                     di.addQuery( findByPrimaryKeyMethod, findByPrimarKeyQuery.toString() );
381                 }
382                 if (di.getLocalHomeInterface() != null) {
383                     Method JavaDoc findByPrimaryKeyMethod = di.getLocalHomeInterface().getMethod( "findByPrimaryKey", new Class JavaDoc[]{di.getPrimaryKeyClass()} );
384                     di.addQuery( findByPrimaryKeyMethod, findByPrimarKeyQuery.toString() );
385                 }
386             } catch ( Exception JavaDoc e ) {
387                 throw new org.openejb.SystemException( "Could not generate a query statement for the findByPrimaryKey method of the deployment = " + di.getDeploymentID(), e );
388             }
389         }
390         resetMap = new HashMap JavaDoc();
391         resetMap.put( byte.class, new Byte JavaDoc( ( byte ) 0 ) );
392         resetMap.put( boolean.class, new Boolean JavaDoc( false ) );
393         resetMap.put( char.class, new Character JavaDoc( ( char ) 0 ) );
394         resetMap.put( short.class, new Short JavaDoc( ( short ) 0 ) );
395         resetMap.put( int.class, new Integer JavaDoc( 0 ) );
396         resetMap.put( long.class, new Long JavaDoc( 0 ) );
397         resetMap.put( float.class, new Float JavaDoc( 0 ) );
398         resetMap.put( double.class, new Double JavaDoc( 0.0 ) );
399     }
400
401     boolean initialized;
402
403     /**
404      * I keep forgetting why I put this block outside the init method.
405      * Caused by: java.lang.NullPointerException
406      * at org.openejb.OpenEJB.getJNDIContext(OpenEJB.java:415)
407      * at org.openejb.core.ivm.naming.java.javaURLContextFactory.getContext(javaURLContextFactory.java:111)
408      * at org.openejb.core.ivm.naming.java.javaURLContextFactory.getObjectInstance(javaURLContextFactory.java:87)
409      * at javax.naming.spi.NamingManager.getURLObject(NamingManager.java:579)
410      *
411      * That's the reason. A dependency problem. Castor JDO depends on looking up the
412      * datasource from JNDI, which has not been fully constructed till after the containers
413      * are constructed.
414      */

415     protected void postInit() {
416         if (initialized) return;
417
418         Properties JavaDoc systemProperties = System.getProperties();
419         synchronized(systemProperties) {
420             String JavaDoc userDir = systemProperties.getProperty("user.dir");
421             try{
422                 File JavaDoc base = SystemInstance.get().getBase().getDirectory();
423                 systemProperties.setProperty("user.dir", base.getAbsolutePath());
424                 ClassLoader JavaDoc classLoader = deployments()[0].getBeanClass().getClassLoader();
425                 jdo_ForLocalTransaction.setClassLoader(classLoader);
426                 jdo_ForLocalTransaction.getDatabase();
427                 jdo_ForGlobalTransaction.setClassLoader(classLoader);
428                 jdo_ForGlobalTransaction.getDatabase();
429             } catch (Throwable JavaDoc e){
430                 logger.fatal("Castor JDO initialization failed: "+e.getMessage(), e);
431                 throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc("Castor JDO initialization failed").initCause(e);
432             } finally {
433                 systemProperties.setProperty("user.dir",userDir);
434             }
435             initialized = true;
436         }
437     }
438     //===============================
439
// begin Container Implementation
440
//
441

442     /**
443      * Gets the <code>DeploymentInfo</code> objects for all the beans deployed
444      * in this container.
445      *
446      * @return an array of DeploymentInfo objects
447      * @see org.openejb.DeploymentInfo
448      * @see org.openejb.Container#deployments()
449      */

450     public DeploymentInfo[] deployments() {
451         return( DeploymentInfo[] ) deploymentRegistry.values().toArray( new DeploymentInfo[deploymentRegistry.size()] );
452     }
453
454     /**
455      * Gets the <code>DeploymentInfo</code> object for the bean with the
456      * specified deployment id.
457      *
458      * @param deploymentID
459      * @return the DeploymentInfo object associated with the bean.
460      * @see org.openejb.DeploymentInfo
461      * @see org.openejb.Container#getDeploymentInfo(Object)
462      * @see org.openejb.DeploymentInfo#getDeploymentID()
463      */

464     public DeploymentInfo getDeploymentInfo( Object JavaDoc deploymentID ) {
465         return( DeploymentInfo ) deploymentRegistry.get( deploymentID );
466     }
467
468     /**
469      * Gets the type of container (STATELESS, STATEFUL, ENTITY, or MESSAGE_DRIVEN
470      *
471      * @return id type bean container
472      */

473     public int getContainerType() {
474         return Container.ENTITY;
475     }
476
477     /**
478      * Gets the id of this container.
479      *
480      * @return the id of this container.
481      * @see org.openejb.Container#getContainerID
482      */

483     public Object JavaDoc getContainerID() {
484         return containerID;
485     }
486
487     /**
488      * Adds a bean to this container.
489      * @param deploymentID the deployment id of the bean to deploy.
490      * @param info the DeploymentInfo object associated with the bean.
491      * @throws org.openejb.OpenEJBException
492      * Occurs when the container is not able to deploy the bean for some
493      * reason.
494      */

495     public void deploy( Object JavaDoc deploymentID, DeploymentInfo info ) throws OpenEJBException {
496         HashMap JavaDoc registry = ( HashMap JavaDoc ) deploymentRegistry.clone();
497         registry.put( deploymentID, info );
498         deploymentRegistry = registry;
499     }
500
501     /**
502      * Invokes a method on an instance of the specified bean deployment.
503      *
504      * @param deployID the dployment id of the bean deployment
505      * @param callMethod the method to be called on the bean instance
506      * @param args the arguments to use when invoking the specified method
507      * @param primKey the primary key class of the bean or null if the bean does not need a primary key
508      * @param securityIdentity
509      * @return the result of invoking the specified method on the bean instance
510      * @throws org.openejb.OpenEJBException
511      * @see org.openejb.RpcContainer#invoke
512      * @see org.openejb.core.stateful.StatefulContainer#invoke StatefulContainer.invoke
513      */

514     public Object JavaDoc invoke( Object JavaDoc deployID, Method JavaDoc callMethod, Object JavaDoc[] args, Object JavaDoc primKey, Object JavaDoc securityIdentity )
515     throws org.openejb.OpenEJBException {
516         postInit();
517         try {
518             org.openejb.core.DeploymentInfo deployInfo = ( org.openejb.core.DeploymentInfo ) this.getDeploymentInfo( deployID );
519
520             ThreadContext callContext = ThreadContext.getThreadContext();
521             callContext.set( deployInfo, primKey, securityIdentity );
522
523             // check authorization to invoke
524

525             boolean authorized = OpenEJB.getSecurityService().isCallerAuthorized( securityIdentity, deployInfo.getAuthorizedRoles( callMethod ) );
526             if ( !authorized )
527                 throw new org.openejb.ApplicationException( new RemoteException JavaDoc( "Unauthorized Access by Principal Denied" ) );
528
529             // process home interface methods
530
Class JavaDoc declaringClass = callMethod.getDeclaringClass();
531             String JavaDoc methodName = callMethod.getName();
532
533             if (EJBHome JavaDoc.class.isAssignableFrom(declaringClass) || EJBLocalHome JavaDoc.class.isAssignableFrom(declaringClass) ){
534                 if ( declaringClass != EJBHome JavaDoc.class && declaringClass != EJBLocalHome JavaDoc.class) {
535                     // Its a home interface method, which is declared by the bean provider, but not a EJBHome method.
536
// only create() and find<METHOD>( ) are declared by the bean provider.
537
if ( methodName.equals( "create" ) ) {
538                         // create( ) method called, execute ejbCreate() method
539
return createEJBObject( callMethod, args, callContext );
540                     } else if ( methodName.startsWith( "find" ) ) {
541                         // find<METHOD> called, execute ejbFind<METHOD>
542
return findEJBObject( callMethod, args, callContext );
543                     } else {
544                         // home method called, execute ejbHome method
545
throw new org.openejb.InvalidateReferenceException( new java.rmi.RemoteException JavaDoc( "Invalid method " + methodName + " only find<METHOD>( ) and create( ) method are allowed in EJB 1.1 container-managed persistence" ) );
546                     }
547                 } else if ( methodName.equals( "remove" ) ) {
548                     removeEJBObject( callMethod, args, callContext );
549                     return null;
550                 }
551             } else if((EJBObject JavaDoc.class == declaringClass || EJBLocalObject JavaDoc.class == declaringClass) && methodName.equals("remove") ) {
552                 removeEJBObject( callMethod, args, callContext );
553                 return null;
554             }
555
556
557
558             // retreive instance from instance manager
559
callContext.setCurrentOperation( Operations.OP_BUSINESS );
560             Method JavaDoc runMethod = deployInfo.getMatchingBeanMethod( callMethod );
561
562             Object JavaDoc retValue = businessMethod( callMethod, runMethod, args, callContext );
563
564             // see comments in org.openejb.core.DeploymentInfo.
565
return deployInfo.convertIfLocalReference( callMethod, retValue );
566
567
568         } finally {
569             /*
570                 The thread context must be stripped from the thread before returning or throwing an exception
571                 so that an object outside the container does not have access to a
572                 bean's JNDI ENC. In addition, its important for the
573                 org.openejb.core.ivm.java.javaURLContextFactory, which determines the context
574                 of a JNDI lookup based on the presence of a ThreadContext object. If no ThreadContext
575                 object is available, then the request is assumed to be made from outside the container
576                 system and is given the global OpenEJB JNDI name space instead. If there is a thread context,
577                 then the request is assumed to be made from within the container system and so the
578                 javaContextFactory must return the JNDI ENC of the current enterprise bean which it
579                 obtains from the DeploymentInfo object associated with the current thread context.
580             */

581             ThreadContext.setThreadContext( null );
582         }
583     }
584     //
585
// end ContainerManager Implementation
586
//====================================
587

588
589     //============================================
590
// begin methods unique to this implementation
591
//
592

593     /**
594      * Discards this instance so that it may be garbage collected
595      *
596      * @param bean
597      * @param threadContext
598      */

599     public void discardInstance( EnterpriseBean JavaDoc bean, ThreadContext threadContext ) {
600         if ( bean != null ) txReadyPoolMap.remove( bean );
601     }
602
603     /**
604      * Obtains a bean instance from the method ready pool. If the pool is empty
605      * a new instance is instantiated,
606      * and the setEntityContext method is called.
607      *
608      * The bean instance is transitioned into the tx method ready pool before
609      * its returned to the caller. this ensures it can returned to the method
610      * ready pool when its released from the transaction.
611      *
612      * @param callContext
613      * @return EntityBean
614      * @exception java.lang.IllegalAccessException
615      * @exception java.lang.reflect.InvocationTargetException
616      * @exception java.lang.InstantiationException
617      */

618     public EntityBean JavaDoc fetchFreeInstance( ThreadContext callContext ) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, InstantiationException JavaDoc {
619
620         org.openejb.core.DeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
621
622         /*
623         Obtain the stack of instances of this deployment that are in the method ready state.
624         */

625         Stack methodReadyPool = ( Stack ) methodReadyPoolMap.get( deploymentInfo.getDeploymentID() );
626
627         if ( methodReadyPool == null ) {
628             // TODO:3: Localize this message
629
throw new java.lang.RuntimeException JavaDoc( "Invalid deployment id " + deploymentInfo.getDeploymentID() + " for this container" );
630         }
631
632         /*
633         Get a method ready instance from the top of the stack.
634         */

635         //DMB: This is funny, pop will always return null because we _never_ add
636
// any instances to the stack. What is the point of this pool?
637
EntityBean JavaDoc bean = ( EntityBean JavaDoc ) methodReadyPool.pop();
638
639         if ( bean == null ) {
640             byte currentOperation = callContext.getCurrentOperation();
641             try {
642                 bean = ( EntityBean JavaDoc ) deploymentInfo.getBeanClass().newInstance();
643                 /*
644                 setEntityContext executes in an unspecified transactional context.
645                 In this case we choose to allow it to have what every transaction
646                 context is current. Better then suspending it unnecessarily.
647                 */

648                 callContext.setCurrentOperation( Operations.OP_SET_CONTEXT );
649                 Object JavaDoc[] params = new javax.ejb.EntityContext JavaDoc[]{( javax.ejb.EntityContext JavaDoc ) deploymentInfo.getEJBContext()};
650                 //logger.debug(bean + ".setEntityContext("+params[0]+")");
651
SET_ENTITY_CONTEXT_METHOD.invoke( bean, params );
652             } finally {
653                 callContext.setCurrentOperation( currentOperation );
654             }
655         } else {
656             // Here we need to reset all fields to their default values ( 0 for primitive types, null for pointers )
657
resetBeanFields( bean, deploymentInfo );
658         }
659         // move the bean instance to the tx method ready pool
660
txReadyPoolMap.put( bean, methodReadyPool );
661         return bean;
662     }
663
664
665     /**
666      * Processes a business method invokation
667      *
668      * @param callMethod
669      * @param runMethod
670      * @param args
671      * @param callContext
672      * @return Object
673      * @exception org.openejb.OpenEJBException
674      */

675     protected Object JavaDoc businessMethod( Method JavaDoc callMethod, Method JavaDoc runMethod, Object JavaDoc[] args, ThreadContext callContext )
676     throws org.openejb.OpenEJBException {
677
678         EntityBean JavaDoc bean = null;
679
680         TransactionPolicy txPolicy = callContext.getDeploymentInfo().getTransactionPolicy( callMethod );
681         TransactionContext txContext = new TransactionContext( callContext );
682
683         txPolicy.beforeInvoke( bean, txContext );
684
685         Object JavaDoc returnValue = null;
686         try {
687
688             Database db = getDatabase( callContext );
689
690             bean = fetchAndLoadBean( callContext, db );
691             //logger.debug("Invoking business method on "+bean);
692
if ( OpenEJB.getTransactionManager().getTransaction() != null ) {
693                 try {
694                     Key key = new Key( OpenEJB.getTransactionManager().getTransaction(),
695                                        callContext.getDeploymentInfo().getDeploymentID(),
696                                        callContext.getPrimaryKey() );
697                     SynchronizationWrapper sync = new SynchronizationWrapper( ( ( javax.ejb.EntityBean JavaDoc ) bean ), key );
698
699                     OpenEJB.getTransactionManager().getTransaction().registerSynchronization( sync );
700
701                     syncWrappers.put( key, sync );
702                 } catch ( Exception JavaDoc ex ) {
703                     ex.printStackTrace();
704                 }
705             }
706
707             returnValue = runMethod.invoke( bean, args );
708
709         } catch ( java.lang.reflect.InvocationTargetException JavaDoc ite ) {
710             // handle enterprise bean exceptions
711
if ( ite.getTargetException() instanceof RuntimeException JavaDoc ) {
712                 /* System Exception ****************************/
713                 txPolicy.handleSystemException( ite.getTargetException(), bean, txContext );
714
715             } else {
716                 /* Application Exception ***********************/
717                 txPolicy.handleApplicationException( ite.getTargetException(), txContext );
718             }
719         } catch ( org.exolab.castor.jdo.DuplicateIdentityException e ) {
720             /* Application Exception ***********************/
721             //TODO:3: Localize this message
722
Exception JavaDoc re = new javax.ejb.DuplicateKeyException JavaDoc( "Attempt to update an entity bean (DeploymentID=\"" + callContext.getDeploymentInfo().getDeploymentID() + "\") with an primary key that already exsists. Castor nested exception message = " + e.getMessage() );
723             txPolicy.handleSystemException( re, bean, txContext );
724
725         } catch ( org.exolab.castor.jdo.ClassNotPersistenceCapableException e ) {
726             /* System Exception ****************************/
727             //TODO:3: Localize this message
728
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to update an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") that can not be persisted.", e );
729             txPolicy.handleSystemException( re, bean, txContext );
730
731         } catch ( org.exolab.castor.jdo.TransactionAbortedException e ) {
732             /* System Exception ****************************/
733             //TODO:3: Localize this message
734
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to update an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") failed because transaction was aborted.", e );
735             txPolicy.handleSystemException( re, bean, txContext );
736
737         } catch ( org.exolab.castor.jdo.TransactionNotInProgressException e ) {
738             /* System Exception ****************************/
739             //TODO:3: Localize this message
740
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to update an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") failed because a transaction didn't exist.", e );
741             txPolicy.handleSystemException( re, bean, txContext );
742
743         } catch ( org.exolab.castor.jdo.DatabaseNotFoundException e ) {
744             /* System Exception ****************************/
745             txPolicy.handleSystemException( e, bean, txContext );
746
747         } catch ( org.exolab.castor.jdo.PersistenceException e ) {
748             /* System Exception ****************************/
749             txPolicy.handleSystemException( e, bean, txContext );
750
751         } catch ( Throwable JavaDoc e ) {// handle reflection exception
752
/*
753               Any exception thrown by reflection; not by the enterprise bean. Possible
754               Exceptions are:
755                 InstantiationException - if the bean instance can not be instantiated. Thrown by fetchAndLoadBean()
756                 IllegalAccessException - if the underlying method is inaccessible.
757                 IllegalArgumentException - if the number of actual and formal parameters differ, or if an unwrapping conversion fails.
758                 NullPointerException - if the specified object is null and the method is an instance method.
759                 ExceptionInInitializerError - if the initialization provoked by this method fails.
760             */

761             /* System Exception ****************************/
762             txPolicy.handleSystemException( e, bean, txContext );
763
764         } finally {
765             txPolicy.afterInvoke( bean, txContext );
766         }
767
768         return returnValue;
769
770
771     }
772
773     /**
774      * This method is responsible for delegating the ejbCreate() and
775      * ejbPostCreate() methods on the an entity bean. Transaction attributes are
776      * applied to determine the correct transaction context.
777      *
778      * Allowed operations are imposed according to the EJB 1.1 specification.
779      *
780      * @param callMethod
781      * @param args
782      * @param callContext
783      * @return ProxyInfo
784      * @exception org.openejb.OpenEJBException
785      */

786     protected ProxyInfo createEJBObject( Method JavaDoc callMethod, Object JavaDoc[] args, ThreadContext callContext )
787     throws org.openejb.OpenEJBException {
788         org.openejb.core.DeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
789
790         EntityBean JavaDoc bean = null;
791         Object JavaDoc primaryKey = null;
792
793         TransactionPolicy txPolicy = callContext.getDeploymentInfo().getTransactionPolicy( callMethod );
794         TransactionContext txContext = new TransactionContext( callContext );
795
796         txPolicy.beforeInvoke( bean, txContext );
797
798
799         try {
800
801             /*
802               Obtain a bean instance from the method ready pool
803             */

804             bean = fetchFreeInstance( callContext );
805
806             /*
807                Obtain the proper ejbCreate() method
808             */

809             Method JavaDoc ejbCreateMethod = deploymentInfo.getMatchingBeanMethod( callMethod );
810
811             /*
812               Set the context for allowed operations
813             */

814             callContext.setCurrentOperation( Operations.OP_CREATE );
815
816             /*
817               Invoke the proper ejbCreate() method on the instance
818             */

819             ejbCreateMethod.invoke( bean, args );
820
821             int txStatus = OpenEJB.getTransactionManager().getStatus();
822             if ( txStatus == Status.STATUS_ACTIVE || txStatus == Status.STATUS_NO_TRANSACTION ) {
823
824                 /*
825                   Get the JDO database for this deployment
826                 */

827                 Database db = getDatabase( callContext );
828
829                 /*
830                   Create a Castor Transaction if there isn't one in progress
831                 */

832                 if ( !db.isActive() ) db.begin();
833
834                 /*
835                   Use Castor JDO to insert the entity bean into the database
836                 */

837                 db.create( bean );
838
839             }
840
841             /*
842             Each bean deployment has a unique KeyGenerator that is responsible
843             for two operations.
844             1. Convert EJB developer defined complex primary keys to Castor
845                JDO Complex objects
846             2. Extract a primary key object from a loaded Entity bean instance.
847             */

848             KeyGenerator kg = deploymentInfo.getKeyGenerator();
849
850             /*
851             The KeyGenerator creates a new primary key and populates its fields with the
852             primary key fields of the bean instance. Each deployment has its own KeyGenerator.
853             */

854             primaryKey = kg.getPrimaryKey( bean );
855
856             /*
857               place the primary key into the current ThreadContext so its available for
858               the ejbPostCreate()
859             */

860             callContext.setPrimaryKey( primaryKey );
861
862             /*
863               Set the current operation for the allowed operations check
864             */

865             callContext.setCurrentOperation( Operations.OP_POST_CREATE );
866
867             /*
868               Obtain the ejbPostCreate method that matches the ejbCreate method
869             */

870             Method JavaDoc ejbPostCreateMethod = deploymentInfo.getMatchingPostCreateMethod( ejbCreateMethod );
871
872             /*
873               Invoke the ejbPostCreate method on the bean instance
874             */

875             ejbPostCreateMethod.invoke( bean, args );
876
877             /*
878             According to section 9.1.5.1 of the EJB 1.1 specification, the "ejbPostCreate(...)
879             method executes in the same transaction context as the previous ejbCreate(...) method."
880
881             The bean is first insterted using db.create( ) and then after ejbPostCreate( ) its
882             updated using db.update(). This protocol allows for visablity of the bean after ejbCreate
883             within the current trasnaction.
884             */

885             //DMB: Why is update commented out?
886
//db.update(bean);
887

888             /*
889               Reset the primary key in the ThreadContext to null, its original value
890             */

891             callContext.setPrimaryKey( null );
892
893         } catch ( java.lang.reflect.InvocationTargetException JavaDoc ite ) {// handle enterprise bean exceptions
894
if ( ite.getTargetException() instanceof RuntimeException JavaDoc ) {
895                 /* System Exception ****************************/
896                 txPolicy.handleSystemException( ite.getTargetException(), bean, txContext );
897             } else {
898                 /* Application Exception ***********************/
899                 txPolicy.handleApplicationException( ite.getTargetException(), txContext );
900             }
901         } catch ( org.exolab.castor.jdo.DuplicateIdentityException e ) {
902             /* Application Exception ***********************/
903             Exception JavaDoc re = new javax.ejb.DuplicateKeyException JavaDoc( "Attempt to create an entity bean (DeploymentID=\"" + callContext.getDeploymentInfo().getDeploymentID() + "\") with an primary key that already exsists. Castor nested exception message = " + e.getMessage() );
904             txPolicy.handleSystemException( re, bean, txContext );
905
906         } catch ( org.exolab.castor.jdo.ClassNotPersistenceCapableException e ) {
907             /* System Exception ****************************/
908             RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to create an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") that can not be persisted.", e );
909             txPolicy.handleSystemException( re, bean, txContext );
910
911         } catch ( org.exolab.castor.jdo.TransactionAbortedException e ) {
912             /* System Exception ****************************/
913             //TransactionRolledbackException re = new TransactionRolledbackException("Attempt to create an entity bean (DeploymentID=\""+ThreadContext.getThreadContext().getDeploymentInfo().getDeploymentID()+"\") failed because transaction was aborted. Nested exception message = "+tae.getMessage()));
914
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to create an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") failed because transaction was aborted.", e );
915             txPolicy.handleSystemException( re, bean, txContext );
916
917         } catch ( org.exolab.castor.jdo.TransactionNotInProgressException e ) {
918             /* System Exception ****************************/
919             RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to create an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") failed because a transaction didn't exist.", e );
920             txPolicy.handleSystemException( re, bean, txContext );
921
922         } catch ( org.exolab.castor.jdo.DatabaseNotFoundException e ) {
923             /* System Exception ****************************/
924             txPolicy.handleSystemException( e, bean, txContext );
925
926         } catch ( org.exolab.castor.jdo.PersistenceException e ) {
927             /* System Exception ****************************/
928             txPolicy.handleSystemException( e, bean, txContext );
929
930         } catch ( Throwable JavaDoc e ) {// handle reflection exception
931
/*
932               Any exception thrown by reflection; not by the enterprise bean. Possible
933               Exceptions are:
934                 InstantiationException - if the bean instance can not be instantiated. Thrown by fetchAndLoadBean()
935                 IllegalAccessException - if the underlying method is inaccessible.
936                 IllegalArgumentException - if the number of actual and formal parameters differ, or if an unwrapping conversion fails.
937                 NullPointerException - if the specified object is null and the method is an instance method.
938                 ExceptionInInitializerError - if the initialization provoked by this method fails.
939             */

940             /* System Exception ****************************/
941             txPolicy.handleSystemException( e, bean, txContext );
942         } finally {
943             txPolicy.afterInvoke( bean, txContext );
944         }
945         Class JavaDoc callingClass = callMethod.getDeclaringClass();
946         boolean isLocalInterface = EJBLocalHome JavaDoc.class.isAssignableFrom(callingClass);
947
948         return new ProxyInfo( deploymentInfo, primaryKey, isLocalInterface, this );
949     }
950
951     protected static final Object JavaDoc[] noArgs = new Object JavaDoc[0];
952
953     /**
954      * This method is used to execute the find methods which are considered
955      * global in scope. Global methods use bean instances from the MethodReady
956      * pool and are not specific to on bean identity.
957      *
958      * The return value will be either a single ProxyInfo object or collection of
959      * ProxyInfo objects representing one or more remote references.
960      *
961      * @param callMethod
962      * @param args
963      * @param callContext
964      * @return Object
965      * @exception org.openejb.OpenEJBException
966      */

967     protected Object JavaDoc findEJBObject( Method JavaDoc callMethod, Object JavaDoc[] args, ThreadContext callContext ) throws org.openejb.OpenEJBException {
968
969         org.openejb.core.DeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
970
971         QueryResults results = null;
972         Object JavaDoc returnValue = null;
973         EntityBean JavaDoc bean = null;
974
975         /* Obtain the OQL statement that matches the find method of the remote interface */
976         String JavaDoc queryString = deploymentInfo.getQuery( callMethod );
977
978         /* Get the transaction policy assigned to this method */
979         TransactionPolicy txPolicy = callContext.getDeploymentInfo().getTransactionPolicy( callMethod );
980         TransactionContext txContext = new TransactionContext( callContext );
981
982         txPolicy.beforeInvoke( bean, txContext );
983
984
985         try {
986
987             /*
988               Get the JDO database for this deployment
989             */

990             Database db = getDatabase( callContext );
991
992             /*
993               Create a Castor Transaction if there isn't one in progress
994             */

995             if ( !db.isActive() ) db.begin();
996
997             /*
998               Obtain a OQLQuery object based on the String query
999             */

1000            OQLQuery query = db.getOQLQuery( queryString );
1001
1002
1003            if ( callMethod.getName().equals( "findByPrimaryKey" ) ) {
1004                // bind complex primary key to query
1005
KeyGenerator kg = deploymentInfo.getKeyGenerator();
1006
1007                if ( kg.isKeyComplex() ) {
1008                    /*
1009                    * This code moves the fields of the primary key into a JDO Complex object
1010                    * which can then be used in the database.bind operation
1011                    */

1012                    org.exolab.castor.persist.spi.Complex c = kg.getJdoComplex( args[0] );
1013                    args = new Object JavaDoc[c.size()];
1014                    for ( int i = 0; i < args.length; i++ )
1015                        args[i] = c.get( i );
1016                }
1017            }
1018
1019
1020            if ( args == null ) args = noArgs;
1021
1022            for ( int i = 0; i < args.length; i++ ) {
1023                if ( args[i] instanceof javax.ejb.EJBObject JavaDoc ) {
1024                    /*
1025                    Its possible that the finder method's arguments are actually EJBObject reference in
1026                    which case the EJBObject reference is replaced with the EJB object's primary key.
1027                    The limitation of this facility is that the EJB object must use a single field primary key
1028                    and not a complex primary key. Complex primary keys of EJBObject argumetns are not supported.
1029                    For Example:
1030
1031                    EJB Home Interface Find method:
1032                    public Collection findThings(Customer customer);
1033
1034                    OQL in deployment descriptor
1035                    "SELECT t FROM Thing t WHERE t.customer_id = $1"
1036
1037                    */

1038                    try {
1039                        args[i] = ( ( javax.ejb.EJBObject JavaDoc ) args[i] ).getPrimaryKey();
1040                    } catch ( java.rmi.RemoteException JavaDoc re ) {
1041                        //TODO:3: Localize this message
1042
throw new javax.ejb.FinderException JavaDoc( "Could not extract primary key from EJBObject reference; argument number " + i );
1043                    }
1044                }
1045
1046                /*
1047                Bind the arguments of the home interface find method to the query.
1048                The big assumption here is that the arguments of the find operation
1049                are in the same order as the arguments in the query. The bean developer
1050                must declare the OQL arguments with the proper number so that match the order
1051                of the find arguments.
1052                For Example:
1053
1054                EJB Home Interface Find method:
1055                public Collection findThings(String name, double weight, String Type);
1056
1057                OQL in deployment descriptor
1058                "SELECT t FROM Thing t WHERE t.weight = $2 AND t.type = $3 AND t.name = $1"
1059                */

1060
1061                query.bind( args[i] );
1062            }
1063
1064
1065            /* execute the query */
1066            results = query.execute();
1067
1068            /*
1069            Each bean deployment has a unique KeyGenerator that is responsible for two operations.
1070            1. Convert EJB developer defined complex primary keys to Castor JDO Complex objects
1071            2. Extract a primary key object from a loaded Entity bean instance.
1072            */

1073            KeyGenerator kg = deploymentInfo.getKeyGenerator();
1074
1075            Object JavaDoc primaryKey = null;
1076            Class JavaDoc callingClass = callMethod.getDeclaringClass();
1077            boolean isLocalInterface = EJBLocalHome JavaDoc.class.isAssignableFrom(callingClass);
1078
1079            /*
1080            The following block of code is responsible for returning ProxyInfo object(s) for each
1081            matching entity bean found by the query. If its a multi-value find operation a Vector
1082            of ProxyInfo objects will be returned. If its a single-value find operation then a
1083            single ProxyInfo object is returned.
1084            */

1085            if ( callMethod.getReturnType() == java.util.Collection JavaDoc.class || callMethod.getReturnType() == java.util.Enumeration JavaDoc.class ) {
1086                java.util.Vector JavaDoc proxies = new java.util.Vector JavaDoc();
1087                while ( results.hasMore() ) {
1088                    /* Fetch the next entity bean from the query results */
1089                    bean = ( EntityBean JavaDoc ) results.next();
1090
1091                    /*
1092                    The KeyGenerator creates a new primary key and populates its fields with the
1093                    primary key fields of the bean instance. Each deployment has its own KeyGenerator.
1094                    */

1095                    primaryKey = kg.getPrimaryKey( bean );
1096                    /* create a new ProxyInfo based on the deployment info and primary key and add it to the vector */
1097                    proxies.addElement( new ProxyInfo( deploymentInfo, primaryKey, isLocalInterface, this ) );
1098                }
1099                if ( callMethod.getReturnType() == java.util.Enumeration JavaDoc.class )
1100                    returnValue = new org.openejb.util.Enumerator( proxies );
1101                else
1102                    returnValue = proxies;
1103            } else {
1104                /* Fetch the entity bean from the query results */
1105                if ( !results.hasMore() )
1106                    throw new javax.ejb.ObjectNotFoundException JavaDoc( "A Enteprise bean with deployment_id = " + deploymentInfo.getDeploymentID() + " and primarykey = " + args[0] + " Does not exist" );
1107
1108                bean = ( EntityBean JavaDoc ) results.next();
1109                /*
1110                    The KeyGenerator creates a new primary key and populates its fields with the
1111                    primary key fields of the bean instance. Each deployment has its own KeyGenerator.
1112                */

1113                primaryKey = kg.getPrimaryKey( bean );
1114                /* create a new ProxyInfo based on the deployment info and primary key */
1115                returnValue = new ProxyInfo( deploymentInfo, primaryKey, isLocalInterface, this );
1116            }
1117
1118        } catch ( javax.ejb.FinderException JavaDoc fe ) {
1119            /* Application Exception *********************** thrown when attempting to extract EJBObject argument */
1120            txPolicy.handleApplicationException( fe, txContext );
1121
1122        } catch ( org.exolab.castor.jdo.QueryException qe ) {
1123            /* Application Exception ***********************/
1124            javax.ejb.FinderException JavaDoc fe = new javax.ejb.FinderException JavaDoc( "Castor JDO could not execute query for this finder method. QueryException: " + qe.getMessage() );
1125            // TODO:3: Localize this message
1126
txPolicy.handleApplicationException( fe, txContext );
1127
1128        } catch ( org.exolab.castor.jdo.TransactionNotInProgressException e ) {
1129            /* System Exception ****************************/
1130            // TODO:3: Localize this message
1131
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to create an entity bean (DeploymentID=\"" + callContext.getDeploymentInfo().getDeploymentID() + "\") failed because a transaction didn't exist.", e );
1132            txPolicy.handleSystemException( re, bean, txContext );
1133
1134        } catch ( org.exolab.castor.jdo.DatabaseNotFoundException e ) {
1135            /* System Exception ****************************/
1136            txPolicy.handleSystemException( e, bean, txContext );
1137
1138        } catch ( org.exolab.castor.jdo.PersistenceException e ) {
1139            /* System Exception ****************************/
1140            txPolicy.handleSystemException( e, bean, txContext );
1141
1142        } catch ( Throwable JavaDoc e ) {// handle reflection exception
1143
/*
1144              Any exception thrown by reflection; not by the enterprise bean. Possible
1145              Exceptions are:
1146                InstantiationException - if the bean instance can not be instantiated. Thrown by fetchAndLoadBean()
1147                IllegalAccessException - if the underlying method is inaccessible.
1148                IllegalArgumentException - if the number of actual and formal parameters differ, or if an unwrapping conversion fails.
1149                NullPointerException - if the specified object is null and the method is an instance method.
1150                ExceptionInInitializerError - if the initialization provoked by this method fails.
1151            */

1152            /* System Exception ****************************/
1153            txPolicy.handleSystemException( e, bean, txContext );
1154        } finally {
1155            if ( results != null ) results.close();
1156            txPolicy.afterInvoke( bean, txContext );
1157        }
1158        return returnValue;
1159    }
1160
1161
1162    /**
1163     * Removes the EJBObject
1164     *
1165     * @param callMethod
1166     * @param args
1167     * @param callContext
1168     * @exception org.openejb.OpenEJBException
1169     */

1170    protected void removeEJBObject( Method JavaDoc callMethod, Object JavaDoc[] args, ThreadContext callContext )
1171    throws org.openejb.OpenEJBException {
1172        EntityBean JavaDoc bean = null;
1173        TransactionContext txContext = new TransactionContext( callContext );
1174        TransactionPolicy txPolicy = callContext.getDeploymentInfo().getTransactionPolicy( callMethod );
1175
1176        txPolicy.beforeInvoke( bean, txContext );
1177
1178        try {
1179            int status = OpenEJB.getTransactionManager().getStatus();
1180            // are the other statuses possible here ?
1181
if ( status == Status.STATUS_ACTIVE || status == Status.STATUS_NO_TRANSACTION ) {
1182
1183                /*
1184                  Get the JDO database for this deployment
1185                */

1186                Database db = getDatabase( callContext );
1187
1188                /*
1189                  Create a Castor Transaction if there isn't one in progress
1190                */

1191                if ( !db.isActive() ) db.begin();
1192
1193                bean = fetchAndLoadBean( callContext, db );
1194
1195                callContext.setCurrentOperation( Operations.OP_REMOVE );
1196                EJB_REMOVE_METHOD.invoke( bean, null );
1197
1198                db.remove( bean );
1199            }
1200        } catch ( java.lang.reflect.InvocationTargetException JavaDoc ite ) {
1201            // handle enterprise bean exceptions
1202
if ( ite.getTargetException() instanceof RuntimeException JavaDoc ) {
1203                /* System Exception ****************************/
1204                txPolicy.handleSystemException( ite.getTargetException(), bean, txContext );
1205            } else {
1206                /* Application Exception ***********************/
1207                txPolicy.handleApplicationException( ite.getTargetException(), txContext );
1208            }
1209        } catch ( org.exolab.castor.jdo.DuplicateIdentityException e ) {
1210            /* Application Exception ***********************/
1211            // TODO:3: Localize this message
1212
Exception JavaDoc re = new javax.ejb.DuplicateKeyException JavaDoc( "Attempt to remove an entity bean (DeploymentID=\"" + callContext.getDeploymentInfo().getDeploymentID() + "\") with an primary key that already exsists. Castor nested exception message = " + e.getMessage() );
1213            txPolicy.handleSystemException( re, bean, txContext );
1214
1215        } catch ( org.exolab.castor.jdo.ClassNotPersistenceCapableException e ) {
1216            /* System Exception ****************************/
1217            // TODO:3: Localize this message
1218
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to remove an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") that can not be persisted.", e );
1219            txPolicy.handleSystemException( re, bean, txContext );
1220
1221        } catch ( org.exolab.castor.jdo.TransactionAbortedException e ) {
1222            /* System Exception ****************************/
1223            //TransactionRolledbackException re = new TransactionRolledbackException("Attempt to remove an entity bean (DeploymentID=\""+ThreadContext.getThreadContext().getDeploymentInfo().getDeploymentID()+"\") failed because transaction was aborted. Nested exception message = "+tae.getMessage()));
1224
// TODO:3: Localize this message
1225
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to remove an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") failed because transaction was aborted.", e );
1226            txPolicy.handleSystemException( re, bean, txContext );
1227
1228        } catch ( org.exolab.castor.jdo.TransactionNotInProgressException e ) {
1229            /* System Exception ****************************/
1230            // TODO:3: Localize this message
1231
RemoteException JavaDoc re = new RemoteException JavaDoc( "Attempt to remove an entity bean (DeploymentID=\"" + txContext.callContext.getDeploymentInfo().getDeploymentID() + "\") failed because a transaction didn't exist.", e );
1232            txPolicy.handleSystemException( re, bean, txContext );
1233
1234        } catch ( org.exolab.castor.jdo.DatabaseNotFoundException e ) {
1235            /* System Exception ****************************/
1236            txPolicy.handleSystemException( e, bean, txContext );
1237
1238        } catch ( org.exolab.castor.jdo.PersistenceException e ) {
1239            /* System Exception ****************************/
1240            txPolicy.handleSystemException( e, bean, txContext );
1241
1242        } catch ( Throwable JavaDoc e ) {// handle reflection exception
1243
/*
1244
1245              Any exception thrown by reflection; not by the enterprise bean.
1246              Possible Exceptions are:
1247              InstantiationException -
1248                if the bean instance can not be instantiated. Thrown by
1249                fetchAndLoadBean()
1250
1251              IllegalAccessException -
1252                if the underlying method is inaccessible.
1253
1254              IllegalArgumentException -
1255                if the number of actual and formal parameters differ, or
1256                if an unwrapping conversion fails.
1257
1258              NullPointerException -
1259                if the specified object is null and the method is an
1260                instance method.
1261
1262              ExceptionInInitializerError -
1263                if the initialization provoked by this method fails.
1264
1265            */

1266            /* System Exception ****************************/
1267            txPolicy.handleSystemException( e, bean, txContext );
1268        } finally {
1269            txPolicy.afterInvoke( bean, txContext );
1270        }
1271    }
1272
1273    /**
1274     * This method is responsible for loading the bean from the database based
1275     * on the primary key identity contained in the callContext parameter. If
1276     * the primary key is complex (a custom class with one or more fields) the
1277     * key is converted into a Castor JDO Complex identity object which is
1278     * used by the Database.load() method. If the primary key is a single
1279     * field key (usally a primitive wrapper (Integer, Boolean, etc.) or
1280     * String) then the primary key is used by the Database.load() method
1281     * directly.
1282     *
1283     * @param callContext
1284     * @param db
1285     * @return EntityBean
1286     * @exception org.exolab.castor.jdo.PersistenceException
1287     * @exception org.exolab.castor.jdo.ObjectNotFoundException
1288     * @exception org.exolab.castor.jdo.TransactionNotInProgressException
1289     * @exception org.exolab.castor.jdo.LockNotGrantedException
1290     * @exception java.lang.InstantiationException
1291     * @exception java.lang.reflect.InvocationTargetException
1292     * @exception java.lang.IllegalAccessException
1293     */

1294    protected EntityBean JavaDoc fetchAndLoadBean( ThreadContext callContext, Database db )
1295    throws org.exolab.castor.jdo.PersistenceException, org.exolab.castor.jdo.ObjectNotFoundException,
1296    org.exolab.castor.jdo.TransactionNotInProgressException, org.exolab.castor.jdo.LockNotGrantedException,
1297    java.lang.InstantiationException JavaDoc, java.lang.reflect.InvocationTargetException JavaDoc,
1298    java.lang.IllegalAccessException JavaDoc {
1299        /*
1300          Each bean deployment has a unique KeyGenerator that is responsible
1301          for two operations.
1302
1303          1. Convert EJB developer defined complex primary keys to Castor JDO
1304             Complex objects
1305
1306          2. Extract a primary key object from a loaded Entity bean instance.
1307        */

1308        KeyGenerator kg = callContext.getDeploymentInfo().getKeyGenerator();
1309
1310        /*
1311            obtains a bean instance from the method ready pool, or
1312            instantiates a new one calling setEntityContext.
1313            Also places the bean instance in the tx method ready pool.
1314        */

1315        EntityBean JavaDoc bean = null;
1316
1317        /*
1318            Castor JDO doesn't recognize EJB complex primary keys, so if the
1319            key is complex it must be marshalled into a Castor JDO Complex
1320            object in order to perform a load operation.
1321        */

1322        if ( kg.isKeyComplex() ) {
1323            Complex complexIdentity = kg.getJdoComplex( callContext.getPrimaryKey() );
1324            /*
1325             * yip: Castor JDO bases on and maintains one instance of object of
1326             * the same type and identity in each transaction. fetchFreeInstance
1327             * didn't take accout of it and always return another instance; passing
1328             * another instance to load the same type and identity as an existing
1329             * object will casuses PersistenceException to be thrown. It's why "bean"
1330             * is commented out.
1331             */

1332            bean = ( EntityBean JavaDoc ) db.load( callContext.getDeploymentInfo().getBeanClass(),
1333                                           complexIdentity/*,
1334                                       bean*/
);
1335
1336        } else {
1337            bean = ( EntityBean JavaDoc ) db.load( callContext.getDeploymentInfo().getBeanClass(),
1338                                           callContext.getPrimaryKey()/*,
1339                                       bean*/
);
1340        }
1341
1342        return bean;
1343    }
1344
1345    /**
1346     * If their is no transaction the CastorTransactionScopeManager.begin()
1347     * method would have set the unspecified value of the ThreadContext to a
1348     * non-transaction managed database object.
1349     *
1350     * Otherwise if their is a transction contrext, the unspecified value
1351     * will be null.
1352     *
1353     * This allows us to know when an operation (createEJBObject,
1354     * removeEJBObject, busienssMethod) requires transaction-managed Database
1355     * object or a non-transaction managed database object.
1356     *
1357     * @param callContext
1358     * @return Database
1359     * @exception org.exolab.castor.jdo.DatabaseNotFoundException
1360     * @exception org.exolab.castor.jdo.PersistenceException
1361     * @exception javax.transaction.SystemException
1362     */

1363    protected Database getDatabase( ThreadContext callContext )
1364    throws org.exolab.castor.jdo.DatabaseNotFoundException,
1365    org.exolab.castor.jdo.PersistenceException,
1366    javax.transaction.SystemException JavaDoc {
1367        /*
1368         If their is no transaction the CastorTransactionScopeManager.begin()
1369         method would have set the unspecified value of the ThreadContext to a
1370         non-transaction managed database object.
1371
1372         Otherwise if their is a transction context, the unspecified value
1373         will be null.
1374
1375         This allows us to know when an operation (createEJBObject,
1376         removeEJBObject, busienssMethod) requires transaction-managed
1377         Database object or a non-transaction managed database object.
1378         */

1379        Database db = ( Database ) callContext.getUnspecified();
1380
1381        if ( db != null ) {
1382            return db;
1383        } else {
1384            /*
1385             BIG PROBLEM: Transacitons should use the same Database object.
1386             If Thomas won't put this into JDO then I'll have to put into the
1387             container.
1388
1389             1. Check thread to see if current transacion is mapped to any
1390                existing Database object.
1391
1392             2. If it is, return that Database object.
1393
1394             3. If not obtain new Database object
1395
1396             4. Register the Tranaction and Database object in a hashmap keyed
1397                by tx.
1398
1399             5. When transaction completes, remove tx-to-database mapping from
1400                hashmap.
1401
1402             */

1403            return jdo_ForGlobalTransaction.getDatabase();
1404        }
1405    }
1406
1407    /**
1408     Section 9.2.4 EJB 1.1:
1409     "The Container must ensure that the values of the container-managed fields are set to the Java language
1410     defaults (e.g. 0 for integer, null for pointers) prior to invoking an ejbCreate(...) method on an
1411     instance."
1412     */

1413    protected void resetBeanFields( java.lang.Object JavaDoc bean, org.openejb.core.DeploymentInfo info ) {
1414        final String JavaDoc[] cmFields = info.getCmrFields();
1415        final Class JavaDoc beanClass = bean.getClass();
1416
1417        try {
1418            for ( int i = 0; i < cmFields.length; i++ ) {
1419                Field JavaDoc field = beanClass.getDeclaredField( cmFields[i] );
1420                Object JavaDoc value = resetMap.get( field.getType() );
1421// System.out.println("Setting field "+cmFields[i]+" to "+value);
1422
field.set( bean, value );
1423            }
1424        } catch ( Exception JavaDoc e ) {
1425            // NoSuchFieldException or IllegalAccessException
1426
// internal inconistency. This should have been handled at start time.
1427
logger.error( "Internal inconsistency accessing the fields of a CMP entity bean" + bean + ":" + e );
1428        }
1429    }
1430
1431    /******************************************************************************
1432     * *
1433     * CallbackInterceptor methods *
1434     * *
1435     ******************************************************************************/

1436
1437    /**
1438     * Called to indicate that an object needs to be instatiated.
1439     * <p>
1440     * The parameters are ignored. Data is obtained from the deployment info
1441     * which has been obtained, in turn, from the current call context.
1442     *
1443     * @return an instance of the object needs to be instatiated
1444     * @param className The name of the class of the object to be created
1445     * @param loader The class loader to use when creating the object
1446     */

1447    public Object JavaDoc newInstance( String JavaDoc className, ClassLoader JavaDoc loader ) {
1448
1449        Object JavaDoc obj = null;
1450
1451        try {
1452            obj = fetchFreeInstance( ThreadContext.getThreadContext() );
1453        } catch ( IllegalAccessException JavaDoc iae ) {
1454            throw new RuntimeException JavaDoc( iae.getLocalizedMessage() );
1455        } catch ( InvocationTargetException JavaDoc ite ) {
1456            throw new RuntimeException JavaDoc( ite.getLocalizedMessage() );
1457        } catch ( InstantiationException JavaDoc ie ) {
1458            throw new RuntimeException JavaDoc( ie.getLocalizedMessage() );
1459        }
1460
1461        return obj;
1462    }
1463
1464    /**
1465     * Called to indicate that the object has been loaded from persistent
1466     * storage.
1467     *
1468     * @return null or the extending Class. In the latter case Castor will
1469     * reload the object of the given class with the same identity.
1470     * @param object The object
1471     */

1472    public Class JavaDoc loaded( Object JavaDoc object, short accessMode ) {
1473        return null;
1474    }
1475
1476
1477    /**
1478     * Called to indicate that an object is to be stored in persistent
1479     * storage.
1480     *
1481     * @param object The object
1482     * @param modified Is the object modified?
1483     */

1484    public void storing( Object JavaDoc object, boolean modified ) {
1485    }
1486
1487    /**
1488     * Called to indicate that an object is to be created in persistent
1489     * storage.
1490     *
1491     * @param object The object
1492     * @param db The database in which this object will be created
1493     */

1494    public void creating( Object JavaDoc object, Database db ) {
1495    }
1496
1497
1498    /**
1499     * Called to indicate that an object has been created.
1500     *
1501     * @param object The object
1502     */

1503    public void created( Object JavaDoc object ) {
1504    }
1505
1506
1507    /**
1508     * Called to indicate that an object is to be deleted.
1509     * <p>
1510     * This method is made at commit time on objects deleted during the
1511     * transaction before setting their fields to null.
1512     *
1513     * @param object The object
1514     */

1515    public void removing( Object JavaDoc object ) {
1516    }
1517
1518
1519    /**
1520     * Called to indicate that an object has been deleted.
1521     * <p>
1522     * This method is called during db.remove().
1523     *
1524     * @param object The object
1525     */

1526    public void removed( Object JavaDoc object ) {
1527    }
1528
1529
1530    /**
1531     * Called to indicate that an object has been made transient.
1532     * <p>
1533     * This method is made at commit or rollback time on all objects
1534     * that were presistent during the life time of the transaction.
1535     *
1536     * @param object The object
1537     * @param committed True if the object has been commited, false
1538     * if rollback or otherwise cancelled
1539     */

1540    public void releasing( Object JavaDoc object, boolean committed ) {
1541        /*
1542        Every time a bean instance is fetched using fetchFreeInstance( ) it is
1543        automatically added to the txReadyPoolMap indexed by the bean instance
1544        with the value being the MethodReadPool.
1545
1546        This allows bean instances to be pooled as this is the Castor JDO only
1547        method that provides any notification that a bean instance is no
1548        longer in use.
1549        */

1550
1551        LinkedListStack stack = ( LinkedListStack ) txReadyPoolMap.remove( object );
1552        if ( stack != null ) stack.push( object );
1553    }
1554
1555
1556    /**
1557     * Called to indicate that an object has been made persistent.
1558     *
1559     * @param object The object
1560     * @param db The database to which this object belongs
1561     */

1562    public void using( Object JavaDoc object, Database db ) {
1563    }
1564
1565
1566    /**
1567     * Called to indicate that an object has been updated at the end of
1568     * a "long" transaction.
1569     *
1570     * @param object The object
1571     */

1572    public void updated( Object JavaDoc object ) {
1573    }
1574
1575    public static class Key {
1576        Object JavaDoc deploymentID, primaryKey;
1577        Transaction JavaDoc transaction;
1578
1579        public Key( Transaction JavaDoc tx, Object JavaDoc depID, Object JavaDoc prKey ) {
1580            transaction = tx;
1581            deploymentID = depID;
1582            primaryKey = prKey;
1583        }
1584
1585        public int hashCode() {
1586            return transaction.hashCode() ^ deploymentID.hashCode() ^ primaryKey.hashCode();
1587        }
1588
1589        public boolean equals( Object JavaDoc other ) {
1590            if ( other != null && other.getClass() == CastorCMP11_EntityContainer.Key.class ) {
1591                Key otherKey = ( Key ) other;
1592                if ( otherKey.transaction.equals( transaction ) && otherKey.deploymentID.equals( deploymentID ) && otherKey.primaryKey.equals(
1593                                                                                                                                             primaryKey ) )
1594                    return true;
1595            }
1596            return false;
1597        }
1598    }
1599
1600    public class SynchronizationWrapper
1601    implements javax.transaction.Synchronization JavaDoc {
1602        EntityBean JavaDoc bean;
1603        Key myIndex;
1604
1605        public SynchronizationWrapper( EntityBean JavaDoc ebean, Key key ) {
1606            bean = ebean;
1607            myIndex = key;
1608        }
1609
1610        public void beforeCompletion() {
1611            try {
1612                bean.ejbStore();
1613            } catch ( Exception JavaDoc re ) {
1614                javax.transaction.TransactionManager JavaDoc txmgr = OpenEJB.getTransactionManager();
1615                try {
1616                    txmgr.setRollbackOnly();
1617                } catch ( javax.transaction.SystemException JavaDoc se ) {
1618                    // log the exception
1619
}
1620
1621            }
1622        }
1623
1624        public void afterCompletion( int status ) {
1625            syncWrappers.remove( myIndex );
1626        }
1627
1628    }
1629    
1630    /*
1631     * (non-Javadoc)
1632     * @see org.exolab.castor.persist.spi.CallbackInterceptor#loaded(java.lang.Object, org.exolab.castor.mapping.AccessMode)
1633     */

1634    public Class JavaDoc loaded(Object JavaDoc loaded, AccessMode mode) throws Exception JavaDoc {
1635        return loaded(loaded, mode.getId());
1636    }
1637
1638}
Popular Tags