Save This Page
Home » jboss-5.0.0.CR1-src » org.jboss.ejb.plugins.cmp » jdbc » bridge » [javadoc | source]
    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;
   25   import java.lang.reflect.Method;
   26   import javax.sql.DataSource;
   27   import java.sql.PreparedStatement;
   28   import java.sql.ResultSet;
   29   import java.util.ArrayList;
   30   import java.util.Collection;
   31   import java.util.Collections;
   32   import java.util.HashSet;
   33   import java.util.Iterator;
   34   import java.util.List;
   35   import java.util.Map;
   36   import java.util.Set;
   37   import java.util.HashMap;
   38   import java.util.Arrays; 
   39   import java.rmi.RemoteException;
   40   import javax.ejb.EJBException;
   41   import javax.ejb.EJBLocalObject;
   42   import javax.ejb.EJBLocalHome;
   43   import javax.ejb.RemoveException;
   44   import javax.ejb.NoSuchObjectLocalException;
   45   import javax.transaction.Status;
   46   import javax.transaction.Synchronization;
   47   import javax.transaction.SystemException;
   48   import javax.transaction.Transaction;
   49   import javax.transaction.TransactionManager;
   50   import javax.transaction.RollbackException;
   51   
   52   import org.jboss.deployment.DeploymentException;
   53   import org.jboss.ejb.EntityCache;
   54   import org.jboss.ejb.EntityContainer;
   55   import org.jboss.ejb.EntityEnterpriseContext;
   56   import org.jboss.ejb.LocalProxyFactory;
   57   import org.jboss.ejb.plugins.cmp.bridge.EntityBridge;
   58   import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
   59   import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext;
   60   import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager;
   61   import org.jboss.ejb.plugins.cmp.jdbc.JDBCType;
   62   import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
   63   import org.jboss.ejb.plugins.cmp.jdbc.CascadeDeleteStrategy;
   64   import org.jboss.ejb.plugins.cmp.jdbc.RelationData;
   65   import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
   66   import org.jboss.ejb.plugins.cmp.jdbc.JDBCParameterSetter;
   67   import org.jboss.ejb.plugins.cmp.jdbc.JDBCResultSetReader;
   68   import org.jboss.tm.TransactionLocal;
   69   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
   70   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
   71   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData;
   72   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
   73   import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
   74   import org.jboss.ejb.plugins.lock.Entrancy;
   75   import org.jboss.invocation.InvocationType;
   76   import org.jboss.logging.Logger; 
   77   import org.jboss.security.SecurityContext;
   78   
   79   /**
   80    * JDBCCMRFieldBridge a bean relationship. This class only supports
   81    * relationships between entities managed by a JDBCStoreManager in the same
   82    * application.
   83    * <p/>
   84    * Life-cycle:
   85    * Tied to the EntityBridge.
   86    * <p/>
   87    * Multiplicity:
   88    * One for each role that entity has.
   89    *
   90    * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
   91    * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
   92    * @version $Revision: 62678 $
   93    */
   94   public final class JDBCCMRFieldBridge extends JDBCAbstractCMRFieldBridge
   95   {
   96      /**
   97       * The entity bridge to which this cmr field belongs.
   98       */
   99      private final JDBCEntityBridge entity;
  100      /**
  101       * The manager of this entity.
  102       */
  103      private final JDBCStoreManager manager;
  104      /**
  105       * Metadata of the relationship role that this field represents.
  106       */
  107      private final JDBCRelationshipRoleMetaData metadata;
  108      /**
  109       * The data source used to acess the relation table if relevant.
  110       */
  111      private DataSource dataSource;
  112      /**
  113       * The relation table name if relevent.
  114       */
  115      private String qualifiedTableName;
  116      private String tableName;
  117      /**
  118       * The key fields that this entity maintains in the relation table.
  119       */
  120      private JDBCCMP2xFieldBridge[] tableKeyFields;
  121      /**
  122       * JDBCType for the foreign key fields. Basically, this is an ordered
  123       * merge of the JDBCType of the foreign key field.
  124       */
  125      private JDBCType jdbcType;
  126      /**
  127       * The related entity's container.
  128       */
  129      private WeakReference relatedContainerRef;
  130      /**
  131       * The related entity's jdbc store manager
  132       */
  133      private JDBCStoreManager relatedManager;
  134      /**
  135       * The related entity.
  136       */
  137      private JDBCEntityBridge relatedEntity;
  138      /**
  139       * The related entity's cmr field for this relationship.
  140       */
  141      private JDBCCMRFieldBridge relatedCMRField;
  142      /**
  143       * da log.
  144       */
  145      private final Logger log;
  146   
  147      /**
  148       * Foreign key fields of this entity (i.e., related entities pk fields)
  149       */
  150      private JDBCCMP2xFieldBridge[] foreignKeyFields;
  151      /**
  152       * Indicates whether all FK fields are mapped to PK fields
  153       */
  154      private boolean allFKFieldsMappedToPKFields;
  155      /**
  156       * This map contains related PK fields that are mapped through FK fields to this entity's PK fields
  157       */
  158      private final Map relatedPKFieldsByMyPKFields = new HashMap();
  159      /**
  160       * This map contains related PK fields keyed by FK fields
  161       */
  162      private final Map relatedPKFieldsByMyFKFields = new HashMap();
  163      /**
  164       * Indicates whether there are foreign key fields mapped to CMP fields
  165       */
  166      private boolean hasFKFieldsMappedToCMPFields;
  167   
  168      // Map for lists of related PK values keyed by this side's PK values.
  169      // The values are put/removed by related entities when its fields representing
  170      // foreign key are changed. When entity with this CMR is created, this map is checked
  171      // for waiting for it entities. Relationship with waiting entities is established,
  172      // removing waiting entities' primary keys from the map.
  173      // NOTE: this map is used only for foreign key fields mapped to CMP fields.
  174      private final TransactionLocal relatedPKValuesWaitingForMyPK = new TransactionLocal()
  175      {
  176         protected Object initialValue()
  177         {
  178            return new HashMap();
  179         }
  180      };
  181   
  182      /**
  183       * FindByPrimaryKey method used to find related instances in case when FK fields mapped to PK fields
  184       */
  185      private Method relatedFindByPrimaryKey;
  186   
  187      /**
  188       * index of the field in the JDBCContext
  189       */
  190      private final int jdbcContextIndex;
  191   
  192      /**
  193       * cascade-delete strategy
  194       */
  195      private CascadeDeleteStrategy cascadeDeleteStrategy;
  196   
  197      /**
  198       * This CMR field and its related CMR field share the same RelationDataManager
  199       */
  200      private RelationDataManager relationManager;
  201   
  202      /**
  203       * Creates a cmr field for the entity based on the metadata.
  204       */
  205      public JDBCCMRFieldBridge(JDBCEntityBridge entity,
  206                                JDBCStoreManager manager,
  207                                JDBCRelationshipRoleMetaData metadata)
  208         throws DeploymentException
  209      {
  210         this.entity = entity;
  211         this.manager = manager;
  212         this.metadata = metadata;
  213         this.jdbcContextIndex = ((JDBCEntityBridge) manager.getEntityBridge()).getNextJDBCContextIndex();
  214   
  215         //  Creat the log
  216         String categoryName = this.getClass().getName() +
  217            "." + manager.getMetaData().getName() + ".";
  218         if(metadata.getCMRFieldName() != null)
  219         {
  220            categoryName += metadata.getCMRFieldName();
  221         }
  222         else
  223         {
  224            categoryName += metadata.getRelatedRole().getEntity().getName() +
  225               "-" + metadata.getRelatedRole().getCMRFieldName();
  226         }
  227         this.log = Logger.getLogger(categoryName);
  228      }
  229   
  230      public RelationDataManager getRelationDataManager()
  231      {
  232         return relationManager;
  233      }
  234   
  235      public void resolveRelationship() throws DeploymentException
  236      {
  237         //
  238         // Set handles to the related entity's container, cache,
  239         // manager, and invoker
  240         //
  241   
  242         // Related Entity Name
  243         String relatedEntityName = metadata.getRelatedRole().getEntity().getName();
  244   
  245         // Related Entity
  246         Catalog catalog = (Catalog) manager.getApplicationData("CATALOG");
  247         relatedEntity = (JDBCEntityBridge) catalog.getEntityByEJBName(relatedEntityName);
  248         if(relatedEntity == null)
  249         {
  250            throw new DeploymentException("Related entity not found: " +
  251               "entity=" +
  252               entity.getEntityName() +
  253               ", " +
  254               "cmrField=" +
  255               getFieldName() +
  256               ", " +
  257               "relatedEntity=" + relatedEntityName);
  258         }
  259   
  260         // Related CMR Field
  261         JDBCCMRFieldBridge[] cmrFields = (JDBCCMRFieldBridge[]) relatedEntity.getCMRFields();
  262         for(int i = 0; i < cmrFields.length; ++i)
  263         {
  264            JDBCCMRFieldBridge cmrField = cmrFields[i];
  265            if(metadata.getRelatedRole() == cmrField.getMetaData())
  266            {
  267               relatedCMRField = cmrField;
  268               break;
  269            }
  270         }
  271   
  272         // if we didn't find the related CMR field throw an exception
  273         // with a detailed message
  274         if(relatedCMRField == null)
  275         {
  276            String message = "Related CMR field not found in " +
  277               relatedEntity.getEntityName() + " for relationship from";
  278   
  279            message += entity.getEntityName() + ".";
  280            if(getFieldName() != null)
  281            {
  282               message += getFieldName();
  283            }
  284            else
  285            {
  286               message += "<no-field>";
  287            }
  288   
  289            message += " to ";
  290            message += relatedEntityName + ".";
  291            if(metadata.getRelatedRole().getCMRFieldName() != null)
  292            {
  293               message += metadata.getRelatedRole().getCMRFieldName();
  294            }
  295            else
  296            {
  297               message += "<no-field>";
  298            }
  299   
  300            throw new DeploymentException(message);
  301         }
  302   
  303         // Related Manager
  304         relatedManager = (JDBCStoreManager) relatedEntity.getManager();
  305   
  306         // Related Container
  307         EntityContainer relatedContainer = relatedManager.getContainer();
  308         this.relatedContainerRef = new WeakReference(relatedContainer);
  309   
  310         // related findByPrimaryKey
  311         Class homeClass = (relatedContainer.getLocalHomeClass() != null ?
  312            relatedContainer.getLocalHomeClass() : relatedContainer.getHomeClass());
  313         try
  314         {
  315            relatedFindByPrimaryKey =
  316               homeClass.getMethod("findByPrimaryKey", new Class[]{relatedEntity.getPrimaryKeyClass()});
  317         }
  318         catch(Exception e)
  319         {
  320            throw new DeploymentException("findByPrimaryKey(" +
  321               relatedEntity.getPrimaryKeyClass().getName()
  322               + " pk) was not found in " + homeClass.getName());
  323         }
  324   
  325         //
  326         // Initialize the key fields
  327         //
  328         if(metadata.getRelationMetaData().isTableMappingStyle())
  329         {
  330            // initialize relation table key fields
  331            Collection tableKeys = metadata.getKeyFields();
  332            List keyFieldsList = new ArrayList(tableKeys.size());
  333   
  334            // first phase is to create fk fields
  335            Map pkFieldsToFKFields = new HashMap(tableKeys.size());
  336            for(Iterator i = tableKeys.iterator(); i.hasNext();)
  337            {
  338               JDBCCMPFieldMetaData cmpFieldMetaData = (JDBCCMPFieldMetaData) i.next();
  339               FieldBridge pkField = entity.getFieldByName(cmpFieldMetaData.getFieldName());
  340               if(pkField == null)
  341               {
  342                  throw new DeploymentException("Primary key not found for key-field " + cmpFieldMetaData.getFieldName());
  343               }
  344               pkFieldsToFKFields.put(pkField, new JDBCCMP2xFieldBridge(manager, cmpFieldMetaData));
  345            }
  346            // second step is to order fk fields to match the order of pk fields
  347            JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
  348            for(int i = 0; i < pkFields.length; ++i)
  349            {
  350               Object fkField = pkFieldsToFKFields.get(pkFields[i]);
  351               if(fkField == null)
  352               {
  353                  throw new DeploymentException("Primary key " + pkFields[i].getFieldName() + " is not mapped.");
  354               }
  355               keyFieldsList.add(fkField);
  356            }
  357            tableKeyFields = (JDBCCMP2xFieldBridge[]) keyFieldsList.toArray(
  358               new JDBCCMP2xFieldBridge[keyFieldsList.size()]);
  359   
  360            dataSource = metadata.getRelationMetaData().getDataSource();
  361         }
  362         else
  363         {
  364            initializeForeignKeyFields();
  365            dataSource = hasForeignKey() ? entity.getDataSource() : relatedEntity.getDataSource();
  366         }
  367   
  368         // Fix table name
  369         //
  370         // This code doesn't work here...  The problem each side will generate
  371         // the table name and this will only work for simple generation.
  372         qualifiedTableName = SQLUtil.fixTableName(metadata.getRelationMetaData().getDefaultTableName(), dataSource);
  373         tableName = SQLUtil.getTableNameWithoutSchema(qualifiedTableName);
  374   
  375         relationManager = relatedCMRField.initRelationManager(this);
  376      }
  377   
  378      /**
  379       * The third phase of deployment. The method is called when relationships are already resolved.
  380       *
  381       * @throws DeploymentException
  382       */
  383      public void start() throws DeploymentException
  384      {
  385         cascadeDeleteStrategy = CascadeDeleteStrategy.getCascadeDeleteStrategy(this);
  386      }
  387   
  388      public boolean removeFromRelations(EntityEnterpriseContext ctx, Object[] oldRelationsRef)
  389      {
  390         load(ctx);
  391   
  392         FieldState fieldState = getFieldState(ctx);
  393         List value = fieldState.getValue();
  394   
  395         boolean removed = false;
  396         if(!value.isEmpty())
  397         {
  398            if(hasFKFieldsMappedToCMPFields)
  399            {
  400               if(isForeignKeyValid(value.get(0)))
  401               {
  402                  cascadeDeleteStrategy.removedIds(ctx, oldRelationsRef, value);
  403                  removed = true;
  404               }
  405            }
  406            else
  407            {
  408               cascadeDeleteStrategy.removedIds(ctx, oldRelationsRef, value);
  409               removed = true;
  410            }
  411         }
  412         return removed;
  413      }
  414   
  415      public void cascadeDelete(EntityEnterpriseContext ctx, List oldValues)
  416         throws RemoveException, RemoteException
  417      {
  418         cascadeDeleteStrategy.cascadeDelete(ctx, oldValues);
  419      }
  420   
  421      public boolean isBatchCascadeDelete()
  422      {
  423         return (cascadeDeleteStrategy instanceof CascadeDeleteStrategy.BatchCascadeDeleteStrategy);
  424      }
  425   
  426      /**
  427       * Gets the manager of this entity.
  428       */
  429      public JDBCStoreManager getJDBCStoreManager()
  430      {
  431         return manager;
  432      }
  433   
  434      /**
  435       * Gets bridge for this entity.
  436       */
  437      public JDBCAbstractEntityBridge getEntity()
  438      {
  439         return entity;
  440      }
  441   
  442      /**
  443       * Gets the metadata of the relationship role that this field represents.
  444       */
  445      public JDBCRelationshipRoleMetaData getMetaData()
  446      {
  447         return metadata;
  448      }
  449   
  450      /**
  451       * Gets the relation metadata.
  452       */
  453      public JDBCRelationMetaData getRelationMetaData()
  454      {
  455         return metadata.getRelationMetaData();
  456      }
  457   
  458      /**
  459       * Gets the name of this field.
  460       */
  461      public String getFieldName()
  462      {
  463         return metadata.getCMRFieldName();
  464      }
  465   
  466      /**
  467       * Gets the name of the relation table if relevent.
  468       */
  469      public String getQualifiedTableName()
  470      {
  471         return qualifiedTableName;
  472      }
  473   
  474      public String getTableName()
  475      {
  476         return tableName;
  477      }
  478   
  479      /**
  480       * Gets the datasource of the relation table if relevent.
  481       */
  482      public DataSource getDataSource()
  483      {
  484         return dataSource;
  485      }
  486   
  487      /**
  488       * Gets the read ahead meta data.
  489       */
  490      public JDBCReadAheadMetaData getReadAhead()
  491      {
  492         return metadata.getReadAhead();
  493      }
  494   
  495      public JDBCType getJDBCType()
  496      {
  497         return jdbcType;
  498      }
  499   
  500      public boolean isPrimaryKeyMember()
  501      {
  502         return false;
  503      }
  504   
  505      /**
  506       * Does this cmr field have foreign keys.
  507       */
  508      public boolean hasForeignKey()
  509      {
  510         return foreignKeyFields != null;
  511      }
  512   
  513      /**
  514       * Returns true if all FK fields are mapped to PK fields
  515       */
  516      public boolean allFkFieldsMappedToPkFields()
  517      {
  518         return allFKFieldsMappedToPKFields;
  519      }
  520   
  521      /**
  522       * Is this a collection valued field.
  523       */
  524      public boolean isCollectionValued()
  525      {
  526         return metadata.getRelatedRole().isMultiplicityMany();
  527      }
  528   
  529      /**
  530       * Is this a single valued field.
  531       */
  532      public boolean isSingleValued()
  533      {
  534         return metadata.getRelatedRole().isMultiplicityOne();
  535      }
  536   
  537      /**
  538       * Gets the key fields that this entity maintains in the relation table.
  539       */
  540      public JDBCFieldBridge[] getTableKeyFields()
  541      {
  542         return tableKeyFields;
  543      }
  544   
  545      /**
  546       * Gets the foreign key fields of this entity (i.e., related entities pk fields)
  547       */
  548      public JDBCFieldBridge[] getForeignKeyFields()
  549      {
  550         return foreignKeyFields;
  551      }
  552   
  553      /**
  554       * The related entity's cmr field for this relationship.
  555       */
  556      public JDBCAbstractCMRFieldBridge getRelatedCMRField()
  557      {
  558         return relatedCMRField;
  559      }
  560   
  561      /**
  562       * The related manger.
  563       */
  564      public JDBCStoreManager getRelatedManager()
  565      {
  566         return relatedManager;
  567      }
  568   
  569      /**
  570       * The related entity.
  571       */
  572      public EntityBridge getRelatedEntity()
  573      {
  574         return relatedEntity;
  575      }
  576   
  577      /**
  578       * The related entity.
  579       */
  580      public JDBCEntityBridge getRelatedJDBCEntity()
  581      {
  582         return relatedEntity;
  583      }
  584   
  585      /**
  586       * The related container
  587       */
  588      private final EntityContainer getRelatedContainer()
  589      {
  590         return (EntityContainer) relatedContainerRef.get();
  591      }
  592   
  593      /**
  594       * The related entity's local home interface.
  595       */
  596      public final Class getRelatedLocalInterface()
  597      {
  598         return getRelatedContainer().getLocalClass();
  599      }
  600   
  601      /**
  602       * The related entity's local container invoker.
  603       */
  604      public final LocalProxyFactory getRelatedInvoker()
  605      {
  606         return getRelatedContainer().getLocalProxyFactory();
  607      }
  608   
  609      /**
  610       * @param ctx - entity's context
  611       * @return true if entity is loaded, false - otherwise.
  612       */
  613      public boolean isLoaded(EntityEnterpriseContext ctx)
  614      {
  615         return getFieldState(ctx).isLoaded;
  616      }
  617   
  618      /**
  619       * Establishes relationships with related entities waited for passed in context
  620       * to be created.
  621       *
  622       * @param ctx - entity's context.
  623       */
  624      public void addRelatedPKsWaitedForMe(EntityEnterpriseContext ctx)
  625      {
  626         final Map relatedPKsMap = getRelatedPKsWaitingForMyPK();
  627         synchronized(relatedPKsMap)
  628         {
  629            List relatedPKsWaitingForMe = (List) relatedPKsMap.get(ctx.getId());
  630            if(relatedPKsWaitingForMe != null)
  631            {
  632               for(Iterator waitingPKsIter = relatedPKsWaitingForMe.iterator(); waitingPKsIter.hasNext();)
  633               {
  634                  Object waitingPK = waitingPKsIter.next();
  635                  waitingPKsIter.remove();
  636                  if(isForeignKeyValid(waitingPK))
  637                  {
  638                     createRelationLinks(ctx, waitingPK);
  639                  }
  640               }
  641            }
  642         }
  643      }
  644   
  645      /**
  646       * Is this field readonly?
  647       */
  648      public boolean isReadOnly()
  649      {
  650         return getRelationMetaData().isReadOnly();
  651      }
  652   
  653      /**
  654       * Had the read time expired?
  655       */
  656      public boolean isReadTimedOut(EntityEnterpriseContext ctx)
  657      {
  658         // if we are read/write then we are always timed out
  659         if(!isReadOnly())
  660         {
  661            return true;
  662         }
  663   
  664         // if read-time-out is -1 then we never time out.
  665         if(getRelationMetaData().getReadTimeOut() == -1)
  666         {
  667            return false;
  668         }
  669   
  670         long readInterval = System.currentTimeMillis() - getFieldState(ctx).getLastRead();
  671         return readInterval > getRelationMetaData().getReadTimeOut();
  672      }
  673   
  674      /**
  675       * @param ctx - entity's context.
  676       * @return the value of this field.
  677       */
  678      public Object getValue(EntityEnterpriseContext ctx)
  679      {
  680         // no user checks yet, but this is where they would go
  681         return getInstanceValue(ctx);
  682      }
  683   
  684      /**
  685       * Sets new value.
  686       *
  687       * @param ctx   - entity's context;
  688       * @param value - new value.
  689       */
  690      public void setValue(EntityEnterpriseContext ctx, Object value)
  691      {
  692         if(isReadOnly())
  693         {
  694            throw new EJBException("Field is read-only: fieldName=" + getFieldName());
  695         }
  696   
  697         if(!JDBCEntityBridge.isEjbCreateDone(ctx))
  698         {
  699            throw new IllegalStateException("A CMR field cannot be set " +
  700               "in ejbCreate; this should be done in the ejbPostCreate " +
  701               "method instead [EJB 2.0 Spec. 10.5.2].");
  702         }
  703   
  704         if(isCollectionValued() && value == null)
  705         {
  706            throw new IllegalArgumentException("null cannot be assigned to a " +
  707               "collection-valued cmr-field [EJB 2.0 Spec. 10.3.8].");
  708         }
  709         /*
  710         if(allFKFieldsMappedToPKFields)
  711         {
  712            throw new IllegalStateException(
  713               "Can't modify relationship: CMR field "
  714               + entity.getEntityName() + "." + getFieldName()
  715               + " has foreign key fields mapped to the primary key columns."
  716               + " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5].");
  717         }
  718         */
  719   
  720         setInstanceValue(ctx, value);
  721      }
  722   
  723      /**
  724       * Gets the value of the cmr field for the instance associated with
  725       * the context.
  726       */
  727      public Object getInstanceValue(EntityEnterpriseContext myCtx)
  728      {
  729         load(myCtx);
  730   
  731         FieldState fieldState = getFieldState(myCtx);
  732         if(isCollectionValued())
  733         {
  734            return fieldState.getRelationSet();
  735         }
  736   
  737         // only return one
  738         try
  739         {
  740            List value = fieldState.getValue();
  741            if(!value.isEmpty())
  742            {
  743               Object fk = value.get(0);
  744               return getRelatedEntityByFK(fk);
  745            }
  746            else if(foreignKeyFields != null)
  747            {
  748               // for those completely mapped to CMP fields and created in this current tx !!!
  749               Object relatedId = getRelatedIdFromContext(myCtx);
  750               if(relatedId != null)
  751               {
  752                  return getRelatedEntityByFK(relatedId);
  753               }
  754            }
  755            return null;
  756         }
  757         catch(EJBException e)
  758         {
  759            throw e;
  760         }
  761         catch(Exception e)
  762         {
  763            throw new EJBException(e);
  764         }
  765      }
  766   
  767      /**
  768       * Returns related entity's local interface.
  769       * If there are foreign key fields mapped to CMP fields, existence of related entity is checked
  770       * with findByPrimaryKey and if, in this case, related instance is not found, null is returned.
  771       * If foreign key fields mapped to its own columns then existence of related entity is not checked
  772       * and just its local object is returned.
  773       *
  774       * @param fk - foreign key value.
  775       * @return related local object instance.
  776       */
  777      public EJBLocalObject getRelatedEntityByFK(Object fk)
  778      {
  779         EJBLocalObject relatedLocalObject = null;
  780         final EntityContainer relatedContainer = getRelatedContainer();
  781   
  782         if(hasFKFieldsMappedToCMPFields
  783            && relatedManager.getReadAheadCache().getPreloadDataMap(fk, false) == null // not in preload cache
  784         )
  785         {
  786            EJBLocalHome relatedHome = relatedContainer.getLocalProxyFactory().getEJBLocalHome();
  787            try
  788            {
  789               relatedLocalObject = (EJBLocalObject) relatedFindByPrimaryKey.invoke(relatedHome, new Object[]{fk});
  790            }
  791            catch(Exception ignore)
  792            {
  793               // no such entity. it is ok to ignore
  794            }
  795         }
  796         else
  797         {
  798            relatedLocalObject = relatedContainer.getLocalProxyFactory().getEntityEJBLocalObject(fk);
  799         }
  800   
  801         return relatedLocalObject;
  802      }
  803   
  804      /**
  805       * This method is called only for CMR fields with foreign key fields mapped to CMP fields
  806       * to check the validity of the foreign key value.
  807       *
  808       * @param fk the foreign key to check
  809       * @return true if there is related entity with the equal primary key
  810       */
  811      public boolean isForeignKeyValid(Object fk)
  812      {
  813         boolean valid;
  814         if(relatedManager.getReadAheadCache().getPreloadDataMap(fk, false) != null)
  815         {
  816            valid = true;
  817         }
  818         else
  819         {
  820            EJBLocalHome relatedHome = getRelatedContainer().getLocalProxyFactory().getEJBLocalHome();
  821            try
  822            {
  823               relatedFindByPrimaryKey.invoke(relatedHome, new Object[]{fk});
  824               valid = true;
  825            }
  826            catch(Exception ignore)
  827            {
  828               // no such entity. it is ok to ignore
  829               valid = false;
  830            }
  831         }
  832         return valid;
  833      }
  834   
  835      /**
  836       * Sets the value of the cmr field for the instance associated with
  837       * the context.
  838       */
  839      public void setInstanceValue(EntityEnterpriseContext myCtx, Object newValue)
  840      {
  841         // validate new value first
  842         List newPks;
  843         if(newValue instanceof Collection)
  844         {
  845            Collection col = (Collection) newValue;
  846            if(!col.isEmpty())
  847            {
  848               newPks = new ArrayList(col.size());
  849               for(Iterator iter = col.iterator(); iter.hasNext();)
  850               {
  851                  Object localObject = iter.next();
  852                  if(localObject != null)
  853                  {
  854                     Object relatedId = getRelatedPrimaryKey(localObject);
  855   
  856                     // check whether new value modifies the primary key if there are FK fields mapped to PK fields
  857                     if(relatedPKFieldsByMyPKFields.size() > 0)
  858                     {
  859                        checkSetForeignKey(myCtx, relatedId);
  860                     }
  861   
  862                     newPks.add(relatedId);
  863                  }
  864               }
  865            }
  866            else
  867            {
  868               newPks = Collections.EMPTY_LIST;
  869            }
  870         }
  871         else
  872         {
  873            if(newValue != null)
  874            {
  875               newPks = Collections.singletonList(getRelatedPrimaryKey(newValue));
  876            }
  877            else
  878            {
  879               newPks = Collections.EMPTY_LIST;
  880            }
  881         }
  882   
  883         // load the current value
  884         load(myCtx);
  885         FieldState fieldState = getFieldState(myCtx);
  886   
  887         // is this just setting our own relation set back
  888         if(newValue == fieldState.getRelationSet())
  889         {
  890            return;
  891         }
  892   
  893         try
  894         {
  895            // Remove old value(s)
  896            List value = fieldState.getValue();
  897            if(!value.isEmpty())
  898            {
  899               Object[] curPks = value.toArray(new Object[value.size()]);
  900               for(int i = 0; i < curPks.length; ++i)
  901               {
  902                  destroyRelationLinks(myCtx, curPks[i]);
  903               }
  904            }
  905   
  906            // Add new value(s)
  907            for(int i = 0; i < newPks.size(); ++i)
  908            {
  909               createRelationLinks(myCtx, newPks.get(i));
  910            }
  911         }
  912         catch(RuntimeException e)
  913         {
  914            throw e;
  915         }
  916         catch(Exception e)
  917         {
  918            throw new EJBException(e);
  919         }
  920      }
  921   
  922      /**
  923       * Checks whether new foreign key value conflicts with primary key value
  924       * in case of foreign key to primary key mapping.
  925       *
  926       * @param myCtx    - entity's context;
  927       * @param newValue - new foreign key value.
  928       * @throws IllegalStateException - if new foreign key value changes
  929       *                               primary key value, otherwise returns silently.
  930       */
  931      private void checkSetForeignKey(EntityEnterpriseContext myCtx, Object newValue)
  932         throws IllegalStateException
  933      {
  934         JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
  935         for(int i = 0; i < pkFields.length; ++i)
  936         {
  937            JDBCCMP2xFieldBridge pkField = (JDBCCMP2xFieldBridge) pkFields[i];
  938            JDBCCMP2xFieldBridge relatedPkField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyPKFields.get(pkField);
  939            if(relatedPkField != null)
  940            {
  941               Object comingValue = relatedPkField.getPrimaryKeyValue(newValue);
  942               Object currentValue = pkField.getInstanceValue(myCtx);
  943   
  944               // they shouldn't be null
  945               if(!comingValue.equals(currentValue))
  946               {
  947                  throw new IllegalStateException("Can't create relationship: CMR field "
  948                     +
  949                     entity.getEntityName() +
  950                     "." +
  951                     getFieldName()
  952                     +
  953                     " has foreign key fields mapped to the primary key columns."
  954                     +
  955                     " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5]."
  956                     +
  957                     " primary key value is " +
  958                     currentValue
  959                     + " overriding value is " + comingValue);
  960               }
  961            }
  962         }
  963      }
  964   
  965      /**
  966       * Creates the relation links between the instance associated with the
  967       * context and the related instance (just the id is passed in).
  968       * <p/>
  969       * This method calls a.addRelation(b) and b.addRelation(a)
  970       */
  971      public void createRelationLinks(EntityEnterpriseContext myCtx, Object relatedId)
  972      {
  973         createRelationLinks(myCtx, relatedId, true);
  974      }
  975   
  976      public void createRelationLinks(EntityEnterpriseContext myCtx, Object relatedId, boolean updateForeignKey)
  977      {
  978         if(isReadOnly())
  979         {
  980            throw new EJBException("Field is read-only: " + getFieldName());
  981         }
  982   
  983         // If my multiplicity is one, then we need to free the new related context
  984         // from its old relationship.
  985         Transaction tx = getTransaction();
  986         if(metadata.isMultiplicityOne())
  987         {
  988            Object oldRelatedId = relatedCMRField.invokeGetRelatedId(tx, relatedId);
  989            if(oldRelatedId != null)
  990            {
  991               invokeRemoveRelation(tx, oldRelatedId, relatedId);
  992               relatedCMRField.invokeRemoveRelation(tx, relatedId, oldRelatedId);
  993            }
  994         }
  995   
  996         addRelation(myCtx, relatedId, updateForeignKey);
  997         relatedCMRField.invokeAddRelation(tx, relatedId, myCtx.getId());
  998      }
  999   
 1000      /**
 1001       * Destroys the relation links between the instance associated with the
 1002       * context and the related instance (just the id is passed in).
 1003       * <p/>
 1004       * This method calls a.removeRelation(b) and b.removeRelation(a)
 1005       */
 1006      public void destroyRelationLinks(EntityEnterpriseContext myCtx, Object relatedId)
 1007      {
 1008         destroyRelationLinks(myCtx, relatedId, true);
 1009      }
 1010   
 1011      /**
 1012       * Destroys the relation links between the instance associated with the
 1013       * context and the related instance (just the id is passed in).
 1014       * <p/>
 1015       * This method calls a.removeRelation(b) and b.removeRelation(a)
 1016       * <p/>
 1017       * If updateValueCollection is false, the related id collection is not
 1018       * updated. This form is only used by the RelationSet iterator.
 1019       */
 1020      public void destroyRelationLinks(EntityEnterpriseContext myCtx,
 1021                                       Object relatedId,
 1022                                       boolean updateValueCollection)
 1023      {
 1024         destroyRelationLinks(myCtx, relatedId, updateValueCollection, true);
 1025      }
 1026   
 1027      public void destroyRelationLinks(EntityEnterpriseContext myCtx,
 1028                                       Object relatedId,
 1029                                       boolean updateValueCollection,
 1030                                       boolean updateForeignKey)
 1031      {
 1032         if(isReadOnly())
 1033         {
 1034            throw new EJBException("Field is read-only: " + getFieldName());
 1035         }
 1036   
 1037         removeRelation(myCtx, relatedId, updateValueCollection, updateForeignKey);
 1038         relatedCMRField.invokeRemoveRelation(getTransaction(), relatedId, myCtx.getId());
 1039      }
 1040   
 1041      /**
 1042       * Schedules children for cascade delete.
 1043       */
 1044      public void scheduleChildrenForCascadeDelete(EntityEnterpriseContext ctx)
 1045      {
 1046         load(ctx);
 1047         FieldState fieldState = getFieldState(ctx);
 1048         List value = fieldState.getValue();
 1049         if(!value.isEmpty())
 1050         {
 1051            Transaction tx = getTransaction();
 1052            for(int i = 0; i < value.size(); ++i)
 1053            {
 1054               relatedCMRField.invokeScheduleForCascadeDelete(tx, value.get(i));
 1055            }
 1056         }
 1057      }
 1058   
 1059      /**
 1060       * Schedules children for batch cascade delete.
 1061       */
 1062      public void scheduleChildrenForBatchCascadeDelete(EntityEnterpriseContext ctx)
 1063      {
 1064         load(ctx);
 1065         FieldState fieldState = getFieldState(ctx);
 1066         List value = fieldState.getValue();
 1067         if(!value.isEmpty())
 1068         {
 1069            Transaction tx = getTransaction();
 1070            for(int i = 0; i < value.size(); ++i)
 1071            {
 1072               relatedCMRField.invokeScheduleForBatchCascadeDelete(tx, value.get(i));
 1073            }
 1074         }
 1075      }
 1076   
 1077      /**
 1078       * Schedules the instance with myId for cascade delete.
 1079       */
 1080      private Object invokeScheduleForCascadeDelete(Transaction tx, Object myId)
 1081      {
 1082         try
 1083         {
 1084            EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache(); 
 1085            SecurityContext sc = SecurityActions.getSecurityContext(); 
 1086            
 1087            CMRInvocation invocation = new CMRInvocation();
 1088            invocation.setCmrMessage(CMRMessage.SCHEDULE_FOR_CASCADE_DELETE);
 1089            invocation.setEntrancy(Entrancy.NON_ENTRANT);
 1090            invocation.setId(instanceCache.createCacheKey(myId));
 1091            invocation.setArguments(new Object[]{this});
 1092            invocation.setTransaction(tx);
 1093            invocation.setPrincipal(sc.getUtil().getUserPrincipal());
 1094            invocation.setCredential(sc.getUtil().getCredential());
 1095            invocation.setType(InvocationType.LOCAL); 
 1096            return manager.getContainer().invoke(invocation);
 1097         }
 1098         catch(EJBException e)
 1099         {
 1100            throw e;
 1101         }
 1102         catch(Exception e)
 1103         {
 1104            throw new EJBException("Error in scheduleForCascadeDelete()", e);
 1105         }
 1106      }
 1107   
 1108      /**
 1109       * Schedules the instance with myId for batch cascade delete.
 1110       */
 1111      private Object invokeScheduleForBatchCascadeDelete(Transaction tx, Object myId)
 1112      {
 1113         try
 1114         {
 1115            EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache(); 
 1116            SecurityContext sc = SecurityActions.getSecurityContext(); 
 1117            
 1118            CMRInvocation invocation = new CMRInvocation();
 1119            invocation.setCmrMessage(CMRMessage.SCHEDULE_FOR_BATCH_CASCADE_DELETE);
 1120            invocation.setEntrancy(Entrancy.NON_ENTRANT);
 1121            invocation.setId(instanceCache.createCacheKey(myId));
 1122            invocation.setArguments(new Object[]{this});
 1123            invocation.setTransaction(tx);
 1124            invocation.setPrincipal(sc.getUtil().getUserPrincipal());
 1125            invocation.setCredential(sc.getUtil().getCredential()); 
 1126            invocation.setType(InvocationType.LOCAL);
 1127            return manager.getContainer().invoke(invocation);
 1128         }
 1129         catch(EJBException e)
 1130         {
 1131            throw e;
 1132         }
 1133         catch(Exception e)
 1134         {
 1135            throw new EJBException("Error in scheduleForBatchCascadeDelete()", e);
 1136         }
 1137      }
 1138   
 1139      /**
 1140       * Invokes the getRelatedId on the related CMR field via the container
 1141       * invocation interceptor chain.
 1142       */
 1143      private Object invokeGetRelatedId(Transaction tx, Object myId)
 1144      {
 1145         try
 1146         {
 1147            EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache(); 
 1148            SecurityContext sc = SecurityActions.getSecurityContext(); 
 1149            
 1150            CMRInvocation invocation = new CMRInvocation();
 1151            invocation.setCmrMessage(CMRMessage.GET_RELATED_ID);
 1152            invocation.setEntrancy(Entrancy.NON_ENTRANT);
 1153            invocation.setId(instanceCache.createCacheKey(myId));
 1154            invocation.setArguments(new Object[]{this});
 1155            invocation.setTransaction(tx);
 1156            invocation.setPrincipal(sc.getUtil().getUserPrincipal());
 1157            invocation.setCredential(sc.getUtil().getCredential()); 
 1158            invocation.setType(InvocationType.LOCAL);
 1159            return manager.getContainer().invoke(invocation);
 1160         }
 1161         catch(EJBException e)
 1162         {
 1163            throw e;
 1164         }
 1165         catch(Exception e)
 1166         {
 1167            throw new EJBException("Error in getRelatedId", e);
 1168         }
 1169      }
 1170   
 1171      /**
 1172       * Invokes the addRelation on the related CMR field via the container
 1173       * invocation interceptor chain.
 1174       */
 1175      private void invokeAddRelation(Transaction tx, Object myId, Object relatedId)
 1176      {
 1177         try
 1178         {
 1179            SecurityContext sc = SecurityActions.getSecurityContext(); 
 1180            EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache(); 
 1181   
 1182            CMRInvocation invocation = new CMRInvocation();
 1183            invocation.setCmrMessage(CMRMessage.ADD_RELATION);
 1184            invocation.setEntrancy(Entrancy.NON_ENTRANT);
 1185            invocation.setId(instanceCache.createCacheKey(myId));
 1186            invocation.setArguments(new Object[]{this, relatedId});
 1187            invocation.setTransaction(tx);
 1188            invocation.setPrincipal(sc.getUtil().getUserPrincipal());
 1189            invocation.setCredential(sc.getUtil().getCredential());
 1190            invocation.setType(InvocationType.LOCAL); 
 1191            manager.getContainer().invoke(invocation);
 1192         }
 1193         catch(EJBException e)
 1194         {
 1195            throw e;
 1196         }
 1197         catch(Exception e)
 1198         {
 1199            throw new EJBException("Error in addRelation", e);
 1200         }
 1201      }
 1202   
 1203      /**
 1204       * Invokes the removeRelation on the related CMR field via the container
 1205       * invocation interceptor chain.
 1206       */
 1207      private void invokeRemoveRelation(Transaction tx, Object myId, Object relatedId)
 1208      {
 1209         try
 1210         {
 1211            EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache(); 
 1212            SecurityContext sc = SecurityActions.getSecurityContext(); 
 1213            
 1214            CMRInvocation invocation = new CMRInvocation();
 1215            invocation.setCmrMessage(CMRMessage.REMOVE_RELATION);
 1216            invocation.setEntrancy(Entrancy.NON_ENTRANT);
 1217            invocation.setId(instanceCache.createCacheKey(myId));
 1218            invocation.setArguments(new Object[]{this, relatedId});
 1219            invocation.setTransaction(tx);
 1220            invocation.setPrincipal(sc.getUtil().getUserPrincipal());
 1221            invocation.setCredential(sc.getUtil().getCredential()); 
 1222            invocation.setType(InvocationType.LOCAL);
 1223            manager.getContainer().invoke(invocation);
 1224         }
 1225         catch(EJBException e)
 1226         {
 1227            throw e;
 1228         }
 1229         catch(Exception e)
 1230         {
 1231            throw new EJBException("Error in removeRelation", e);
 1232         }
 1233      }
 1234   
 1235      /**
 1236       * Get the related entity's id.  This only works on single valued cmr fields.
 1237       */
 1238      public Object getRelatedId(EntityEnterpriseContext myCtx)
 1239      {
 1240         if(isCollectionValued())
 1241         {
 1242            throw new EJBException("getRelatedId may only be called on a cmr-field with a multiplicity of one.");
 1243         }
 1244   
 1245         load(myCtx);
 1246         List value = getFieldState(myCtx).getValue();
 1247         return value.isEmpty() ? null : value.get(0);
 1248      }
 1249   
 1250      /**
 1251       * Creates a new instance of related id based on foreign key value in the context.
 1252       *
 1253       * @param ctx - entity's context.
 1254       * @return related entity's id.
 1255       */
 1256      public Object getRelatedIdFromContext(EntityEnterpriseContext ctx)
 1257      {
 1258         Object relatedId = null;
 1259         Object fkFieldValue;
 1260         for(int i = 0; i < foreignKeyFields.length; ++i)
 1261         {
 1262            JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
 1263            fkFieldValue = fkField.getInstanceValue(ctx);
 1264            if(fkFieldValue == null)
 1265            {
 1266               return null;
 1267            }
 1268            JDBCCMP2xFieldBridge relatedPKField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyFKFields.get(fkField);
 1269            relatedId = relatedPKField.setPrimaryKeyValue(relatedId, fkFieldValue);
 1270         }
 1271         return relatedId;
 1272      }
 1273   
 1274      /**
 1275       * Adds the foreign key to the set of related ids, and updates any foreign key fields.
 1276       */
 1277      public void addRelation(EntityEnterpriseContext myCtx, Object fk)
 1278      {
 1279         addRelation(myCtx, fk, true);
 1280         relationManager.addRelation(this, myCtx.getId(), relatedCMRField, fk);
 1281      }
 1282   
 1283      private void addRelation(EntityEnterpriseContext myCtx, Object fk, boolean updateForeignKey)
 1284      {
 1285         checkSetForeignKey(myCtx, fk);
 1286   
 1287         if(isReadOnly())
 1288         {
 1289            throw new EJBException("Field is read-only: " + getFieldName());
 1290         }
 1291   
 1292         if(!JDBCEntityBridge.isEjbCreateDone(myCtx))
 1293         {
 1294            throw new IllegalStateException("A CMR field cannot be set or added " +
 1295               "to a relationship in ejbCreate; this should be done in the " +
 1296               "ejbPostCreate method instead [EJB 2.0 Spec. 10.5.2].");
 1297         }
 1298   
 1299         // add to current related set
 1300         FieldState myState = getFieldState(myCtx);
 1301         myState.addRelation(fk);
 1302   
 1303         // set the foreign key, if we have one.
 1304         if(hasForeignKey() && updateForeignKey)
 1305         {
 1306            setForeignKey(myCtx, fk);
 1307         }
 1308      }
 1309   
 1310      /**
 1311       * Removes the foreign key to the set of related ids, and updates any foreign key fields.
 1312       */
 1313      public void removeRelation(EntityEnterpriseContext myCtx, Object fk)
 1314      {
 1315         removeRelation(myCtx, fk, true, true);
 1316         relationManager.removeRelation(this, myCtx.getId(), relatedCMRField, fk);
 1317      }
 1318   
 1319      private void removeRelation(EntityEnterpriseContext myCtx,
 1320                                  Object fk,
 1321                                  boolean updateValueCollection,
 1322                                  boolean updateForeignKey)
 1323      {
 1324         if(isReadOnly())
 1325         {
 1326            throw new EJBException("Field is read-only: " + getFieldName());
 1327         }
 1328   
 1329         // remove from current related set
 1330         if(updateValueCollection)
 1331         {
 1332            FieldState myState = getFieldState(myCtx);
 1333            myState.removeRelation(fk);
 1334         }
 1335   
 1336         // set the foreign key to null, if we have one.
 1337         if(hasForeignKey() && updateForeignKey)
 1338         {
 1339            setForeignKey(myCtx, null);
 1340         }
 1341      }
 1342   
 1343      /**
 1344       * loads the collection of related ids
 1345       * NOTE: after loading, the field might not be in a clean state as we support adding and removing
 1346       * relations while the field is not loaded. The actual value of the field will be the value loaded
 1347       * plus added relations and minus removed relations while the field was not loaded.
 1348       */
 1349      private void load(EntityEnterpriseContext myCtx)
 1350      {
 1351         // if we are already loaded we're done
 1352         FieldState fieldState = getFieldState(myCtx);
 1353         if(fieldState.isLoaded())
 1354         {
 1355            return;
 1356         }
 1357   
 1358         // check the preload cache
 1359         if(log.isTraceEnabled())
 1360         {
 1361            log.trace("Read ahead cahce load: cmrField=" + getFieldName() + " pk=" + myCtx.getId());
 1362         }
 1363   
 1364         manager.getReadAheadCache().load(myCtx);
 1365         if(fieldState.isLoaded())
 1366         {
 1367            return;
 1368         }
 1369   
 1370         // load the value from the database
 1371         Collection values;
 1372         if(hasForeignKey())
 1373         {
 1374            // WARN: this method will load foreign keys if they are not yet loaded and
 1375            // changes relationship lazy loading in advanced training labs.
 1376            // i.e. it will load lazy cmp fields first of this entity and then will lazy load the related entity
 1377            // instead of loading this entity JOIN related entity in one query.
 1378            //Object fk = getRelatedIdFromContext(myCtx);
 1379   
 1380            boolean loadWithManager = false;
 1381            Object fk = null;
 1382            for(int i = 0; i < foreignKeyFields.length; ++i)
 1383            {
 1384               JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
 1385               // if the field is not loaded then load relationship with manager
 1386               if(!fkField.isLoaded(myCtx))
 1387               {
 1388                  loadWithManager = true;
 1389                  break;
 1390               }
 1391   
 1392               Object fkFieldValue = fkField.getInstanceValue(myCtx);
 1393               // if one of the fk is null, the whole fk is considered to be null
 1394               if(fkFieldValue == null)
 1395               {
 1396                  fk = null;
 1397                  break;
 1398               }
 1399               JDBCCMP2xFieldBridge relatedPKField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyFKFields.get(fkField);
 1400               fk = relatedPKField.setPrimaryKeyValue(fk, fkFieldValue);
 1401            }
 1402   
 1403            if(loadWithManager)
 1404            {
 1405               values = manager.loadRelation(this, myCtx.getId());
 1406            }
 1407            else
 1408            {
 1409               values = (fk == null ? Collections.EMPTY_LIST : Collections.singletonList(fk));
 1410            }
 1411         }
 1412         else
 1413         {
 1414            values = manager.loadRelation(this, myCtx.getId());
 1415         }
 1416         load(myCtx, values);
 1417      }
 1418   
 1419      public void load(EntityEnterpriseContext myCtx, Collection values)
 1420      {
 1421         // did we get more then one value for a single valued field
 1422         if(isSingleValued() && values.size() > 1)
 1423         {
 1424            throw new EJBException("Data contains multiple values, but this cmr field is single valued: " + values);
 1425         }
 1426   
 1427         // add the new values
 1428         FieldState fieldState = getFieldState(myCtx);
 1429         fieldState.loadRelations(values);
 1430   
 1431         // set the foreign key, if we have one.
 1432         if(hasForeignKey())
 1433         {
 1434            // update the states and locked values of FK fields
 1435            if(!values.isEmpty())
 1436            {
 1437               Object loadedValue = values.iterator().next();
 1438               for(int i = 0; i < foreignKeyFields.length; ++i)
 1439               {
 1440                  JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
 1441                  Object fieldValue = fkField.getPrimaryKeyValue(loadedValue);
 1442                  fkField.updateState(myCtx, fieldValue);
 1443               }
 1444            }
 1445   
 1446            // set the real FK value
 1447            List realValue = fieldState.getValue();
 1448            Object fk = realValue.isEmpty() ? null : realValue.get(0);
 1449            setForeignKey(myCtx, fk);
 1450         }
 1451   
 1452         JDBCEntityBridge.setCreated(myCtx);
 1453      }
 1454   
 1455      /**
 1456       * Sets the foreign key field value.
 1457       */
 1458      public void setForeignKey(EntityEnterpriseContext myCtx, Object fk)
 1459      {
 1460         if(!hasForeignKey())
 1461         {
 1462            throw new EJBException(getFieldName() + " CMR field does not have a foreign key to set.");
 1463         }
 1464   
 1465         for(int i = 0; i < foreignKeyFields.length; ++i)
 1466         {
 1467            JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
 1468            Object fieldValue = fkField.getPrimaryKeyValue(fk);
 1469            fkField.setInstanceValue(myCtx, fieldValue);
 1470         }
 1471      }
 1472   
 1473      /**
 1474       * Initialized the foreign key fields.
 1475       */
 1476      public void initInstance(EntityEnterpriseContext ctx)
 1477      {
 1478         // mark this field as loaded
 1479         getFieldState(ctx).loadRelations(Collections.EMPTY_SET);
 1480   
 1481         if(foreignKeyFields == null)
 1482         {
 1483            return;
 1484         }
 1485   
 1486         for(int i = 0; i < foreignKeyFields.length; ++i)
 1487         {
 1488            JDBCCMP2xFieldBridge foreignKeyField = foreignKeyFields[i];
 1489            if(!foreignKeyField.isFKFieldMappedToCMPField())
 1490            {
 1491               foreignKeyField.setInstanceValue(ctx, null);
 1492            }
 1493         }
 1494      }
 1495   
 1496      /**
 1497       * resets the persistence context of the foreign key fields
 1498       */
 1499      public void resetPersistenceContext(EntityEnterpriseContext ctx)
 1500      {
 1501         // only resetStats if the read has timed out
 1502         if(!isReadTimedOut(ctx))
 1503         {
 1504            return;
 1505         }
 1506   
 1507         // clear the field state
 1508         JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
 1509         // invalidate current field state
 1510         /*
 1511         FieldState currentFieldState = (FieldState) jdbcCtx.getFieldState(jdbcContextIndex);
 1512         if(currentFieldState != null)
 1513            currentFieldState.invalidate();
 1514            */
 1515         jdbcCtx.setFieldState(jdbcContextIndex, null);
 1516   
 1517         if(foreignKeyFields == null)
 1518         {
 1519            return;
 1520         }
 1521   
 1522         for(int i = 0; i < foreignKeyFields.length; ++i)
 1523         {
 1524            JDBCCMP2xFieldBridge foreignKeyField = foreignKeyFields[i];
 1525            if(!foreignKeyField.isFKFieldMappedToCMPField())
 1526            {
 1527               foreignKeyField.resetPersistenceContext(ctx);
 1528            }
 1529         }
 1530      }
 1531   
 1532      public int setInstanceParameters(PreparedStatement ps,
 1533                                       int parameterIndex,
 1534                                       EntityEnterpriseContext ctx)
 1535      {
 1536         if(foreignKeyFields == null)
 1537         {
 1538            return parameterIndex;
 1539         }
 1540   
 1541         List value = getFieldState(ctx).getValue();
 1542         Object fk = (value.isEmpty() ? null : value.get(0));
 1543   
 1544         for(int i = 0; i < foreignKeyFields.length; ++i)
 1545         {
 1546            parameterIndex = foreignKeyFields[i].setPrimaryKeyParameters(ps, parameterIndex, fk);
 1547         }
 1548   
 1549         return parameterIndex;
 1550      }
 1551   
 1552      public int loadInstanceResults(ResultSet rs,
 1553                                     int parameterIndex,
 1554                                     EntityEnterpriseContext ctx)
 1555      {
 1556         if(!hasForeignKey())
 1557         {
 1558            return parameterIndex;
 1559         }
 1560   
 1561         // load the value from the database
 1562         Object[] ref = new Object[1];
 1563         parameterIndex = loadArgumentResults(rs, parameterIndex, ref);
 1564   
 1565         // only actually set the value if the state is not already loaded
 1566         FieldState fieldState = getFieldState(ctx);
 1567         if(!fieldState.isLoaded())
 1568         {
 1569            if(ref[0] != null)
 1570            {
 1571               load(ctx, Collections.singleton(ref[0]));
 1572            }
 1573            else
 1574            {
 1575               load(ctx, Collections.EMPTY_SET);
 1576            }
 1577         }
 1578         return parameterIndex;
 1579      }
 1580   
 1581      public int loadArgumentResults(ResultSet rs, int parameterIndex, Object[] fkRef)
 1582      {
 1583         if(foreignKeyFields == null)
 1584         {
 1585            return parameterIndex;
 1586         }
 1587   
 1588         boolean fkIsNull = false;
 1589   
 1590         // value of this field,  will be filled in below
 1591         Object[] argumentRef = new Object[1];
 1592         for(int i = 0; i < foreignKeyFields.length; ++i)
 1593         {
 1594            JDBCCMPFieldBridge field = foreignKeyFields[i];
 1595            parameterIndex = field.loadArgumentResults(rs, parameterIndex, argumentRef);
 1596   
 1597            if(fkIsNull)
 1598            {
 1599               continue;
 1600            }
 1601            if(field.getPrimaryKeyField() != null)
 1602            {
 1603               // if there is a null field among FK fields, the whole FK field is considered null.
 1604               // NOTE: don't throw exception in this case, it's ok if FK is partly mapped to a PK
 1605               // NOTE2: we still need to iterate through foreign key fields and 'load' them to
 1606               // return correct parameterIndex.
 1607               if(argumentRef[0] == null)
 1608               {
 1609                  fkRef[0] = null;
 1610                  fkIsNull = true;
 1611               }
 1612               else
 1613               {
 1614                  // if we don't have a pk object yet create one
 1615                  if(fkRef[0] == null)
 1616                  {
 1617                     fkRef[0] = relatedEntity.createPrimaryKeyInstance();
 1618                  }
 1619                  try
 1620                  {
 1621                     // Set this field's value into the primary key object.
 1622                     field.getPrimaryKeyField().set(fkRef[0], argumentRef[0]);
 1623                  }
 1624                  catch(Exception e)
 1625                  {
 1626                     // Non recoverable internal exception
 1627                     throw new EJBException("Internal error setting foreign-key field " + getFieldName(), e);
 1628                  }
 1629               }
 1630            }
 1631            else
 1632            {
 1633               // This field is the primary key, so no extraction is necessary.
 1634               fkRef[0] = argumentRef[0];
 1635            }
 1636         }
 1637         return parameterIndex;
 1638      }
 1639   
 1640      /**
 1641       * This method is never called.
 1642       * In case of a CMR with foreign key fields, only the foreign key fields are asked for the dirty state.
 1643       */
 1644      public boolean isDirty(EntityEnterpriseContext ctx)
 1645      {
 1646         return foreignKeyFields == null ? relationManager.isDirty() : false;
 1647      }
 1648   
 1649      public boolean invalidateCache(EntityEnterpriseContext ctx)
 1650      {
 1651         JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
 1652         FieldState fieldState = (FieldState) jdbcCtx.getFieldState(jdbcContextIndex);
 1653         return fieldState == null ? false : fieldState.isChanged();
 1654      }
 1655   
 1656      /**
 1657       * This method is never called.
 1658       * In case of a CMR
 1659       * - with foreign key fields, the foreign key fields are cleaned when necessary according to CMP fields'
 1660       * behaviour.
 1661       * - from m:m relationship, added/removed key pairs are cleared in application tx data map on sync.
 1662       */
 1663      public void setClean(EntityEnterpriseContext ctx)
 1664      {
 1665         throw new UnsupportedOperationException();
 1666      }
 1667   
 1668      public boolean isCMPField()
 1669      {
 1670         return false;
 1671      }
 1672   
 1673      public JDBCEntityPersistenceStore getManager()
 1674      {
 1675         return manager;
 1676      }
 1677   
 1678      public boolean hasFKFieldsMappedToCMPFields()
 1679      {
 1680         return hasFKFieldsMappedToCMPFields;
 1681      }
 1682   
 1683      public void addRelatedPKWaitingForMyPK(Object myPK, Object relatedPK)
 1684      {
 1685         Map relatedPKsWaitingForMyPK = getRelatedPKsWaitingForMyPK();
 1686         synchronized(relatedPKsWaitingForMyPK)
 1687         {
 1688            List relatedPKs = (List) relatedPKsWaitingForMyPK.get(myPK);
 1689            if(relatedPKs == null)
 1690            {
 1691               relatedPKs = new ArrayList(1);
 1692               relatedPKsWaitingForMyPK.put(myPK, relatedPKs);
 1693            }
 1694            relatedPKs.add(relatedPK);
 1695         }
 1696      }
 1697   
 1698      public void removeRelatedPKWaitingForMyPK(Object myPK, Object relatedPK)
 1699      {
 1700         final Map relatedPKMap = getRelatedPKsWaitingForMyPK();
 1701         synchronized(relatedPKMap)
 1702         {
 1703            List relatedPKs = (List) relatedPKMap.get(myPK);
 1704            if(relatedPKs != null)
 1705            {
 1706               relatedPKs.remove(relatedPK);
 1707            }
 1708         }
 1709      }
 1710   
 1711      /**
 1712       * Gets the field state object from the persistence context.
 1713       */
 1714      private FieldState getFieldState(EntityEnterpriseContext ctx)
 1715      {
 1716         JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
 1717         FieldState fieldState = (FieldState) jdbcCtx.getFieldState(jdbcContextIndex);
 1718         if(fieldState == null)
 1719         {
 1720            fieldState = new FieldState(ctx);
 1721            jdbcCtx.setFieldState(jdbcContextIndex, fieldState);
 1722         }
 1723         return fieldState;
 1724      }
 1725   
 1726      /**
 1727       * Initializes foreign key fields
 1728       *
 1729       * @throws DeploymentException
 1730       */
 1731      private void initializeForeignKeyFields()
 1732         throws DeploymentException
 1733      {
 1734         Collection foreignKeys = metadata.getRelatedRole().getKeyFields();
 1735   
 1736         // temporary map used later to write fk fields in special order
 1737         Map fkFieldsByRelatedPKFields = new HashMap();
 1738         for(Iterator i = foreignKeys.iterator(); i.hasNext();)
 1739         {
 1740            JDBCCMPFieldMetaData fkFieldMetaData = (JDBCCMPFieldMetaData) i.next();
 1741            JDBCCMP2xFieldBridge relatedPKField =
 1742               (JDBCCMP2xFieldBridge) relatedEntity.getFieldByName(fkFieldMetaData.getFieldName());
 1743   
 1744            // now determine whether the fk is mapped to a pk column
 1745            String fkColumnName = fkFieldMetaData.getColumnName();
 1746            JDBCCMP2xFieldBridge fkField = null;
 1747   
 1748            // look among the CMP fields for the field with the same column name
 1749            JDBCFieldBridge[] tableFields = entity.getTableFields();
 1750            for(int tableInd = 0; tableInd < tableFields.length && fkField == null; ++tableInd)
 1751            {
 1752               JDBCCMP2xFieldBridge cmpField = (JDBCCMP2xFieldBridge) tableFields[tableInd];
 1753               if(fkColumnName.equals(cmpField.getColumnName()))
 1754               {
 1755                  hasFKFieldsMappedToCMPFields = true;
 1756   
 1757                  // construct the foreign key field
 1758                  fkField = new JDBCCMP2xFieldBridge((JDBCStoreManager) cmpField.getManager(), // this cmpField's manager
 1759                     relatedPKField.getFieldName(),
 1760                     relatedPKField.getFieldType(),
 1761                     cmpField.getJDBCType(), // this cmpField's jdbc type
 1762                     relatedPKField.isReadOnly(),
 1763                     relatedPKField.getReadTimeOut(),
 1764                     relatedPKField.getPrimaryKeyClass(),
 1765                     relatedPKField.getPrimaryKeyField(),
 1766                     cmpField, // CMP field I am mapped to
 1767                     this,
 1768                     fkColumnName);
 1769   
 1770                  if(cmpField.isPrimaryKeyMember())
 1771                  {
 1772                     relatedPKFieldsByMyPKFields.put(cmpField, relatedPKField);
 1773                  }
 1774               }
 1775            }
 1776   
 1777            // if the fk is not a part of pk then create a new field
 1778            if(fkField == null)
 1779            {
 1780               fkField = new JDBCCMP2xFieldBridge(manager,
 1781                  fkFieldMetaData,
 1782                  manager.getJDBCTypeFactory().getJDBCType(fkFieldMetaData));
 1783            }
 1784   
 1785            fkFieldsByRelatedPKFields.put(relatedPKField, fkField); // temporary map
 1786            relatedPKFieldsByMyFKFields.put(fkField, relatedPKField);
 1787         }
 1788   
 1789         // Note: this important to order the foreign key fields so that their order matches
 1790         // the order of related entity's pk fields in case of complex primary keys.
 1791         // The order is important in fk-constraint generation and in SELECT when loading
 1792         if(fkFieldsByRelatedPKFields.size() > 0)
 1793         {
 1794            JDBCFieldBridge[] relatedPKFields = relatedEntity.getPrimaryKeyFields();
 1795            List fkList = new ArrayList(relatedPKFields.length);
 1796            for(int i = 0; i < relatedPKFields.length; ++i)
 1797            {
 1798               JDBCCMPFieldBridge fkField = (JDBCCMPFieldBridge) fkFieldsByRelatedPKFields.remove(relatedPKFields[i]);
 1799               fkList.add(fkField);
 1800            }
 1801            foreignKeyFields = (JDBCCMP2xFieldBridge[]) fkList.toArray(new JDBCCMP2xFieldBridge[fkList.size()]);
 1802         }
 1803         else
 1804         {
 1805            foreignKeyFields = null;
 1806         }
 1807   
 1808         // are all FK fields mapped to PK fields?
 1809         allFKFieldsMappedToPKFields = relatedPKFieldsByMyPKFields.size() > 0
 1810            && relatedPKFieldsByMyPKFields.size() == foreignKeyFields.length;
 1811   
 1812         if(foreignKeyFields != null)
 1813         {
 1814            jdbcType = new CMRJDBCType(Arrays.asList(foreignKeyFields));
 1815         }
 1816      }
 1817   
 1818      private Transaction getTransaction()
 1819      {
 1820         try
 1821         {
 1822            EntityContainer container = getJDBCStoreManager().getContainer();
 1823            TransactionManager tm = container.getTransactionManager();
 1824            return tm.getTransaction();
 1825         }
 1826         catch(SystemException e)
 1827         {
 1828            throw new EJBException("Error getting transaction from the transaction manager", e);
 1829         }
 1830      }
 1831   
 1832      /**
 1833       * @return Map of lists of waiting related PK values keyed by not yet created this side's PK value.
 1834       */
 1835      private Map getRelatedPKsWaitingForMyPK()
 1836      {
 1837         return (Map) relatedPKValuesWaitingForMyPK.get();
 1838      }
 1839   
 1840      private RelationDataManager initRelationManager(JDBCCMRFieldBridge relatedField)
 1841      {
 1842         if(relationManager == null)
 1843         {
 1844            if(metadata.getRelationMetaData().isTableMappingStyle())
 1845            {
 1846               relationManager = new M2MRelationManager(this, relatedField);
 1847            }
 1848            else
 1849            {
 1850               relationManager = EMPTY_RELATION_MANAGER;
 1851            }
 1852         }
 1853         return relationManager;
 1854      }
 1855   
 1856      private Object getRelatedPrimaryKey(Object localObject)
 1857      {
 1858         Object relatedId;
 1859         if(relatedEntity.getLocalInterface().isAssignableFrom(localObject.getClass()))
 1860         {
 1861            EJBLocalObject local = (EJBLocalObject) localObject;
 1862            try
 1863            {
 1864               relatedId = local.getPrimaryKey();
 1865            }
 1866            catch(NoSuchObjectLocalException e)
 1867            {
 1868               throw new IllegalArgumentException(e.getMessage());
 1869            }
 1870   
 1871            /*
 1872            if(relatedManager.wasCascadeDeleted(relatedId))
 1873            {
 1874               throw new IllegalArgumentException("The instance was cascade-deleted: pk=" + relatedId);
 1875            }
 1876            */
 1877         }
 1878         else
 1879         {
 1880            throw new IllegalArgumentException("The values of this field must be of type " +
 1881               relatedEntity.getLocalInterface().getName());
 1882         }
 1883         return relatedId;
 1884      }
 1885   
 1886      public String toString()
 1887      {
 1888         return entity.getEntityName() + '.' + getFieldName();
 1889      }
 1890   
 1891      private final class FieldState
 1892      {
 1893         private final EntityEnterpriseContext ctx;
 1894         private List[] setHandle = new List[1];
 1895         private Set addedRelations;
 1896         private Set removedRelations;
 1897         private Set relationSet;
 1898         private boolean isLoaded = false;
 1899         private final long lastRead = -1;
 1900   
 1901         private boolean changed;
 1902   
 1903         public FieldState(EntityEnterpriseContext ctx)
 1904         {
 1905            this.ctx = ctx;
 1906            setHandle[0] = new ArrayList();
 1907         }
 1908   
 1909         /**
 1910          * Get the current value (list of primary keys).
 1911          */
 1912         public List getValue()
 1913         {
 1914            if(!isLoaded)
 1915            {
 1916               throw new EJBException("CMR field value not loaded yet");
 1917            }
 1918            return Collections.unmodifiableList(setHandle[0]);
 1919         }
 1920   
 1921         /**
 1922          * Has this relation been loaded.
 1923          */
 1924         public boolean isLoaded()
 1925         {
 1926            return isLoaded;
 1927         }
 1928   
 1929         /**
 1930          * When was this value last read from the datastore.
 1931          */
 1932         public long getLastRead()
 1933         {
 1934            return lastRead;
 1935         }
 1936   
 1937         /**
 1938          * Add this foreign to the relationship.
 1939          */
 1940         public void addRelation(Object fk)
 1941         {
 1942            if(isLoaded)
 1943            {
 1944               setHandle[0].add(fk);
 1945            }
 1946            else
 1947            {
 1948               if(removedRelations == null)
 1949               {
 1950                  removedRelations = new HashSet();
 1951                  addedRelations = new HashSet();
 1952               }
 1953               removedRelations.remove(fk);
 1954               addedRelations.add(fk);
 1955            }
 1956   
 1957            changed = true;
 1958         }
 1959   
 1960         /**
 1961          * Remove this foreign to the relationship.
 1962          */
 1963         public void removeRelation(Object fk)
 1964         {
 1965            if(isLoaded)
 1966            {
 1967               setHandle[0].remove(fk);
 1968            }
 1969            else
 1970            {
 1971               if(removedRelations == null)
 1972               {
 1973                  removedRelations = new HashSet();
 1974                  addedRelations = new HashSet();
 1975               }
 1976               addedRelations.remove(fk);
 1977               removedRelations.add(fk);
 1978            }
 1979   
 1980            changed = true;
 1981         }
 1982   
 1983         /**
 1984          * loads the collection of related ids
 1985          */
 1986         public void loadRelations(Collection values)
 1987         {
 1988            // check if we are aleready loaded
 1989            if(isLoaded)
 1990            {
 1991               throw new EJBException("CMR field value is already loaded");
 1992            }
 1993   
 1994            // just in the case where there are lingering values
 1995            setHandle[0].clear();
 1996   
 1997            // add the new values
 1998            setHandle[0].addAll(values);
 1999   
 2000            if(removedRelations != null)
 2001            {
 2002               // remove the already removed values
 2003               setHandle[0].removeAll(removedRelations);
 2004               removedRelations = null;
 2005            }
 2006   
 2007            if(addedRelations != null)
 2008            {
 2009               // add the already added values
 2010               // but remove FKs we are going to add to avoid duplication
 2011               setHandle[0].removeAll(addedRelations);
 2012               setHandle[0].addAll(addedRelations);
 2013               addedRelations = null;
 2014            }
 2015   
 2016            // mark the field loaded
 2017            isLoaded = true;
 2018         }
 2019   
 2020         /**
 2021          * Get the current relation set or create a new one.
 2022          */
 2023         public Set getRelationSet()
 2024         {
 2025            if(!isLoaded)
 2026            {
 2027               throw new EJBException("CMR field value not loaded yet");
 2028            }
 2029   
 2030            if(ctx.isReadOnly())
 2031            {
 2032               // we are in a read-only invocation, so return a snapshot set
 2033               return new RelationSet(JDBCCMRFieldBridge.this,
 2034                  ctx,
 2035                  new List[]{new ArrayList(setHandle[0])},
 2036                  true);
 2037            }
 2038   
 2039            // if we already have a relationset use it
 2040            if(relationSet != null)
 2041            {
 2042               return relationSet;
 2043            }
 2044   
 2045            // construct a new relationshet
 2046            try
 2047            {
 2048               // get the curent transaction
 2049               EntityContainer container = getJDBCStoreManager().getContainer();
 2050               TransactionManager tm = container.getTransactionManager();
 2051               Transaction tx = tm.getTransaction();
 2052   
 2053               // if whe have a valid transaction...
 2054               if(tx != null && (tx.getStatus() == Status.STATUS_ACTIVE || tx.getStatus() == Status.STATUS_PREPARING))
 2055               {
 2056                  // crete the relation set and register for a tx callback
 2057                  relationSet = new RelationSet(JDBCCMRFieldBridge.this, ctx, setHandle, false);
 2058                  TxSynchronization sync = new TxSynchronization(FieldState.this);
 2059                  tx.registerSynchronization(sync);
 2060               }
 2061               else
 2062               {
 2063                  // if there is no transaction create a pre-failed list
 2064                  relationSet = new RelationSet(JDBCCMRFieldBridge.this, ctx, new List[1], false);
 2065               }
 2066   
 2067               return relationSet;
 2068            }
 2069            catch(SystemException e)
 2070            {
 2071               throw new EJBException("Error while creating RelationSet", e);
 2072            }
 2073            catch(RollbackException e)
 2074            {
 2075               throw new EJBException("Error while creating RelationSet", e);
 2076            }
 2077         }
 2078   
 2079         /**
 2080          * Invalidate the current relationship set.
 2081          */
 2082         public void invalidate()
 2083         {
 2084            // make a new set handle and copy the currentList to the new handle
 2085            // this will cause old references to the relationSet to throw an
 2086            // IllegalStateException if accesses, but will not cause a reload
 2087            // in Commit Option A
 2088            List currentList = null;
 2089            if(setHandle != null && setHandle.length > 0)
 2090            {
 2091               currentList = setHandle[0];
 2092               setHandle[0] = null;
 2093            }
 2094            setHandle = new List[1];
 2095            setHandle[0] = currentList;
 2096   
 2097            relationSet = null;
 2098            changed = false;
 2099         }
 2100   
 2101         public boolean isChanged()
 2102         {
 2103            return changed;
 2104         }
 2105      }
 2106   
 2107      private final static class CMRJDBCType implements JDBCType
 2108      {
 2109         private final String[] columnNames;
 2110         private final Class[] javaTypes;
 2111         private final int[] jdbcTypes;
 2112         private final String[] sqlTypes;
 2113         private final boolean[] notNull;
 2114   
 2115         private CMRJDBCType(List fields)
 2116         {
 2117            List columnNamesList = new ArrayList();
 2118            List javaTypesList = new ArrayList();
 2119            List jdbcTypesList = new ArrayList();
 2120            List sqlTypesList = new ArrayList();
 2121            List notNullList = new ArrayList();
 2122   
 2123            for(Iterator iter = fields.iterator(); iter.hasNext();)
 2124            {
 2125               JDBCCMPFieldBridge field = (JDBCCMPFieldBridge) iter.next();
 2126               JDBCType type = field.getJDBCType();
 2127               for(int i = 0; i < type.getColumnNames().length; i++)
 2128               {
 2129                  columnNamesList.add(type.getColumnNames()[i]);
 2130                  javaTypesList.add(type.getJavaTypes()[i]);
 2131                  jdbcTypesList.add(new Integer(type.getJDBCTypes()[i]));
 2132                  sqlTypesList.add(type.getSQLTypes()[i]);
 2133                  notNullList.add(new Boolean(type.getNotNull()[i]));
 2134               }
 2135            }
 2136            columnNames = (String[]) columnNamesList.toArray(new String[columnNamesList.size()]);
 2137            javaTypes = (Class[]) javaTypesList.toArray(new Class[javaTypesList.size()]);
 2138            sqlTypes = (String[]) sqlTypesList.toArray(new String[sqlTypesList.size()]);
 2139   
 2140            jdbcTypes = new int[jdbcTypesList.size()];
 2141            for(int i = 0; i < jdbcTypes.length; i++)
 2142            {
 2143               jdbcTypes[i] = ((Integer) jdbcTypesList.get(i)).intValue();
 2144            }
 2145   
 2146            notNull = new boolean[notNullList.size()];
 2147            for(int i = 0; i < notNull.length; i++)
 2148            {
 2149               notNull[i] = ((Boolean) notNullList.get(i)).booleanValue();
 2150            }
 2151         }
 2152   
 2153         public String[] getColumnNames()
 2154         {
 2155            return columnNames;
 2156         }
 2157   
 2158         public Class[] getJavaTypes()
 2159         {
 2160            return javaTypes;
 2161         }
 2162   
 2163         public int[] getJDBCTypes()
 2164         {
 2165            return jdbcTypes;
 2166         }
 2167   
 2168         public String[] getSQLTypes()
 2169         {
 2170            return sqlTypes;
 2171         }
 2172   
 2173         public boolean[] getNotNull()
 2174         {
 2175            return notNull;
 2176         }
 2177   
 2178         public boolean[] getAutoIncrement()
 2179         {
 2180            return new boolean[]{false};
 2181         }
 2182   
 2183         public Object getColumnValue(int index, Object value)
 2184         {
 2185            throw new UnsupportedOperationException();
 2186         }
 2187   
 2188         public Object setColumnValue(int index, Object value, Object columnValue)
 2189         {
 2190            throw new UnsupportedOperationException();
 2191         }
 2192   
 2193         public boolean hasMapper()
 2194         {
 2195            throw new UnsupportedOperationException("hasMapper is not implemented.");
 2196         }
 2197   
 2198         public boolean isSearchable()
 2199         {
 2200            throw new UnsupportedOperationException("isSearchable is not implemented.");
 2201         }
 2202   
 2203         public JDBCResultSetReader[] getResultSetReaders()
 2204         {
 2205            // foreign key fields has their result set readers
 2206            throw new UnsupportedOperationException();
 2207         }
 2208   
 2209         public JDBCParameterSetter[] getParameterSetter()
 2210         {
 2211            throw new UnsupportedOperationException();
 2212         }
 2213      }
 2214   
 2215      private final static class TxSynchronization implements Synchronization
 2216      {
 2217         private final WeakReference fieldStateRef;
 2218   
 2219         private TxSynchronization(FieldState fieldState)
 2220         {
 2221            if(fieldState == null)
 2222            {
 2223               throw new IllegalArgumentException("fieldState is null");
 2224            }
 2225            this.fieldStateRef = new WeakReference(fieldState);
 2226         }
 2227   
 2228         public void beforeCompletion()
 2229         {
 2230            // REVIEW: THIS WILL NOT BE INVOKED ON A ROLLBACK
 2231            // Be Careful where you put this invalidate
 2232            // If you put it in afterCompletion, the beanlock will probably
 2233            // be released before the invalidate and you will have a race
 2234            FieldState fieldState = (FieldState) fieldStateRef.get();
 2235            if(fieldState != null)
 2236            {
 2237               fieldState.invalidate();
 2238            }
 2239         }
 2240   
 2241         public void afterCompletion(int status)
 2242         {
 2243         }
 2244      }
 2245   
 2246      public static interface RelationDataManager
 2247      {
 2248         void addRelation(JDBCCMRFieldBridge field, Object id, JDBCCMRFieldBridge relatedField, Object relatedId);
 2249   
 2250         void removeRelation(JDBCCMRFieldBridge field, Object id, JDBCCMRFieldBridge relatedField, Object relatedId);
 2251   
 2252         boolean isDirty();
 2253   
 2254         RelationData getRelationData();
 2255      }
 2256   
 2257      private static final RelationDataManager EMPTY_RELATION_MANAGER = new RelationDataManager()
 2258      {
 2259         public void addRelation(JDBCCMRFieldBridge field, Object id, JDBCCMRFieldBridge relatedField, Object relatedId)
 2260         {
 2261         }
 2262   
 2263         public void removeRelation(JDBCCMRFieldBridge field,
 2264                                    Object id,
 2265                                    JDBCCMRFieldBridge relatedField,
 2266                                    Object relatedId)
 2267         {
 2268         }
 2269   
 2270         public boolean isDirty()
 2271         {
 2272            return false;
 2273         }
 2274   
 2275         public RelationData getRelationData()
 2276         {
 2277            throw new UnsupportedOperationException();
 2278         }
 2279      };
 2280   
 2281      public static class M2MRelationManager
 2282         implements RelationDataManager
 2283      {
 2284         private final JDBCCMRFieldBridge leftField;
 2285         private final JDBCCMRFieldBridge rightField;
 2286   
 2287         private final TransactionLocal relationData = new TransactionLocal()
 2288         {
 2289            protected Object initialValue()
 2290            {
 2291               return new RelationData(leftField, rightField);
 2292            }
 2293         };
 2294   
 2295         public M2MRelationManager(JDBCCMRFieldBridge leftField, JDBCCMRFieldBridge rightField)
 2296         {
 2297            this.leftField = leftField;
 2298            this.rightField = rightField;
 2299         }
 2300   
 2301         public void addRelation(JDBCCMRFieldBridge field,
 2302                                 Object id,
 2303                                 JDBCCMRFieldBridge relatedField,
 2304                                 Object relatedId)
 2305         {
 2306            final RelationData local = getRelationData();
 2307            local.addRelation(field, id, relatedField, relatedId);
 2308         }
 2309   
 2310         public void removeRelation(JDBCCMRFieldBridge field,
 2311                                    Object id,
 2312                                    JDBCCMRFieldBridge relatedField,
 2313                                    Object relatedId)
 2314         {
 2315            RelationData local = getRelationData();
 2316            local.removeRelation(field, id, relatedField, relatedId);
 2317         }
 2318   
 2319         public boolean isDirty()
 2320         {
 2321            RelationData local = getRelationData();
 2322            return local.isDirty();
 2323         }
 2324   
 2325         public RelationData getRelationData()
 2326         {
 2327            final RelationData local = (RelationData) relationData.get();
 2328            return local;
 2329         }
 2330      }
 2331   
 2332      /*interface SecurityActions
 2333      {
 2334         class UTIL
 2335         {
 2336            static SecurityActions getSecurityActions()
 2337            {
 2338               return System.getSecurityManager() == null ? NON_PRIVILEGED : PRIVILEGED;
 2339            }
 2340         }
 2341   
 2342         SecurityActions NON_PRIVILEGED = new SecurityActions()
 2343         {
 2344            public Principal getPrincipal()
 2345            {
 2346               return SecurityAssociation.getPrincipal();
 2347            }
 2348   
 2349            public Object getCredential()
 2350            {
 2351               return SecurityAssociation.getCredential();
 2352            }
 2353         };
 2354   
 2355         SecurityActions PRIVILEGED = new SecurityActions()
 2356         {
 2357            private final PrivilegedAction getPrincipalAction = new PrivilegedAction()
 2358            {
 2359               public Object run()
 2360               {
 2361                  return SecurityAssociation.getPrincipal();
 2362               }
 2363            };
 2364   
 2365            private final PrivilegedAction getCredentialAction = new PrivilegedAction()
 2366            {
 2367               public Object run()
 2368               {
 2369                  return SecurityAssociation.getCredential();
 2370               }
 2371            };
 2372   
 2373            public Principal getPrincipal()
 2374            {
 2375               return (Principal) AccessController.doPrivileged(getPrincipalAction);
 2376            }
 2377   
 2378            public Object getCredential()
 2379            {
 2380               return AccessController.doPrivileged(getCredentialAction);
 2381            }
 2382         };
 2383   
 2384         Principal getPrincipal();
 2385   
 2386         Object getCredential();
 2387      }*/
 2388   }

Save This Page
Home » jboss-5.0.0.CR1-src » org.jboss.ejb.plugins.cmp » jdbc » bridge » [javadoc | source]