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