KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > cmp > jdbc > bridge > JDBCCMRFieldBridge


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.cmp.jdbc.bridge;
23
24 import java.lang.ref.WeakReference JavaDoc;
25 import java.lang.reflect.Method JavaDoc;
26 import javax.sql.DataSource JavaDoc;
27 import java.sql.PreparedStatement JavaDoc;
28 import java.sql.ResultSet JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Set JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Arrays JavaDoc;
39 import java.security.AccessController JavaDoc;
40 import java.security.PrivilegedAction JavaDoc;
41 import java.security.Principal JavaDoc;
42 import java.rmi.RemoteException JavaDoc;
43 import javax.ejb.EJBException JavaDoc;
44 import javax.ejb.EJBLocalObject JavaDoc;
45 import javax.ejb.EJBLocalHome JavaDoc;
46 import javax.ejb.RemoveException JavaDoc;
47 import javax.ejb.NoSuchObjectLocalException JavaDoc;
48 import javax.transaction.Status JavaDoc;
49 import javax.transaction.Synchronization JavaDoc;
50 import javax.transaction.SystemException JavaDoc;
51 import javax.transaction.Transaction JavaDoc;
52 import javax.transaction.TransactionManager JavaDoc;
53 import javax.transaction.RollbackException JavaDoc;
54
55 import org.jboss.deployment.DeploymentException;
56 import org.jboss.ejb.EntityCache;
57 import org.jboss.ejb.EntityContainer;
58 import org.jboss.ejb.EntityEnterpriseContext;
59 import org.jboss.ejb.LocalProxyFactory;
60 import org.jboss.ejb.plugins.cmp.bridge.EntityBridge;
61 import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
62 import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext;
63 import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager;
64 import org.jboss.ejb.plugins.cmp.jdbc.JDBCType;
65 import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
66 import org.jboss.ejb.plugins.cmp.jdbc.CascadeDeleteStrategy;
67 import org.jboss.ejb.plugins.cmp.jdbc.RelationData;
68 import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
69 import org.jboss.ejb.plugins.cmp.jdbc.JDBCParameterSetter;
70 import org.jboss.ejb.plugins.cmp.jdbc.JDBCResultSetReader;
71 import org.jboss.tm.TransactionLocal;
72 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
73 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
74 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData;
75 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
76 import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
77 import org.jboss.ejb.plugins.lock.Entrancy;
78 import org.jboss.invocation.InvocationType;
79 import org.jboss.logging.Logger;
80 import org.jboss.security.SecurityAssociation;
81
82 /**
83  * JDBCCMRFieldBridge a bean relationship. This class only supports
84  * relationships between entities managed by a JDBCStoreManager in the same
85  * application.
86  * <p/>
87  * Life-cycle:
88  * Tied to the EntityBridge.
89  * <p/>
90  * Multiplicity:
91  * One for each role that entity has.
92  *
93  * @author <a HREF="mailto:dain@daingroup.com">Dain Sundstrom</a>
94  * @author <a HREF="mailto:alex@jboss.org">Alex Loubyansky</a>
95  * @version $Revision: 58402 $
96  */

97 public final class JDBCCMRFieldBridge extends JDBCAbstractCMRFieldBridge
98 {
99    /**
100     * The entity bridge to which this cmr field belongs.
101     */

102    private final JDBCEntityBridge entity;
103    /**
104     * The manager of this entity.
105     */

106    private final JDBCStoreManager manager;
107    /**
108     * Metadata of the relationship role that this field represents.
109     */

110    private final JDBCRelationshipRoleMetaData metadata;
111    /**
112     * The data source used to acess the relation table if relevant.
113     */

114    private DataSource JavaDoc dataSource;
115    /**
116     * The relation table name if relevent.
117     */

118    private String JavaDoc qualifiedTableName;
119    private String JavaDoc tableName;
120    /**
121     * The key fields that this entity maintains in the relation table.
122     */

123    private JDBCCMP2xFieldBridge[] tableKeyFields;
124    /**
125     * JDBCType for the foreign key fields. Basically, this is an ordered
126     * merge of the JDBCType of the foreign key field.
127     */

128    private JDBCType jdbcType;
129    /**
130     * The related entity's container.
131     */

132    private WeakReference JavaDoc relatedContainerRef;
133    /**
134     * The related entity's jdbc store manager
135     */

136    private JDBCStoreManager relatedManager;
137    /**
138     * The related entity.
139     */

140    private JDBCEntityBridge relatedEntity;
141    /**
142     * The related entity's cmr field for this relationship.
143     */

144    private JDBCCMRFieldBridge relatedCMRField;
145    /**
146     * da log.
147     */

148    private final Logger log;
149
150    /**
151     * Foreign key fields of this entity (i.e., related entities pk fields)
152     */

153    private JDBCCMP2xFieldBridge[] foreignKeyFields;
154    /**
155     * Indicates whether all FK fields are mapped to PK fields
156     */

157    private boolean allFKFieldsMappedToPKFields;
158    /**
159     * This map contains related PK fields that are mapped through FK fields to this entity's PK fields
160     */

161    private final Map JavaDoc relatedPKFieldsByMyPKFields = new HashMap JavaDoc();
162    /**
163     * This map contains related PK fields keyed by FK fields
164     */

165    private final Map JavaDoc relatedPKFieldsByMyFKFields = new HashMap JavaDoc();
166    /**
167     * Indicates whether there are foreign key fields mapped to CMP fields
168     */

169    private boolean hasFKFieldsMappedToCMPFields;
170
171    // Map for lists of related PK values keyed by this side's PK values.
172
// The values are put/removed by related entities when its fields representing
173
// foreign key are changed. When entity with this CMR is created, this map is checked
174
// for waiting for it entities. Relationship with waiting entities is established,
175
// removing waiting entities' primary keys from the map.
176
// NOTE: this map is used only for foreign key fields mapped to CMP fields.
177
private final TransactionLocal relatedPKValuesWaitingForMyPK = new TransactionLocal()
178    {
179       protected Object JavaDoc initialValue()
180       {
181          return new HashMap JavaDoc();
182       }
183    };
184
185    /**
186     * FindByPrimaryKey method used to find related instances in case when FK fields mapped to PK fields
187     */

188    private Method JavaDoc relatedFindByPrimaryKey;
189
190    /**
191     * index of the field in the JDBCContext
192     */

193    private final int jdbcContextIndex;
194
195    /**
196     * cascade-delete strategy
197     */

198    private CascadeDeleteStrategy cascadeDeleteStrategy;
199
200    /**
201     * This CMR field and its related CMR field share the same RelationDataManager
202     */

203    private RelationDataManager relationManager;
204
205    /**
206     * Creates a cmr field for the entity based on the metadata.
207     */

208    public JDBCCMRFieldBridge(JDBCEntityBridge entity,
209                              JDBCStoreManager manager,
210                              JDBCRelationshipRoleMetaData metadata)
211       throws DeploymentException
212    {
213       this.entity = entity;
214       this.manager = manager;
215       this.metadata = metadata;
216       this.jdbcContextIndex = ((JDBCEntityBridge) manager.getEntityBridge()).getNextJDBCContextIndex();
217
218       // Creat the log
219
String JavaDoc categoryName = this.getClass().getName() +
220          "." + manager.getMetaData().getName() + ".";
221       if(metadata.getCMRFieldName() != null)
222       {
223          categoryName += metadata.getCMRFieldName();
224       }
225       else
226       {
227          categoryName += metadata.getRelatedRole().getEntity().getName() +
228             "-" + metadata.getRelatedRole().getCMRFieldName();
229       }
230       this.log = Logger.getLogger(categoryName);
231    }
232
233    public RelationDataManager getRelationDataManager()
234    {
235       return relationManager;
236    }
237
238    public void resolveRelationship() throws DeploymentException
239    {
240       //
241
// Set handles to the related entity's container, cache,
242
// manager, and invoker
243
//
244

245       // Related Entity Name
246
String JavaDoc relatedEntityName = metadata.getRelatedRole().getEntity().getName();
247
248       // Related Entity
249
Catalog catalog = (Catalog) manager.getApplicationData("CATALOG");
250       relatedEntity = (JDBCEntityBridge) catalog.getEntityByEJBName(relatedEntityName);
251       if(relatedEntity == null)
252       {
253          throw new DeploymentException("Related entity not found: " +
254             "entity=" +
255             entity.getEntityName() +
256             ", " +
257             "cmrField=" +
258             getFieldName() +
259             ", " +
260             "relatedEntity=" + relatedEntityName);
261       }
262
263       // Related CMR Field
264
JDBCCMRFieldBridge[] cmrFields = (JDBCCMRFieldBridge[]) relatedEntity.getCMRFields();
265       for(int i = 0; i < cmrFields.length; ++i)
266       {
267          JDBCCMRFieldBridge cmrField = cmrFields[i];
268          if(metadata.getRelatedRole() == cmrField.getMetaData())
269          {
270             relatedCMRField = cmrField;
271             break;
272          }
273       }
274
275       // if we didn't find the related CMR field throw an exception
276
// with a detailed message
277
if(relatedCMRField == null)
278       {
279          String JavaDoc message = "Related CMR field not found in " +
280             relatedEntity.getEntityName() + " for relationship from";
281
282          message += entity.getEntityName() + ".";
283          if(getFieldName() != null)
284          {
285             message += getFieldName();
286          }
287          else
288          {
289             message += "<no-field>";
290          }
291
292          message += " to ";
293          message += relatedEntityName + ".";
294          if(metadata.getRelatedRole().getCMRFieldName() != null)
295          {
296             message += metadata.getRelatedRole().getCMRFieldName();
297          }
298          else
299          {
300             message += "<no-field>";
301          }
302
303          throw new DeploymentException(message);
304       }
305
306       // Related Manager
307
relatedManager = (JDBCStoreManager) relatedEntity.getManager();
308
309       // Related Container
310
EntityContainer relatedContainer = relatedManager.getContainer();
311       this.relatedContainerRef = new WeakReference JavaDoc(relatedContainer);
312
313       // related findByPrimaryKey
314
Class JavaDoc homeClass = (relatedContainer.getLocalHomeClass() != null ?
315          relatedContainer.getLocalHomeClass() : relatedContainer.getHomeClass());
316       try
317       {
318          relatedFindByPrimaryKey =
319             homeClass.getMethod("findByPrimaryKey", new Class JavaDoc[]{relatedEntity.getPrimaryKeyClass()});
320       }
321       catch(Exception JavaDoc e)
322       {
323          throw new DeploymentException("findByPrimaryKey(" +
324             relatedEntity.getPrimaryKeyClass().getName()
325             + " pk) was not found in " + homeClass.getName());
326       }
327
328       //
329
// Initialize the key fields
330
//
331
if(metadata.getRelationMetaData().isTableMappingStyle())
332       {
333          // initialize relation table key fields
334
Collection JavaDoc tableKeys = metadata.getKeyFields();
335          List JavaDoc keyFieldsList = new ArrayList JavaDoc(tableKeys.size());
336
337          // first phase is to create fk fields
338
Map JavaDoc pkFieldsToFKFields = new HashMap JavaDoc(tableKeys.size());
339          for(Iterator JavaDoc i = tableKeys.iterator(); i.hasNext();)
340          {
341             JDBCCMPFieldMetaData cmpFieldMetaData = (JDBCCMPFieldMetaData) i.next();
342             FieldBridge pkField = entity.getFieldByName(cmpFieldMetaData.getFieldName());
343             if(pkField == null)
344             {
345                throw new DeploymentException("Primary key not found for key-field " + cmpFieldMetaData.getFieldName());
346             }
347             pkFieldsToFKFields.put(pkField, new JDBCCMP2xFieldBridge(manager, cmpFieldMetaData));
348          }
349          // second step is to order fk fields to match the order of pk fields
350
JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
351          for(int i = 0; i < pkFields.length; ++i)
352          {
353             Object JavaDoc fkField = pkFieldsToFKFields.get(pkFields[i]);
354             if(fkField == null)
355             {
356                throw new DeploymentException("Primary key " + pkFields[i].getFieldName() + " is not mapped.");
357             }
358             keyFieldsList.add(fkField);
359          }
360          tableKeyFields = (JDBCCMP2xFieldBridge[]) keyFieldsList.toArray(
361             new JDBCCMP2xFieldBridge[keyFieldsList.size()]);
362
363          dataSource = metadata.getRelationMetaData().getDataSource();
364       }
365       else
366       {
367          initializeForeignKeyFields();
368          dataSource = hasForeignKey() ? entity.getDataSource() : relatedEntity.getDataSource();
369       }
370
371       // Fix table name
372
//
373
// This code doesn't work here... The problem each side will generate
374
// the table name and this will only work for simple generation.
375
qualifiedTableName = SQLUtil.fixTableName(metadata.getRelationMetaData().getDefaultTableName(), dataSource);
376       tableName = SQLUtil.getTableNameWithoutSchema(qualifiedTableName);
377
378       relationManager = relatedCMRField.initRelationManager(this);
379    }
380
381    /**
382     * The third phase of deployment. The method is called when relationships are already resolved.
383     *
384     * @throws DeploymentException
385     */

386    public void start() throws DeploymentException
387    {
388       cascadeDeleteStrategy = CascadeDeleteStrategy.getCascadeDeleteStrategy(this);
389    }
390
391    public boolean removeFromRelations(EntityEnterpriseContext ctx, Object JavaDoc[] oldRelationsRef)
392    {
393       load(ctx);
394
395       FieldState fieldState = getFieldState(ctx);
396       List JavaDoc value = fieldState.getValue();
397
398       boolean removed = false;
399       if(!value.isEmpty())
400       {
401          if(hasFKFieldsMappedToCMPFields)
402          {
403             if(isForeignKeyValid(value.get(0)))
404             {
405                cascadeDeleteStrategy.removedIds(ctx, oldRelationsRef, value);
406                removed = true;
407             }
408          }
409          else
410          {
411             cascadeDeleteStrategy.removedIds(ctx, oldRelationsRef, value);
412             removed = true;
413          }
414       }
415       return removed;
416    }
417
418    public void cascadeDelete(EntityEnterpriseContext ctx, List JavaDoc oldValues)
419       throws RemoveException JavaDoc, RemoteException JavaDoc
420    {
421       cascadeDeleteStrategy.cascadeDelete(ctx, oldValues);
422    }
423
424    public boolean isBatchCascadeDelete()
425    {
426       return (cascadeDeleteStrategy instanceof CascadeDeleteStrategy.BatchCascadeDeleteStrategy);
427    }
428
429    /**
430     * Gets the manager of this entity.
431     */

432    public JDBCStoreManager getJDBCStoreManager()
433    {
434       return manager;
435    }
436
437    /**
438     * Gets bridge for this entity.
439     */

440    public JDBCAbstractEntityBridge getEntity()
441    {
442       return entity;
443    }
444
445    /**
446     * Gets the metadata of the relationship role that this field represents.
447     */

448    public JDBCRelationshipRoleMetaData getMetaData()
449    {
450       return metadata;
451    }
452
453    /**
454     * Gets the relation metadata.
455     */

456    public JDBCRelationMetaData getRelationMetaData()
457    {
458       return metadata.getRelationMetaData();
459    }
460
461    /**
462     * Gets the name of this field.
463     */

464    public String JavaDoc getFieldName()
465    {
466       return metadata.getCMRFieldName();
467    }
468
469    /**
470     * Gets the name of the relation table if relevent.
471     */

472    public String JavaDoc getQualifiedTableName()
473    {
474       return qualifiedTableName;
475    }
476
477    public String JavaDoc getTableName()
478    {
479       return tableName;
480    }
481
482    /**
483     * Gets the datasource of the relation table if relevent.
484     */

485    public DataSource JavaDoc getDataSource()
486    {
487       return dataSource;
488    }
489
490    /**
491     * Gets the read ahead meta data.
492     */

493    public JDBCReadAheadMetaData getReadAhead()
494    {
495       return metadata.getReadAhead();
496    }
497
498    public JDBCType getJDBCType()
499    {
500       return jdbcType;
501    }
502
503    public boolean isPrimaryKeyMember()
504    {
505       return false;
506    }
507
508    /**
509     * Does this cmr field have foreign keys.
510     */

511    public boolean hasForeignKey()
512    {
513       return foreignKeyFields != null;
514    }
515
516    /**
517     * Returns true if all FK fields are mapped to PK fields
518     */

519    public boolean allFkFieldsMappedToPkFields()
520    {
521       return allFKFieldsMappedToPKFields;
522    }
523
524    /**
525     * Is this a collection valued field.
526     */

527    public boolean isCollectionValued()
528    {
529       return metadata.getRelatedRole().isMultiplicityMany();
530    }
531
532    /**
533     * Is this a single valued field.
534     */

535    public boolean isSingleValued()
536    {
537       return metadata.getRelatedRole().isMultiplicityOne();
538    }
539
540    /**
541     * Gets the key fields that this entity maintains in the relation table.
542     */

543    public JDBCFieldBridge[] getTableKeyFields()
544    {
545       return tableKeyFields;
546    }
547
548    /**
549     * Gets the foreign key fields of this entity (i.e., related entities pk fields)
550     */

551    public JDBCFieldBridge[] getForeignKeyFields()
552    {
553       return foreignKeyFields;
554    }
555
556    /**
557     * The related entity's cmr field for this relationship.
558     */

559    public JDBCAbstractCMRFieldBridge getRelatedCMRField()
560    {
561       return relatedCMRField;
562    }
563
564    /**
565     * The related manger.
566     */

567    public JDBCStoreManager getRelatedManager()
568    {
569       return relatedManager;
570    }
571
572    /**
573     * The related entity.
574     */

575    public EntityBridge getRelatedEntity()
576    {
577       return relatedEntity;
578    }
579
580    /**
581     * The related entity.
582     */

583    public JDBCEntityBridge getRelatedJDBCEntity()
584    {
585       return relatedEntity;
586    }
587
588    /**
589     * The related container
590     */

591    private final EntityContainer getRelatedContainer()
592    {
593       return (EntityContainer) relatedContainerRef.get();
594    }
595
596    /**
597     * The related entity's local home interface.
598     */

599    public final Class JavaDoc getRelatedLocalInterface()
600    {
601       return getRelatedContainer().getLocalClass();
602    }
603
604    /**
605     * The related entity's local container invoker.
606     */

607    public final LocalProxyFactory getRelatedInvoker()
608    {
609       return getRelatedContainer().getLocalProxyFactory();
610    }
611
612    /**
613     * @param ctx - entity's context
614     * @return true if entity is loaded, false - otherwise.
615     */

616    public boolean isLoaded(EntityEnterpriseContext ctx)
617    {
618       return getFieldState(ctx).isLoaded;
619    }
620
621    /**
622     * Establishes relationships with related entities waited for passed in context
623     * to be created.
624     *
625     * @param ctx - entity's context.
626     */

627    public void addRelatedPKsWaitedForMe(EntityEnterpriseContext ctx)
628    {
629       final Map JavaDoc relatedPKsMap = getRelatedPKsWaitingForMyPK();
630       synchronized(relatedPKsMap)
631       {
632          List JavaDoc relatedPKsWaitingForMe = (List JavaDoc) relatedPKsMap.get(ctx.getId());
633          if(relatedPKsWaitingForMe != null)
634          {
635             for(Iterator JavaDoc waitingPKsIter = relatedPKsWaitingForMe.iterator(); waitingPKsIter.hasNext();)
636             {
637                Object JavaDoc waitingPK = waitingPKsIter.next();
638                waitingPKsIter.remove();
639                if(isForeignKeyValid(waitingPK))
640                {
641                   createRelationLinks(ctx, waitingPK);
642                }
643             }
644          }
645       }
646    }
647
648    /**
649     * Is this field readonly?
650     */

651    public boolean isReadOnly()
652    {
653       return getRelationMetaData().isReadOnly();
654    }
655
656    /**
657     * Had the read time expired?
658     */

659    public boolean isReadTimedOut(EntityEnterpriseContext ctx)
660    {
661       // if we are read/write then we are always timed out
662
if(!isReadOnly())
663       {
664          return true;
665       }
666
667       // if read-time-out is -1 then we never time out.
668
if(getRelationMetaData().getReadTimeOut() == -1)
669       {
670          return false;
671       }
672
673       long readInterval = System.currentTimeMillis() - getFieldState(ctx).getLastRead();
674       return readInterval > getRelationMetaData().getReadTimeOut();
675    }
676
677    /**
678     * @param ctx - entity's context.
679     * @return the value of this field.
680     */

681    public Object JavaDoc getValue(EntityEnterpriseContext ctx)
682    {
683       // no user checks yet, but this is where they would go
684
return getInstanceValue(ctx);
685    }
686
687    /**
688     * Sets new value.
689     *
690     * @param ctx - entity's context;
691     * @param value - new value.
692     */

693    public void setValue(EntityEnterpriseContext ctx, Object JavaDoc value)
694    {
695       if(isReadOnly())
696       {
697          throw new EJBException JavaDoc("Field is read-only: fieldName=" + getFieldName());
698       }
699
700       if(!JDBCEntityBridge.isEjbCreateDone(ctx))
701       {
702          throw new IllegalStateException JavaDoc("A CMR field cannot be set " +
703             "in ejbCreate; this should be done in the ejbPostCreate " +
704             "method instead [EJB 2.0 Spec. 10.5.2].");
705       }
706
707       if(isCollectionValued() && value == null)
708       {
709          throw new IllegalArgumentException JavaDoc("null cannot be assigned to a " +
710             "collection-valued cmr-field [EJB 2.0 Spec. 10.3.8].");
711       }
712       /*
713       if(allFKFieldsMappedToPKFields)
714       {
715          throw new IllegalStateException(
716             "Can't modify relationship: CMR field "
717             + entity.getEntityName() + "." + getFieldName()
718             + " has foreign key fields mapped to the primary key columns."
719             + " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5].");
720       }
721       */

722
723       setInstanceValue(ctx, value);
724    }
725
726    /**
727     * Gets the value of the cmr field for the instance associated with
728     * the context.
729     */

730    public Object JavaDoc getInstanceValue(EntityEnterpriseContext myCtx)
731    {
732       load(myCtx);
733
734       FieldState fieldState = getFieldState(myCtx);
735       if(isCollectionValued())
736       {
737          return fieldState.getRelationSet();
738       }
739
740       // only return one
741
try
742       {
743          List JavaDoc value = fieldState.getValue();
744          if(!value.isEmpty())
745          {
746             Object JavaDoc fk = value.get(0);
747             return getRelatedEntityByFK(fk);
748          }
749          else if(foreignKeyFields != null)
750          {
751             // for those completely mapped to CMP fields and created in this current tx !!!
752
Object JavaDoc relatedId = getRelatedIdFromContext(myCtx);
753             if(relatedId != null)
754             {
755                return getRelatedEntityByFK(relatedId);
756             }
757          }
758          return null;
759       }
760       catch(EJBException JavaDoc e)
761       {
762          throw e;
763       }
764       catch(Exception JavaDoc e)
765       {
766          throw new EJBException JavaDoc(e);
767       }
768    }
769
770    /**
771     * Returns related entity's local interface.
772     * If there are foreign key fields mapped to CMP fields, existence of related entity is checked
773     * with findByPrimaryKey and if, in this case, related instance is not found, null is returned.
774     * If foreign key fields mapped to its own columns then existence of related entity is not checked
775     * and just its local object is returned.
776     *
777     * @param fk - foreign key value.
778     * @return related local object instance.
779     */

780    public EJBLocalObject JavaDoc getRelatedEntityByFK(Object JavaDoc fk)
781    {
782       EJBLocalObject JavaDoc relatedLocalObject = null;
783       final EntityContainer relatedContainer = getRelatedContainer();
784
785       if(hasFKFieldsMappedToCMPFields
786          && relatedManager.getReadAheadCache().getPreloadDataMap(fk, false) == null // not in preload cache
787
)
788       {
789          EJBLocalHome JavaDoc relatedHome = relatedContainer.getLocalProxyFactory().getEJBLocalHome();
790          try
791          {
792             relatedLocalObject = (EJBLocalObject JavaDoc) relatedFindByPrimaryKey.invoke(relatedHome, new Object JavaDoc[]{fk});
793          }
794          catch(Exception JavaDoc ignore)
795          {
796             // no such entity. it is ok to ignore
797
}
798       }
799       else
800       {
801          relatedLocalObject = relatedContainer.getLocalProxyFactory().getEntityEJBLocalObject(fk);
802       }
803
804       return relatedLocalObject;
805    }
806
807    /**
808     * This method is called only for CMR fields with foreign key fields mapped to CMP fields
809     * to check the validity of the foreign key value.
810     *
811     * @param fk the foreign key to check
812     * @return true if there is related entity with the equal primary key
813     */

814    public boolean isForeignKeyValid(Object JavaDoc fk)
815    {
816       boolean valid;
817       if(relatedManager.getReadAheadCache().getPreloadDataMap(fk, false) != null)
818       {
819          valid = true;
820       }
821       else
822       {
823          EJBLocalHome JavaDoc relatedHome = getRelatedContainer().getLocalProxyFactory().getEJBLocalHome();
824          try
825          {
826             relatedFindByPrimaryKey.invoke(relatedHome, new Object JavaDoc[]{fk});
827             valid = true;
828          }
829          catch(Exception JavaDoc ignore)
830          {
831             // no such entity. it is ok to ignore
832
valid = false;
833          }
834       }
835       return valid;
836    }
837
838    /**
839     * Sets the value of the cmr field for the instance associated with
840     * the context.
841     */

842    public void setInstanceValue(EntityEnterpriseContext myCtx, Object JavaDoc newValue)
843    {
844       // validate new value first
845
List JavaDoc newPks;
846       if(newValue instanceof Collection JavaDoc)
847       {
848          Collection JavaDoc col = (Collection JavaDoc) newValue;
849          if(!col.isEmpty())
850          {
851             newPks = new ArrayList JavaDoc(col.size());
852             for(Iterator JavaDoc iter = col.iterator(); iter.hasNext();)
853             {
854                Object JavaDoc localObject = iter.next();
855                if(localObject != null)
856                {
857                   Object JavaDoc relatedId = getRelatedPrimaryKey(localObject);
858
859                   // check whether new value modifies the primary key if there are FK fields mapped to PK fields
860
if(relatedPKFieldsByMyPKFields.size() > 0)
861                   {
862                      checkSetForeignKey(myCtx, relatedId);
863                   }
864
865                   newPks.add(relatedId);
866                }
867             }
868          }
869          else
870          {
871             newPks = Collections.EMPTY_LIST;
872          }
873       }
874       else
875       {
876          if(newValue != null)
877          {
878             newPks = Collections.singletonList(getRelatedPrimaryKey(newValue));
879          }
880          else
881          {
882             newPks = Collections.EMPTY_LIST;
883          }
884       }
885
886       // load the current value
887
load(myCtx);
888       FieldState fieldState = getFieldState(myCtx);
889
890       // is this just setting our own relation set back
891
if(newValue == fieldState.getRelationSet())
892       {
893          return;
894       }
895
896       try
897       {
898          // Remove old value(s)
899
List JavaDoc value = fieldState.getValue();
900          if(!value.isEmpty())
901          {
902             Object JavaDoc[] curPks = value.toArray(new Object JavaDoc[value.size()]);
903             for(int i = 0; i < curPks.length; ++i)
904             {
905                destroyRelationLinks(myCtx, curPks[i]);
906             }
907          }
908
909          // Add new value(s)
910
for(int i = 0; i < newPks.size(); ++i)
911          {
912             createRelationLinks(myCtx, newPks.get(i));
913          }
914       }
915       catch(RuntimeException JavaDoc e)
916       {
917          throw e;
918       }
919       catch(Exception JavaDoc e)
920       {
921          throw new EJBException JavaDoc(e);
922       }
923    }
924
925    /**
926     * Checks whether new foreign key value conflicts with primary key value
927     * in case of foreign key to primary key mapping.
928     *
929     * @param myCtx - entity's context;
930     * @param newValue - new foreign key value.
931     * @throws IllegalStateException - if new foreign key value changes
932     * primary key value, otherwise returns silently.
933     */

934    private void checkSetForeignKey(EntityEnterpriseContext myCtx, Object JavaDoc newValue)
935       throws IllegalStateException JavaDoc
936    {
937       JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
938       for(int i = 0; i < pkFields.length; ++i)
939       {
940          JDBCCMP2xFieldBridge pkField = (JDBCCMP2xFieldBridge) pkFields[i];
941          JDBCCMP2xFieldBridge relatedPkField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyPKFields.get(pkField);
942          if(relatedPkField != null)
943          {
944             Object JavaDoc comingValue = relatedPkField.getPrimaryKeyValue(newValue);
945             Object JavaDoc currentValue = pkField.getInstanceValue(myCtx);
946
947             // they shouldn't be null
948
if(!comingValue.equals(currentValue))
949             {
950                throw new IllegalStateException JavaDoc("Can't create relationship: CMR field "
951                   +
952                   entity.getEntityName() +
953                   "." +
954                   getFieldName()
955                   +
956                   " has foreign key fields mapped to the primary key columns."
957                   +
958                   " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5]."
959                   +
960                   " primary key value is " +
961                   currentValue
962                   + " overriding value is " + comingValue);
963             }
964          }
965       }
966    }
967
968    /**
969     * Creates the relation links between the instance associated with the
970     * context and the related instance (just the id is passed in).
971     * <p/>
972     * This method calls a.addRelation(b) and b.addRelation(a)
973     */

974    public void createRelationLinks(EntityEnterpriseContext myCtx, Object JavaDoc relatedId)
975    {
976       createRelationLinks(myCtx, relatedId, true);
977    }
978
979    public void createRelationLinks(EntityEnterpriseContext myCtx, Object JavaDoc relatedId, boolean updateForeignKey)
980    {
981       if(isReadOnly())
982       {
983          throw new EJBException JavaDoc("Field is read-only: " + getFieldName());
984       }
985
986       // If my multiplicity is one, then we need to free the new related context
987
// from its old relationship.
988
Transaction JavaDoc tx = getTransaction();
989       if(metadata.isMultiplicityOne())
990       {
991          Object JavaDoc oldRelatedId = relatedCMRField.invokeGetRelatedId(tx, relatedId);
992          if(oldRelatedId != null)
993          {
994             invokeRemoveRelation(tx, oldRelatedId, relatedId);
995             relatedCMRField.invokeRemoveRelation(tx, relatedId, oldRelatedId);
996          }
997       }
998
999       addRelation(myCtx, relatedId, updateForeignKey);
1000      relatedCMRField.invokeAddRelation(tx, relatedId, myCtx.getId());
1001   }
1002
1003   /**
1004    * Destroys the relation links between the instance associated with the
1005    * context and the related instance (just the id is passed in).
1006    * <p/>
1007    * This method calls a.removeRelation(b) and b.removeRelation(a)
1008    */

1009   public void destroyRelationLinks(EntityEnterpriseContext myCtx, Object JavaDoc relatedId)
1010   {
1011      destroyRelationLinks(myCtx, relatedId, true);
1012   }
1013
1014   /**
1015    * Destroys the relation links between the instance associated with the
1016    * context and the related instance (just the id is passed in).
1017    * <p/>
1018    * This method calls a.removeRelation(b) and b.removeRelation(a)
1019    * <p/>
1020    * If updateValueCollection is false, the related id collection is not
1021    * updated. This form is only used by the RelationSet iterator.
1022    */

1023   public void destroyRelationLinks(EntityEnterpriseContext myCtx,
1024                                    Object JavaDoc relatedId,
1025                                    boolean updateValueCollection)
1026   {
1027      destroyRelationLinks(myCtx, relatedId, updateValueCollection, true);
1028   }
1029
1030   public void destroyRelationLinks(EntityEnterpriseContext myCtx,
1031                                    Object JavaDoc relatedId,
1032                                    boolean updateValueCollection,
1033                                    boolean updateForeignKey)
1034   {
1035      if(isReadOnly())
1036      {
1037         throw new EJBException JavaDoc("Field is read-only: " + getFieldName());
1038      }
1039
1040      removeRelation(myCtx, relatedId, updateValueCollection, updateForeignKey);
1041      relatedCMRField.invokeRemoveRelation(getTransaction(), relatedId, myCtx.getId());
1042   }
1043
1044   /**
1045    * Schedules children for cascade delete.
1046    */

1047   public void scheduleChildrenForCascadeDelete(EntityEnterpriseContext ctx)
1048   {
1049      load(ctx);
1050      FieldState fieldState = getFieldState(ctx);
1051      List JavaDoc value = fieldState.getValue();
1052      if(!value.isEmpty())
1053      {
1054         Transaction JavaDoc tx = getTransaction();
1055         for(int i = 0; i < value.size(); ++i)
1056         {
1057            relatedCMRField.invokeScheduleForCascadeDelete(tx, value.get(i));
1058         }
1059      }
1060   }
1061
1062   /**
1063    * Schedules children for batch cascade delete.
1064    */

1065   public void scheduleChildrenForBatchCascadeDelete(EntityEnterpriseContext ctx)
1066   {
1067      load(ctx);
1068      FieldState fieldState = getFieldState(ctx);
1069      List JavaDoc value = fieldState.getValue();
1070      if(!value.isEmpty())
1071      {
1072         Transaction JavaDoc tx = getTransaction();
1073         for(int i = 0; i < value.size(); ++i)
1074         {
1075            relatedCMRField.invokeScheduleForBatchCascadeDelete(tx, value.get(i));
1076         }
1077      }
1078   }
1079
1080   /**
1081    * Schedules the instance with myId for cascade delete.
1082    */

1083   private Object JavaDoc invokeScheduleForCascadeDelete(Transaction JavaDoc tx, Object JavaDoc myId)
1084   {
1085      try
1086      {
1087         EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache();
1088         SecurityActions actions = SecurityActions.UTIL.getSecurityActions();
1089
1090         CMRInvocation invocation = new CMRInvocation();
1091         invocation.setCmrMessage(CMRMessage.SCHEDULE_FOR_CASCADE_DELETE);
1092         invocation.setEntrancy(Entrancy.NON_ENTRANT);
1093         invocation.setId(instanceCache.createCacheKey(myId));
1094         invocation.setArguments(new Object JavaDoc[]{this});
1095         invocation.setTransaction(tx);
1096         invocation.setPrincipal(actions.getPrincipal());
1097         invocation.setCredential(actions.getCredential());
1098         invocation.setType(InvocationType.LOCAL);
1099         return manager.getContainer().invoke(invocation);
1100      }
1101      catch(EJBException JavaDoc e)
1102      {
1103         throw e;
1104      }
1105      catch(Exception JavaDoc e)
1106      {
1107         throw new EJBException JavaDoc("Error in scheduleForCascadeDelete()", e);
1108      }
1109   }
1110
1111   /**
1112    * Schedules the instance with myId for batch cascade delete.
1113    */

1114   private Object JavaDoc invokeScheduleForBatchCascadeDelete(Transaction JavaDoc tx, Object JavaDoc myId)
1115   {
1116      try
1117      {
1118         EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache();
1119         SecurityActions actions = SecurityActions.UTIL.getSecurityActions();
1120
1121         CMRInvocation invocation = new CMRInvocation();
1122         invocation.setCmrMessage(CMRMessage.SCHEDULE_FOR_BATCH_CASCADE_DELETE);
1123         invocation.setEntrancy(Entrancy.NON_ENTRANT);
1124         invocation.setId(instanceCache.createCacheKey(myId));
1125         invocation.setArguments(new Object JavaDoc[]{this});
1126         invocation.setTransaction(tx);
1127         invocation.setPrincipal(actions.getPrincipal());
1128         invocation.setCredential(actions.getCredential());
1129         invocation.setType(InvocationType.LOCAL);
1130         return manager.getContainer().invoke(invocation);
1131      }
1132      catch(EJBException JavaDoc e)
1133      {
1134         throw e;
1135      }
1136      catch(Exception JavaDoc e)
1137      {
1138         throw new EJBException JavaDoc("Error in scheduleForBatchCascadeDelete()", e);
1139      }
1140   }
1141
1142   /**
1143    * Invokes the getRelatedId on the related CMR field via the container
1144    * invocation interceptor chain.
1145    */

1146   private Object JavaDoc invokeGetRelatedId(Transaction JavaDoc tx, Object JavaDoc myId)
1147   {
1148      try
1149      {
1150         EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache();
1151         SecurityActions actions = SecurityActions.UTIL.getSecurityActions();
1152
1153         CMRInvocation invocation = new CMRInvocation();
1154         invocation.setCmrMessage(CMRMessage.GET_RELATED_ID);
1155         invocation.setEntrancy(Entrancy.NON_ENTRANT);
1156         invocation.setId(instanceCache.createCacheKey(myId));
1157         invocation.setArguments(new Object JavaDoc[]{this});
1158         invocation.setTransaction(tx);
1159         invocation.setPrincipal(actions.getPrincipal());
1160         invocation.setCredential(actions.getCredential());
1161         invocation.setType(InvocationType.LOCAL);
1162         return manager.getContainer().invoke(invocation);
1163      }
1164      catch(EJBException JavaDoc e)
1165      {
1166         throw e;
1167      }
1168      catch(Exception JavaDoc e)
1169      {
1170         throw new EJBException JavaDoc("Error in getRelatedId", e);
1171      }
1172   }
1173
1174   /**
1175    * Invokes the addRelation on the related CMR field via the container
1176    * invocation interceptor chain.
1177    */

1178   private void invokeAddRelation(Transaction JavaDoc tx, Object JavaDoc myId, Object JavaDoc relatedId)
1179   {
1180      try
1181      {
1182         EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache();
1183         SecurityActions actions = SecurityActions.UTIL.getSecurityActions();
1184
1185         CMRInvocation invocation = new CMRInvocation();
1186         invocation.setCmrMessage(CMRMessage.ADD_RELATION);
1187         invocation.setEntrancy(Entrancy.NON_ENTRANT);
1188         invocation.setId(instanceCache.createCacheKey(myId));
1189         invocation.setArguments(new Object JavaDoc[]{this, relatedId});
1190         invocation.setTransaction(tx);
1191         invocation.setPrincipal(actions.getPrincipal());
1192         invocation.setCredential(actions.getCredential());
1193         invocation.setType(InvocationType.LOCAL);
1194         manager.getContainer().invoke(invocation);
1195      }
1196      catch(EJBException JavaDoc e)
1197      {
1198         throw e;
1199      }
1200      catch(Exception JavaDoc e)
1201      {
1202         throw new EJBException JavaDoc("Error in addRelation", e);
1203      }
1204   }
1205
1206   /**
1207    * Invokes the removeRelation on the related CMR field via the container
1208    * invocation interceptor chain.
1209    */

1210   private void invokeRemoveRelation(Transaction JavaDoc tx, Object JavaDoc myId, Object JavaDoc relatedId)
1211   {
1212      try
1213      {
1214         EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache();
1215         SecurityActions actions = SecurityActions.UTIL.getSecurityActions();
1216
1217         CMRInvocation invocation = new CMRInvocation();
1218         invocation.setCmrMessage(CMRMessage.REMOVE_RELATION);
1219         invocation.setEntrancy(Entrancy.NON_ENTRANT);
1220         invocation.setId(instanceCache.createCacheKey(myId));
1221         invocation.setArguments(new Object JavaDoc[]{this, relatedId});
1222         invocation.setTransaction(tx);
1223         invocation.setPrincipal(actions.getPrincipal());
1224         invocation.setCredential(actions.getCredential());
1225         invocation.setType(InvocationType.LOCAL);
1226         manager.getContainer().invoke(invocation);
1227      }
1228      catch(EJBException JavaDoc e)
1229      {
1230         throw e;
1231      }
1232      catch(Exception JavaDoc e)
1233      {
1234         throw new EJBException JavaDoc("Error in removeRelation", e);
1235      }
1236   }
1237
1238   /**
1239    * Get the related entity's id. This only works on single valued cmr fields.
1240    */

1241   public Object JavaDoc getRelatedId(EntityEnterpriseContext myCtx)
1242   {
1243      if(isCollectionValued())
1244      {
1245         throw new EJBException JavaDoc("getRelatedId may only be called on a cmr-field with a multiplicity of one.");
1246      }
1247
1248      load(myCtx);
1249      List JavaDoc value = getFieldState(myCtx).getValue();
1250      return value.isEmpty() ? null : value.get(0);
1251   }
1252
1253   /**
1254    * Creates a new instance of related id based on foreign key value in the context.
1255    *
1256    * @param ctx - entity's context.
1257    * @return related entity's id.
1258    */

1259   public Object JavaDoc getRelatedIdFromContext(EntityEnterpriseContext ctx)
1260   {
1261      Object JavaDoc relatedId = null;
1262      Object JavaDoc fkFieldValue;
1263      for(int i = 0; i < foreignKeyFields.length; ++i)
1264      {
1265         JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1266         fkFieldValue = fkField.getInstanceValue(ctx);
1267         if(fkFieldValue == null)
1268         {
1269            return null;
1270         }
1271         JDBCCMP2xFieldBridge relatedPKField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyFKFields.get(fkField);
1272         relatedId = relatedPKField.setPrimaryKeyValue(relatedId, fkFieldValue);
1273      }
1274      return relatedId;
1275   }
1276
1277   /**
1278    * Adds the foreign key to the set of related ids, and updates any foreign key fields.
1279    */

1280   public void addRelation(EntityEnterpriseContext myCtx, Object JavaDoc fk)
1281   {
1282      addRelation(myCtx, fk, true);
1283      relationManager.addRelation(this, myCtx.getId(), relatedCMRField, fk);
1284   }
1285
1286   private void addRelation(EntityEnterpriseContext myCtx, Object JavaDoc fk, boolean updateForeignKey)
1287   {
1288      checkSetForeignKey(myCtx, fk);
1289
1290      if(isReadOnly())
1291      {
1292         throw new EJBException JavaDoc("Field is read-only: " + getFieldName());
1293      }
1294
1295      if(!JDBCEntityBridge.isEjbCreateDone(myCtx))
1296      {
1297         throw new IllegalStateException JavaDoc("A CMR field cannot be set or added " +
1298            "to a relationship in ejbCreate; this should be done in the " +
1299            "ejbPostCreate method instead [EJB 2.0 Spec. 10.5.2].");
1300      }
1301
1302      // add to current related set
1303
FieldState myState = getFieldState(myCtx);
1304      myState.addRelation(fk);
1305
1306      // set the foreign key, if we have one.
1307
if(hasForeignKey() && updateForeignKey)
1308      {
1309         setForeignKey(myCtx, fk);
1310      }
1311   }
1312
1313   /**
1314    * Removes the foreign key to the set of related ids, and updates any foreign key fields.
1315    */

1316   public void removeRelation(EntityEnterpriseContext myCtx, Object JavaDoc fk)
1317   {
1318      removeRelation(myCtx, fk, true, true);
1319      relationManager.removeRelation(this, myCtx.getId(), relatedCMRField, fk);
1320   }
1321
1322   private void removeRelation(EntityEnterpriseContext myCtx,
1323                               Object JavaDoc fk,
1324                               boolean updateValueCollection,
1325                               boolean updateForeignKey)
1326   {
1327      if(isReadOnly())
1328      {
1329         throw new EJBException JavaDoc("Field is read-only: " + getFieldName());
1330      }
1331
1332      // remove from current related set
1333
if(updateValueCollection)
1334      {
1335         FieldState myState = getFieldState(myCtx);
1336         myState.removeRelation(fk);
1337      }
1338
1339      // set the foreign key to null, if we have one.
1340
if(hasForeignKey() && updateForeignKey)
1341      {
1342         setForeignKey(myCtx, null);
1343      }
1344   }
1345
1346   /**
1347    * loads the collection of related ids
1348    * NOTE: after loading, the field might not be in a clean state as we support adding and removing
1349    * relations while the field is not loaded. The actual value of the field will be the value loaded
1350    * plus added relations and minus removed relations while the field was not loaded.
1351    */

1352   private void load(EntityEnterpriseContext myCtx)
1353   {
1354      // if we are already loaded we're done
1355
FieldState fieldState = getFieldState(myCtx);
1356      if(fieldState.isLoaded())
1357      {
1358         return;
1359      }
1360
1361      // check the preload cache
1362
if(log.isTraceEnabled())
1363      {
1364         log.trace("Read ahead cahce load: cmrField=" + getFieldName() + " pk=" + myCtx.getId());
1365      }
1366
1367      manager.getReadAheadCache().load(myCtx);
1368      if(fieldState.isLoaded())
1369      {
1370         return;
1371      }
1372
1373      // load the value from the database
1374
Collection JavaDoc values;
1375      if(hasForeignKey())
1376      {
1377         // WARN: this method will load foreign keys if they are not yet loaded and
1378
// changes relationship lazy loading in advanced training labs.
1379
// i.e. it will load lazy cmp fields first of this entity and then will lazy load the related entity
1380
// instead of loading this entity JOIN related entity in one query.
1381
//Object fk = getRelatedIdFromContext(myCtx);
1382

1383         boolean loadWithManager = false;
1384         Object JavaDoc fk = null;
1385         for(int i = 0; i < foreignKeyFields.length; ++i)
1386         {
1387            JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1388            // if the field is not loaded then load relationship with manager
1389
if(!fkField.isLoaded(myCtx))
1390            {
1391               loadWithManager = true;
1392               break;
1393            }
1394
1395            Object JavaDoc fkFieldValue = fkField.getInstanceValue(myCtx);
1396            // if one of the fk is null, the whole fk is considered to be null
1397
if(fkFieldValue == null)
1398            {
1399               fk = null;
1400               break;
1401            }
1402            JDBCCMP2xFieldBridge relatedPKField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyFKFields.get(fkField);
1403            fk = relatedPKField.setPrimaryKeyValue(fk, fkFieldValue);
1404         }
1405
1406         if(loadWithManager)
1407         {
1408            values = manager.loadRelation(this, myCtx.getId());
1409         }
1410         else
1411         {
1412            values = (fk == null ? Collections.EMPTY_LIST : Collections.singletonList(fk));
1413         }
1414      }
1415      else
1416      {
1417         values = manager.loadRelation(this, myCtx.getId());
1418      }
1419      load(myCtx, values);
1420   }
1421
1422   public void load(EntityEnterpriseContext myCtx, Collection JavaDoc values)
1423   {
1424      // did we get more then one value for a single valued field
1425
if(isSingleValued() && values.size() > 1)
1426      {
1427         throw new EJBException JavaDoc("Data contains multiple values, but this cmr field is single valued: " + values);
1428      }
1429
1430      // add the new values
1431
FieldState fieldState = getFieldState(myCtx);
1432      fieldState.loadRelations(values);
1433
1434      // set the foreign key, if we have one.
1435
if(hasForeignKey())
1436      {
1437         // update the states and locked values of FK fields
1438
if(!values.isEmpty())
1439         {
1440            Object JavaDoc loadedValue = values.iterator().next();
1441            for(int i = 0; i < foreignKeyFields.length; ++i)
1442            {
1443               JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1444               Object JavaDoc fieldValue = fkField.getPrimaryKeyValue(loadedValue);
1445               fkField.updateState(myCtx, fieldValue);
1446            }
1447         }
1448
1449         // set the real FK value
1450
List JavaDoc realValue = fieldState.getValue();
1451         Object JavaDoc fk = realValue.isEmpty() ? null : realValue.get(0);
1452         setForeignKey(myCtx, fk);
1453      }
1454
1455      JDBCEntityBridge.setCreated(myCtx);
1456   }
1457
1458   /**
1459    * Sets the foreign key field value.
1460    */

1461   public void setForeignKey(EntityEnterpriseContext myCtx, Object JavaDoc fk)
1462   {
1463      if(!hasForeignKey())
1464      {
1465         throw new EJBException JavaDoc(getFieldName() + " CMR field does not have a foreign key to set.");
1466      }
1467
1468      for(int i = 0; i < foreignKeyFields.length; ++i)
1469      {
1470         JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1471         Object JavaDoc fieldValue = fkField.getPrimaryKeyValue(fk);
1472         fkField.setInstanceValue(myCtx, fieldValue);
1473      }
1474   }
1475
1476   /**
1477    * Initialized the foreign key fields.
1478    */

1479   public void initInstance(EntityEnterpriseContext ctx)
1480   {
1481      // mark this field as loaded
1482
getFieldState(ctx).loadRelations(Collections.EMPTY_SET);
1483
1484      if(foreignKeyFields == null)
1485      {
1486         return;
1487      }
1488
1489      for(int i = 0; i < foreignKeyFields.length; ++i)
1490      {
1491         JDBCCMP2xFieldBridge foreignKeyField = foreignKeyFields[i];
1492         if(!foreignKeyField.isFKFieldMappedToCMPField())
1493         {
1494            foreignKeyField.setInstanceValue(ctx, null);
1495         }
1496      }
1497   }
1498
1499   /**
1500    * resets the persistence context of the foreign key fields
1501    */

1502   public void resetPersistenceContext(EntityEnterpriseContext ctx)
1503   {
1504      // only resetStats if the read has timed out
1505
if(!isReadTimedOut(ctx))
1506      {
1507         return;
1508      }
1509
1510      // clear the field state
1511
JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
1512      // invalidate current field state
1513
/*
1514      FieldState currentFieldState = (FieldState) jdbcCtx.getFieldState(jdbcContextIndex);
1515      if(currentFieldState != null)
1516         currentFieldState.invalidate();
1517         */

1518      jdbcCtx.setFieldState(jdbcContextIndex, null);
1519
1520      if(foreignKeyFields == null)
1521      {
1522         return;
1523      }
1524
1525      for(int i = 0; i < foreignKeyFields.length; ++i)
1526      {
1527         JDBCCMP2xFieldBridge foreignKeyField = foreignKeyFields[i];
1528         if(!foreignKeyField.isFKFieldMappedToCMPField())
1529         {
1530            foreignKeyField.resetPersistenceContext(ctx);
1531         }
1532      }
1533   }
1534
1535   public int setInstanceParameters(PreparedStatement JavaDoc ps,
1536                                    int parameterIndex,
1537                                    EntityEnterpriseContext ctx)
1538   {
1539      if(foreignKeyFields == null)
1540      {
1541         return parameterIndex;
1542      }
1543
1544      List JavaDoc value = getFieldState(ctx).getValue();
1545      Object JavaDoc fk = (value.isEmpty() ? null : value.get(0));
1546
1547      for(int i = 0; i < foreignKeyFields.length; ++i)
1548      {
1549         parameterIndex = foreignKeyFields[i].setPrimaryKeyParameters(ps, parameterIndex, fk);
1550      }
1551
1552      return parameterIndex;
1553   }
1554
1555   public int loadInstanceResults(ResultSet JavaDoc rs,
1556                                  int parameterIndex,
1557                                  EntityEnterpriseContext ctx)
1558   {
1559      if(!hasForeignKey())
1560      {
1561         return parameterIndex;
1562      }
1563
1564      // load the value from the database
1565
Object JavaDoc[] ref = new Object JavaDoc[1];
1566      parameterIndex = loadArgumentResults(rs, parameterIndex, ref);
1567
1568      // only actually set the value if the state is not already loaded
1569
FieldState fieldState = getFieldState(ctx);
1570      if(!fieldState.isLoaded())
1571      {
1572         if(ref[0] != null)
1573         {
1574            load(ctx, Collections.singleton(ref[0]));
1575         }
1576         else
1577         {
1578            load(ctx, Collections.EMPTY_SET);
1579         }
1580      }
1581      return parameterIndex;
1582   }
1583
1584   public int loadArgumentResults(ResultSet JavaDoc rs, int parameterIndex, Object JavaDoc[] fkRef)
1585   {
1586      if(foreignKeyFields == null)
1587      {
1588         return parameterIndex;
1589      }
1590
1591      boolean fkIsNull = false;
1592
1593      // value of this field, will be filled in below
1594
Object JavaDoc[] argumentRef = new Object JavaDoc[1];
1595      for(int i = 0; i < foreignKeyFields.length; ++i)
1596      {
1597         JDBCCMPFieldBridge field = foreignKeyFields[i];
1598         parameterIndex = field.loadArgumentResults(rs, parameterIndex, argumentRef);
1599
1600         if(fkIsNull)
1601         {
1602            continue;
1603         }
1604         if(field.getPrimaryKeyField() != null)
1605         {
1606            // if there is a null field among FK fields, the whole FK field is considered null.
1607
// NOTE: don't throw exception in this case, it's ok if FK is partly mapped to a PK
1608
// NOTE2: we still need to iterate through foreign key fields and 'load' them to
1609
// return correct parameterIndex.
1610
if(argumentRef[0] == null)
1611            {
1612               fkRef[0] = null;
1613               fkIsNull = true;
1614            }
1615            else
1616            {
1617               // if we don't have a pk object yet create one
1618
if(fkRef[0] == null)
1619               {
1620                  fkRef[0] = relatedEntity.createPrimaryKeyInstance();
1621               }
1622               try
1623               {
1624                  // Set this field's value into the primary key object.
1625
field.getPrimaryKeyField().set(fkRef[0], argumentRef[0]);
1626               }
1627               catch(Exception JavaDoc e)
1628               {
1629                  // Non recoverable internal exception
1630
throw new EJBException JavaDoc("Internal error setting foreign-key field " + getFieldName(), e);
1631               }
1632            }
1633         }
1634         else
1635         {
1636            // This field is the primary key, so no extraction is necessary.
1637
fkRef[0] = argumentRef[0];
1638         }
1639      }
1640      return parameterIndex;
1641   }
1642
1643   /**
1644    * This method is never called.
1645    * In case of a CMR with foreign key fields, only the foreign key fields are asked for the dirty state.
1646    */

1647   public boolean isDirty(EntityEnterpriseContext ctx)
1648   {
1649      return foreignKeyFields == null ? relationManager.isDirty() : false;
1650   }
1651
1652   public boolean invalidateCache(EntityEnterpriseContext ctx)
1653   {
1654      JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
1655      FieldState fieldState = (FieldState) jdbcCtx.getFieldState(jdbcContextIndex);
1656      return fieldState == null ? false : fieldState.isChanged();
1657   }
1658
1659   /**
1660    * This method is never called.
1661    * In case of a CMR
1662    * - with foreign key fields, the foreign key fields are cleaned when necessary according to CMP fields'
1663    * behaviour.
1664    * - from m:m relationship, added/removed key pairs are cleared in application tx data map on sync.
1665    */

1666   public void setClean(EntityEnterpriseContext ctx)
1667   {
1668      throw new UnsupportedOperationException JavaDoc();
1669   }
1670
1671   public boolean isCMPField()
1672   {
1673      return false;
1674   }
1675
1676   public JDBCEntityPersistenceStore getManager()
1677   {
1678      return manager;
1679   }
1680
1681   public boolean hasFKFieldsMappedToCMPFields()
1682   {
1683      return hasFKFieldsMappedToCMPFields;
1684   }
1685
1686   public void addRelatedPKWaitingForMyPK(Object JavaDoc myPK, Object JavaDoc relatedPK)
1687   {
1688      Map JavaDoc relatedPKsWaitingForMyPK = getRelatedPKsWaitingForMyPK();
1689      synchronized(relatedPKsWaitingForMyPK)
1690      {
1691         List JavaDoc relatedPKs = (List JavaDoc) relatedPKsWaitingForMyPK.get(myPK);
1692         if(relatedPKs == null)
1693         {
1694            relatedPKs = new ArrayList JavaDoc(1);
1695            relatedPKsWaitingForMyPK.put(myPK, relatedPKs);
1696         }
1697         relatedPKs.add(relatedPK);
1698      }
1699   }
1700
1701   public void removeRelatedPKWaitingForMyPK(Object JavaDoc myPK, Object JavaDoc relatedPK)
1702   {
1703      final Map JavaDoc relatedPKMap = getRelatedPKsWaitingForMyPK();
1704      synchronized(relatedPKMap)
1705      {
1706         List JavaDoc relatedPKs = (List JavaDoc) relatedPKMap.get(myPK);
1707         if(relatedPKs != null)
1708         {
1709            relatedPKs.remove(relatedPK);
1710         }
1711      }
1712   }
1713
1714   /**
1715    * Gets the field state object from the persistence context.
1716    */

1717   private FieldState getFieldState(EntityEnterpriseContext ctx)
1718   {
1719      JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
1720      FieldState fieldState = (FieldState) jdbcCtx.getFieldState(jdbcContextIndex);
1721      if(fieldState == null)
1722      {
1723         fieldState = new FieldState(ctx);
1724         jdbcCtx.setFieldState(jdbcContextIndex, fieldState);
1725      }
1726      return fieldState;
1727   }
1728
1729   /**
1730    * Initializes foreign key fields
1731    *
1732    * @throws DeploymentException
1733    */

1734   private void initializeForeignKeyFields()
1735      throws DeploymentException
1736   {
1737      Collection JavaDoc foreignKeys = metadata.getRelatedRole().getKeyFields();
1738
1739      // temporary map used later to write fk fields in special order
1740
Map JavaDoc fkFieldsByRelatedPKFields = new HashMap JavaDoc();
1741      for(Iterator JavaDoc i = foreignKeys.iterator(); i.hasNext();)
1742      {
1743         JDBCCMPFieldMetaData fkFieldMetaData = (JDBCCMPFieldMetaData) i.next();
1744         JDBCCMP2xFieldBridge relatedPKField =
1745            (JDBCCMP2xFieldBridge) relatedEntity.getFieldByName(fkFieldMetaData.getFieldName());
1746
1747         // now determine whether the fk is mapped to a pk column
1748
String JavaDoc fkColumnName = fkFieldMetaData.getColumnName();
1749         JDBCCMP2xFieldBridge fkField = null;
1750
1751         // look among the CMP fields for the field with the same column name
1752
JDBCFieldBridge[] tableFields = entity.getTableFields();
1753         for(int tableInd = 0; tableInd < tableFields.length && fkField == null; ++tableInd)
1754         {
1755            JDBCCMP2xFieldBridge cmpField = (JDBCCMP2xFieldBridge) tableFields[tableInd];
1756            if(fkColumnName.equals(cmpField.getColumnName()))
1757            {
1758               hasFKFieldsMappedToCMPFields = true;
1759
1760               // construct the foreign key field
1761
fkField = new JDBCCMP2xFieldBridge((JDBCStoreManager) cmpField.getManager(), // this cmpField's manager
1762
relatedPKField.getFieldName(),
1763                  relatedPKField.getFieldType(),
1764                  cmpField.getJDBCType(), // this cmpField's jdbc type
1765
relatedPKField.isReadOnly(),
1766                  relatedPKField.getReadTimeOut(),
1767                  relatedPKField.getPrimaryKeyClass(),
1768                  relatedPKField.getPrimaryKeyField(),
1769                  cmpField, // CMP field I am mapped to
1770
this,
1771                  fkColumnName);
1772
1773               if(cmpField.isPrimaryKeyMember())
1774               {
1775                  relatedPKFieldsByMyPKFields.put(cmpField, relatedPKField);
1776               }
1777            }
1778         }
1779
1780         // if the fk is not a part of pk then create a new field
1781
if(fkField == null)
1782         {
1783            fkField = new JDBCCMP2xFieldBridge(manager,
1784               fkFieldMetaData,
1785               manager.getJDBCTypeFactory().getJDBCType(fkFieldMetaData));
1786         }
1787
1788         fkFieldsByRelatedPKFields.put(relatedPKField, fkField); // temporary map
1789
relatedPKFieldsByMyFKFields.put(fkField, relatedPKField);
1790      }
1791
1792      // Note: this important to order the foreign key fields so that their order matches
1793
// the order of related entity's pk fields in case of complex primary keys.
1794
// The order is important in fk-constraint generation and in SELECT when loading
1795
if(fkFieldsByRelatedPKFields.size() > 0)
1796      {
1797         JDBCFieldBridge[] relatedPKFields = relatedEntity.getPrimaryKeyFields();
1798         List JavaDoc fkList = new ArrayList JavaDoc(relatedPKFields.length);
1799         for(int i = 0; i < relatedPKFields.length; ++i)
1800         {
1801            JDBCCMPFieldBridge fkField = (JDBCCMPFieldBridge) fkFieldsByRelatedPKFields.remove(relatedPKFields[i]);
1802            fkList.add(fkField);
1803         }
1804         foreignKeyFields = (JDBCCMP2xFieldBridge[]) fkList.toArray(new JDBCCMP2xFieldBridge[fkList.size()]);
1805      }
1806      else
1807      {
1808         foreignKeyFields = null;
1809      }
1810
1811      // are all FK fields mapped to PK fields?
1812
allFKFieldsMappedToPKFields = relatedPKFieldsByMyPKFields.size() > 0
1813         && relatedPKFieldsByMyPKFields.size() == foreignKeyFields.length;
1814
1815      if(foreignKeyFields != null)
1816      {
1817         jdbcType = new CMRJDBCType(Arrays.asList(foreignKeyFields));
1818      }
1819   }
1820
1821   private Transaction JavaDoc getTransaction()
1822   {
1823      try
1824      {
1825         EntityContainer container = getJDBCStoreManager().getContainer();
1826         TransactionManager JavaDoc tm = container.getTransactionManager();
1827         return tm.getTransaction();
1828      }
1829      catch(SystemException JavaDoc e)
1830      {
1831         throw new EJBException JavaDoc("Error getting transaction from the transaction manager", e);
1832      }
1833   }
1834
1835   /**
1836    * @return Map of lists of waiting related PK values keyed by not yet created this side's PK value.
1837    */

1838   private Map JavaDoc getRelatedPKsWaitingForMyPK()
1839   {
1840      return (Map JavaDoc) relatedPKValuesWaitingForMyPK.get();
1841   }
1842
1843   private RelationDataManager initRelationManager(JDBCCMRFieldBridge relatedField)
1844   {
1845      if(relationManager == null)
1846      {
1847         if(metadata.getRelationMetaData().isTableMappingStyle())
1848         {
1849            relationManager = new M2MRelationManager(this, relatedField);
1850         }
1851         else
1852         {
1853            relationManager = EMPTY_RELATION_MANAGER;
1854         }
1855      }
1856      return relationManager;
1857   }
1858
1859   private Object JavaDoc getRelatedPrimaryKey(Object JavaDoc localObject)
1860   {
1861      Object JavaDoc relatedId;
1862      if(relatedEntity.getLocalInterface().isAssignableFrom(localObject.getClass()))
1863      {
1864         EJBLocalObject JavaDoc local = (EJBLocalObject JavaDoc) localObject;
1865         try
1866         {
1867            relatedId = local.getPrimaryKey();
1868         }
1869         catch(NoSuchObjectLocalException JavaDoc e)
1870         {
1871            throw new IllegalArgumentException JavaDoc(e.getMessage());
1872         }
1873
1874         /*
1875         if(relatedManager.wasCascadeDeleted(relatedId))
1876         {
1877            throw new IllegalArgumentException("The instance was cascade-deleted: pk=" + relatedId);
1878         }
1879         */

1880      }
1881      else
1882      {
1883         throw new IllegalArgumentException JavaDoc("The values of this field must be of type " +
1884            relatedEntity.getLocalInterface().getName());
1885      }
1886      return relatedId;
1887   }
1888
1889   public String JavaDoc toString()
1890   {
1891      return entity.getEntityName() + '.' + getFieldName();
1892   }
1893
1894   private final class FieldState
1895   {
1896      private final EntityEnterpriseContext ctx;
1897      private List JavaDoc[] setHandle = new List JavaDoc[1];
1898      private Set JavaDoc addedRelations;
1899      private Set JavaDoc removedRelations;
1900      private Set JavaDoc relationSet;
1901      private boolean isLoaded = false;
1902      private final long lastRead = -1;
1903
1904      private boolean changed;
1905
1906      public FieldState(EntityEnterpriseContext ctx)
1907      {
1908         this.ctx = ctx;
1909         setHandle[0] = new ArrayList JavaDoc();
1910      }
1911
1912      /**
1913       * Get the current value (list of primary keys).
1914       */

1915      public List JavaDoc getValue()
1916      {
1917         if(!isLoaded)
1918         {
1919            throw new EJBException JavaDoc("CMR field value not loaded yet");
1920         }
1921         return Collections.unmodifiableList(setHandle[0]);
1922      }
1923
1924      /**
1925       * Has this relation been loaded.
1926       */

1927      public boolean isLoaded()
1928      {
1929         return isLoaded;
1930      }
1931
1932      /**
1933       * When was this value last read from the datastore.
1934       */

1935      public long getLastRead()
1936      {
1937         return lastRead;
1938      }
1939
1940      /**
1941       * Add this foreign to the relationship.
1942       */

1943      public void addRelation(Object JavaDoc fk)
1944      {
1945         if(isLoaded)
1946         {
1947            setHandle[0].add(fk);
1948         }
1949         else
1950         {
1951            if(removedRelations == null)
1952            {
1953               removedRelations = new HashSet JavaDoc();
1954               addedRelations = new HashSet JavaDoc();
1955            }
1956            removedRelations.remove(fk);
1957            addedRelations.add(fk);
1958         }
1959
1960         changed = true;
1961      }
1962
1963      /**
1964       * Remove this foreign to the relationship.
1965       */

1966      public void removeRelation(Object JavaDoc fk)
1967      {
1968         if(isLoaded)
1969         {
1970            setHandle[0].remove(fk);
1971         }
1972         else
1973         {
1974            if(removedRelations == null)
1975            {
1976               removedRelations = new HashSet JavaDoc();
1977               addedRelations = new HashSet JavaDoc();
1978            }
1979            addedRelations.remove(fk);
1980            removedRelations.add(fk);
1981         }
1982
1983         changed = true;
1984      }
1985
1986      /**
1987       * loads the collection of related ids
1988       */

1989      public void loadRelations(Collection JavaDoc values)
1990      {
1991         // check if we are aleready loaded
1992
if(isLoaded)
1993         {
1994            throw new EJBException JavaDoc("CMR field value is already loaded");
1995         }
1996
1997         // just in the case where there are lingering values
1998
setHandle[0].clear();
1999
2000         // add the new values
2001
setHandle[0].addAll(values);
2002
2003         if(removedRelations != null)
2004         {
2005            // remove the already removed values
2006
setHandle[0].removeAll(removedRelations);
2007            removedRelations = null;
2008         }
2009
2010         if(addedRelations != null)
2011         {
2012            // add the already added values
2013
// but remove FKs we are going to add to avoid duplication
2014
setHandle[0].removeAll(addedRelations);
2015            setHandle[0].addAll(addedRelations);
2016            addedRelations = null;
2017         }
2018
2019         // mark the field loaded
2020
isLoaded = true;
2021      }
2022
2023      /**
2024       * Get the current relation set or create a new one.
2025       */

2026      public Set JavaDoc getRelationSet()
2027      {
2028         if(!isLoaded)
2029         {
2030            throw new EJBException JavaDoc("CMR field value not loaded yet");
2031         }
2032
2033         if(ctx.isReadOnly())
2034         {
2035            // we are in a read-only invocation, so return a snapshot set
2036
return new RelationSet(JDBCCMRFieldBridge.this,
2037               ctx,
2038               new List JavaDoc[]{new ArrayList JavaDoc(setHandle[0])},
2039               true);
2040         }
2041
2042         // if we already have a relationset use it
2043
if(relationSet != null)
2044         {
2045            return relationSet;
2046         }
2047
2048         // construct a new relationshet
2049
try
2050         {
2051            // get the curent transaction
2052
EntityContainer container = getJDBCStoreManager().getContainer();
2053            TransactionManager JavaDoc tm = container.getTransactionManager();
2054            Transaction JavaDoc tx = tm.getTransaction();
2055
2056            // if whe have a valid transaction...
2057
if(tx != null && (tx.getStatus() == Status.STATUS_ACTIVE || tx.getStatus() == Status.STATUS_PREPARING))
2058            {
2059               // crete the relation set and register for a tx callback
2060
relationSet = new RelationSet(JDBCCMRFieldBridge.this, ctx, setHandle, false);
2061               TxSynchronization sync = new TxSynchronization(FieldState.this);
2062               tx.registerSynchronization(sync);
2063            }
2064            else
2065            {
2066               // if there is no transaction create a pre-failed list
2067
relationSet = new RelationSet(JDBCCMRFieldBridge.this, ctx, new List JavaDoc[1], false);
2068            }
2069
2070            return relationSet;
2071         }
2072         catch(SystemException JavaDoc e)
2073         {
2074            throw new EJBException JavaDoc("Error while creating RelationSet", e);
2075         }
2076         catch(RollbackException JavaDoc e)
2077         {
2078            throw new EJBException JavaDoc("Error while creating RelationSet", e);
2079         }
2080      }
2081
2082      /**
2083       * Invalidate the current relationship set.
2084       */

2085      public void invalidate()
2086      {
2087         // make a new set handle and copy the currentList to the new handle
2088
// this will cause old references to the relationSet to throw an
2089
// IllegalStateException if accesses, but will not cause a reload
2090
// in Commit Option A
2091
List JavaDoc currentList = null;
2092         if(setHandle != null && setHandle.length > 0)
2093         {
2094            currentList = setHandle[0];
2095            setHandle[0] = null;
2096         }
2097         setHandle = new List JavaDoc[1];
2098         setHandle[0] = currentList;
2099
2100         relationSet = null;
2101         changed = false;
2102      }
2103
2104      public boolean isChanged()
2105      {
2106         return changed;
2107      }
2108   }
2109
2110   private final static class CMRJDBCType implements JDBCType
2111   {
2112      private final String JavaDoc[] columnNames;
2113      private final Class JavaDoc[] javaTypes;
2114      private final int[] jdbcTypes;
2115      private final String JavaDoc[] sqlTypes;
2116      private final boolean[] notNull;
2117
2118      private CMRJDBCType(List JavaDoc fields)
2119      {
2120         List JavaDoc columnNamesList = new ArrayList JavaDoc();
2121         List JavaDoc javaTypesList = new ArrayList JavaDoc();
2122         List JavaDoc jdbcTypesList = new ArrayList JavaDoc();
2123         List JavaDoc sqlTypesList = new ArrayList JavaDoc();
2124         List JavaDoc notNullList = new ArrayList JavaDoc();
2125
2126         for(Iterator JavaDoc iter = fields.iterator(); iter.hasNext();)
2127         {
2128            JDBCCMPFieldBridge field = (JDBCCMPFieldBridge) iter.next();
2129            JDBCType type = field.getJDBCType();
2130            for(int i = 0; i < type.getColumnNames().length; i++)
2131            {
2132               columnNamesList.add(type.getColumnNames()[i]);
2133               javaTypesList.add(type.getJavaTypes()[i]);
2134               jdbcTypesList.add(new Integer JavaDoc(type.getJDBCTypes()[i]));
2135               sqlTypesList.add(type.getSQLTypes()[i]);
2136               notNullList.add(new Boolean JavaDoc(type.getNotNull()[i]));
2137            }
2138         }
2139         columnNames = (String JavaDoc[]) columnNamesList.toArray(new String JavaDoc[columnNamesList.size()]);
2140         javaTypes = (Class JavaDoc[]) javaTypesList.toArray(new Class JavaDoc[javaTypesList.size()]);
2141         sqlTypes = (String JavaDoc[]) sqlTypesList.toArray(new String JavaDoc[sqlTypesList.size()]);
2142
2143         jdbcTypes = new int[jdbcTypesList.size()];
2144         for(int i = 0; i < jdbcTypes.length; i++)
2145         {
2146            jdbcTypes[i] = ((Integer JavaDoc) jdbcTypesList.get(i)).intValue();
2147         }
2148
2149         notNull = new boolean[notNullList.size()];
2150         for(int i = 0; i < notNull.length; i++)
2151         {
2152            notNull[i] = ((Boolean JavaDoc) notNullList.get(i)).booleanValue();
2153         }
2154      }
2155
2156      public String JavaDoc[] getColumnNames()
2157      {
2158         return columnNames;
2159      }
2160
2161      public Class JavaDoc[] getJavaTypes()
2162      {
2163         return javaTypes;
2164      }
2165
2166      public int[] getJDBCTypes()
2167      {
2168         return jdbcTypes;
2169      }
2170
2171      public String JavaDoc[] getSQLTypes()
2172      {
2173         return sqlTypes;
2174      }
2175
2176      public boolean[] getNotNull()
2177      {
2178         return notNull;
2179      }
2180
2181      public boolean[] getAutoIncrement()
2182      {
2183         return new boolean[]{false};
2184      }
2185
2186      public Object JavaDoc getColumnValue(int index, Object JavaDoc value)
2187      {
2188         throw new UnsupportedOperationException JavaDoc();
2189      }
2190
2191      public Object JavaDoc setColumnValue(int index, Object JavaDoc value, Object JavaDoc columnValue)
2192      {
2193         throw new UnsupportedOperationException JavaDoc();
2194      }
2195
2196      public boolean hasMapper()
2197      {
2198         throw new UnsupportedOperationException JavaDoc("hasMapper is not implemented.");
2199      }
2200
2201      public boolean isSearchable()
2202      {
2203         throw new UnsupportedOperationException JavaDoc("isSearchable is not implemented.");
2204      }
2205
2206      public JDBCResultSetReader[] getResultSetReaders()
2207      {
2208         // foreign key fields has their result set readers
2209
throw new UnsupportedOperationException JavaDoc();
2210      }
2211
2212      public JDBCParameterSetter[] getParameterSetter()
2213      {
2214         throw new UnsupportedOperationException JavaDoc();
2215      }
2216   }
2217
2218   private final static class TxSynchronization implements Synchronization JavaDoc
2219   {
2220      private final WeakReference JavaDoc fieldStateRef;
2221
2222      private TxSynchronization(FieldState fieldState)
2223      {
2224         if(fieldState == null)
2225         {
2226            throw new IllegalArgumentException JavaDoc("fieldState is null");
2227         }
2228         this.fieldStateRef = new WeakReference JavaDoc(fieldState);
2229      }
2230
2231      public void beforeCompletion()
2232      {
2233         // REVIEW: THIS WILL NOT BE INVOKED ON A ROLLBACK
2234
// Be Careful where you put this invalidate
2235
// If you put it in afterCompletion, the beanlock will probably
2236
// be released before the invalidate and you will have a race
2237
FieldState fieldState = (FieldState) fieldStateRef.get();
2238         if(fieldState != null)
2239         {
2240            fieldState.invalidate();
2241         }
2242      }
2243
2244      public void afterCompletion(int status)
2245      {
2246      }
2247   }
2248
2249   public static interface RelationDataManager
2250   {
2251      void addRelation(JDBCCMRFieldBridge field, Object JavaDoc id, JDBCCMRFieldBridge relatedField, Object JavaDoc relatedId);
2252
2253      void removeRelation(JDBCCMRFieldBridge field, Object JavaDoc id, JDBCCMRFieldBridge relatedField, Object JavaDoc relatedId);
2254
2255      boolean isDirty();
2256
2257      RelationData getRelationData();
2258   }
2259
2260   private static final RelationDataManager EMPTY_RELATION_MANAGER = new RelationDataManager()
2261   {
2262      public void addRelation(JDBCCMRFieldBridge field, Object JavaDoc id, JDBCCMRFieldBridge relatedField, Object JavaDoc relatedId)
2263      {
2264      }
2265
2266      public void removeRelation(JDBCCMRFieldBridge field,
2267                                 Object JavaDoc id,
2268                                 JDBCCMRFieldBridge relatedField,
2269                                 Object JavaDoc relatedId)
2270      {
2271      }
2272
2273      public boolean isDirty()
2274      {
2275         return false;
2276      }
2277
2278      public RelationData getRelationData()
2279      {
2280         throw new UnsupportedOperationException JavaDoc();
2281      }
2282   };
2283
2284   public static class M2MRelationManager
2285      implements RelationDataManager
2286   {
2287      private final JDBCCMRFieldBridge leftField;
2288      private final JDBCCMRFieldBridge rightField;
2289
2290      private final TransactionLocal relationData = new TransactionLocal()
2291      {
2292         protected Object JavaDoc initialValue()
2293         {
2294            return new RelationData(leftField, rightField);
2295         }
2296      };
2297
2298      public M2MRelationManager(JDBCCMRFieldBridge leftField, JDBCCMRFieldBridge rightField)
2299      {
2300         this.leftField = leftField;
2301         this.rightField = rightField;
2302      }
2303
2304      public void addRelation(JDBCCMRFieldBridge field,
2305                              Object JavaDoc id,
2306                              JDBCCMRFieldBridge relatedField,
2307                              Object JavaDoc relatedId)
2308      {
2309         final RelationData local = getRelationData();
2310         local.addRelation(field, id, relatedField, relatedId);
2311      }
2312
2313      public void removeRelation(JDBCCMRFieldBridge field,
2314                                 Object JavaDoc id,
2315                                 JDBCCMRFieldBridge relatedField,
2316                                 Object JavaDoc relatedId)
2317      {
2318         RelationData local = getRelationData();
2319         local.removeRelation(field, id, relatedField, relatedId);
2320      }
2321
2322      public boolean isDirty()
2323      {
2324         RelationData local = getRelationData();
2325         return local.isDirty();
2326      }
2327
2328      public RelationData getRelationData()
2329      {
2330         final RelationData local = (RelationData) relationData.get();
2331         return local;
2332      }
2333   }
2334
2335   interface SecurityActions
2336   {
2337      class UTIL
2338      {
2339         static SecurityActions getSecurityActions()
2340         {
2341            return System.getSecurityManager() == null ? NON_PRIVILEGED : PRIVILEGED;
2342         }
2343      }
2344
2345      SecurityActions NON_PRIVILEGED = new SecurityActions()
2346      {
2347         public Principal JavaDoc getPrincipal()
2348         {
2349            return SecurityAssociation.getPrincipal();
2350         }
2351
2352         public Object JavaDoc getCredential()
2353         {
2354            return SecurityAssociation.getCredential();
2355         }
2356      };
2357
2358      SecurityActions PRIVILEGED = new SecurityActions()
2359      {
2360         private final PrivilegedAction JavaDoc getPrincipalAction = new PrivilegedAction JavaDoc()
2361         {
2362            public Object JavaDoc run()
2363            {
2364               return SecurityAssociation.getPrincipal();
2365            }
2366         };
2367
2368         private final PrivilegedAction JavaDoc getCredentialAction = new PrivilegedAction JavaDoc()
2369         {
2370            public Object JavaDoc run()
2371            {
2372               return SecurityAssociation.getCredential();
2373            }
2374         };
2375
2376         public Principal JavaDoc getPrincipal()
2377         {
2378            return (Principal JavaDoc) AccessController.doPrivileged(getPrincipalAction);
2379         }
2380
2381         public Object JavaDoc getCredential()
2382         {
2383            return AccessController.doPrivileged(getCredentialAction);
2384         }
2385      };
2386
2387      Principal JavaDoc getPrincipal();
2388
2389      Object JavaDoc getCredential();
2390   }
2391}
2392
Popular Tags