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.sql.PreparedStatement;
   25   import java.sql.ResultSet;
   26   
   27   import java.util.ArrayList;
   28   import java.util.Collection;
   29   import java.util.Collections;
   30   import java.util.HashMap;
   31   import java.util.Iterator;
   32   import java.util.List;
   33   import java.util.Map;
   34   import java.util.Arrays;
   35   import java.util.NoSuchElementException;
   36   import java.rmi.RemoteException;
   37   
   38   import javax.ejb.EJBException;
   39   import javax.ejb.RemoveException;
   40   import javax.sql.DataSource;
   41   import javax.naming.InitialContext;
   42   import javax.naming.NamingException;
   43   
   44   import org.jboss.deployment.DeploymentException;
   45   import org.jboss.ejb.EntityEnterpriseContext;
   46   
   47   import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext;
   48   import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager;
   49   import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
   50   import org.jboss.ejb.plugins.cmp.jdbc.LockingStrategy;
   51   import org.jboss.ejb.plugins.cmp.jdbc.JDBCTypeFactory;
   52   import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
   53   
   54   import org.jboss.ejb.plugins.cmp.bridge.EntityBridgeInvocationHandler;
   55   import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
   56   
   57   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCAuditMetaData;
   58   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
   59   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
   60   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData;
   61   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
   62   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCOptimisticLockingMetaData;
   63   import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
   64   import org.jboss.proxy.compiler.Proxies;
   65   import org.jboss.proxy.compiler.InvocationHandler;
   66   import org.jboss.logging.Logger;
   67   
   68   
   69   /**
   70    * JDBCEntityBridge follows the Bridge pattern [Gamma et. al, 1995].
   71    * The main job of this class is to construct the bridge from entity meta data.
   72    *
   73    * Life-cycle:
   74    *      Undefined. Should be tied to CMPStoreManager.
   75    *
   76    * Multiplicity:
   77    *      One per cmp entity bean type.
   78    *
   79    * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
   80    * @author <a href="mailto:loubyansky@ua.fm">Alex Loubyansky</a>
   81    * @author <a href="mailto:heiko.rupp@cellent.de">Heiko W. Rupp</a>
   82    * @version $Revision: 43495 $
   83    */
   84   public class JDBCEntityBridge implements JDBCAbstractEntityBridge
   85   {
   86      public final static byte LOADED = 1;
   87      public final static byte LOAD_REQUIRED = 2;
   88      public final static byte DIRTY = 4;
   89      public final static byte CHECK_DIRTY = 8;
   90      public final static byte LOCKED = 16;
   91      public final static byte ADD_TO_SET_ON_UPDATE = 32;
   92      public final static byte ADD_TO_WHERE_ON_UPDATE = 64;
   93   
   94      private static final String DEFAULT_LOADGROUP_NAME = "*";
   95   
   96      private JDBCEntityMetaData metadata;
   97      private JDBCStoreManager manager;
   98      private DataSource dataSource;
   99      private String qualifiedTableName;
  100      private String tableName;
  101   
  102      /** primary key fields (not added to cmpFields) */
  103      private final String primaryKeyFieldName;
  104      private final Class primaryKeyClass;
  105      private JDBCCMPFieldBridge[] primaryKeyFields;
  106      /** CMP fields */
  107      private JDBCCMPFieldBridge[] cmpFields;
  108      /** CMR fields */
  109      private JDBCCMRFieldBridge[] cmrFields;
  110      /** table fields */
  111      private JDBCCMPFieldBridge[] tableFields;
  112   
  113      /** used for optimistic locking. (added to cmpFields) */
  114      private JDBCCMPFieldBridge versionField;
  115   
  116      // Audit fields (added to cmpFields)
  117      private JDBCCMPFieldBridge createdPrincipalField;
  118      private JDBCCMPFieldBridge createdTimeField;
  119      private JDBCCMPFieldBridge updatedPrincipalField;
  120      private JDBCCMPFieldBridge updatedTimeField;
  121   
  122      private Map selectorsByMethod;
  123   
  124      /** Load group is a boolean array with tableFields.length elements. True means the element is in the group. */
  125      private Map loadGroupMasks;
  126      private List lazyLoadGroupMasks;
  127      private boolean[] eagerLoadGroupMask;
  128      private boolean[] defaultLockGroupMask;
  129   
  130      private int jdbcContextSize;
  131   
  132      private final Logger log;
  133   
  134      public JDBCEntityBridge(JDBCEntityMetaData metadata, JDBCStoreManager manager)
  135         throws DeploymentException
  136      {
  137         this.metadata = metadata;
  138         this.manager = manager;
  139         primaryKeyFieldName = metadata.getPrimaryKeyFieldName();
  140         primaryKeyClass = metadata.getPrimaryKeyClass();
  141         log = Logger.getLogger(this.getClass().getName() + "." + metadata.getName());
  142      }
  143   
  144      public void init() throws DeploymentException
  145      {
  146         try
  147         {
  148            InitialContext ic = new InitialContext();
  149            dataSource = (DataSource)ic.lookup(metadata.getDataSourceName());
  150         }
  151         catch(NamingException e)
  152         {
  153            throw new DeploymentException("Error: can't find data source: " +
  154               metadata.getDataSourceName(), e);
  155         }
  156   
  157         qualifiedTableName = SQLUtil.fixTableName(metadata.getDefaultTableName(), dataSource);
  158         int dotIndex = qualifiedTableName.indexOf('.');
  159         tableName = dotIndex == -1 ? qualifiedTableName : qualifiedTableName.substring(dotIndex + 1);
  160   
  161         // CMP fields
  162         loadCMPFields(metadata);
  163   
  164         // CMR fields
  165         loadCMRFields(metadata);
  166   
  167         // create locking field
  168         JDBCOptimisticLockingMetaData lockMetaData = metadata.getOptimisticLocking();
  169         if(lockMetaData != null && lockMetaData.getLockingField() != null)
  170         {
  171            Integer strategy = lockMetaData.getLockingStrategy();
  172            JDBCCMPFieldMetaData versionMD = lockMetaData.getLockingField();
  173   
  174            versionField = getCMPFieldByName(versionMD.getFieldName());
  175            boolean hidden = versionField == null;
  176            if(strategy == JDBCOptimisticLockingMetaData.VERSION_COLUMN_STRATEGY)
  177            {
  178               if(hidden)
  179                  versionField = new JDBCLongVersionFieldBridge(manager, versionMD);
  180               else
  181                  versionField = new JDBCLongVersionFieldBridge((JDBCCMP2xFieldBridge)versionField);
  182            }
  183            else if(strategy == JDBCOptimisticLockingMetaData.TIMESTAMP_COLUMN_STRATEGY)
  184            {
  185               if(hidden)
  186                  versionField = new JDBCTimestampVersionFieldBridge(manager, versionMD);
  187               else
  188                  versionField = new JDBCTimestampVersionFieldBridge((JDBCCMP2xFieldBridge)versionField);
  189            }
  190            else if(strategy == JDBCOptimisticLockingMetaData.KEYGENERATOR_COLUMN_STRATEGY)
  191            {
  192               if(hidden)
  193                  versionField = new JDBCKeyGenVersionFieldBridge(
  194                     manager, versionMD, lockMetaData.getKeyGeneratorFactory());
  195               else
  196                  versionField = new JDBCKeyGenVersionFieldBridge(
  197                     (JDBCCMP2xFieldBridge)versionField, lockMetaData.getKeyGeneratorFactory());
  198            }
  199   
  200            if(hidden)
  201               addCMPField(versionField);
  202            else
  203               tableFields[versionField.getTableIndex()] = versionField;
  204         }
  205   
  206         // audit fields
  207         JDBCAuditMetaData auditMetaData = metadata.getAudit();
  208         if(auditMetaData != null)
  209         {
  210            JDBCCMPFieldMetaData auditField = auditMetaData.getCreatedPrincipalField();
  211            if(auditField != null)
  212            {
  213               createdPrincipalField = getCMPFieldByName(auditField.getFieldName());
  214               if(createdPrincipalField == null)
  215               {
  216                  createdPrincipalField = new JDBCCMP2xFieldBridge(manager, auditField);
  217                  addCMPField(createdPrincipalField);
  218               }
  219            }
  220            else
  221            {
  222               createdPrincipalField = null;
  223            }
  224   
  225            auditField = auditMetaData.getCreatedTimeField();
  226            if(auditField != null)
  227            {
  228               createdTimeField = getCMPFieldByName(auditField.getFieldName());
  229               if(createdTimeField == null)
  230               {
  231                  createdTimeField = new JDBCCMP2xFieldBridge(manager, auditField, JDBCTypeFactory.EQUALS, false);
  232                  addCMPField(createdTimeField);
  233               }
  234               else
  235               {
  236                  // just to override state factory and check-dirty-after-get
  237                  createdTimeField = new JDBCCMP2xFieldBridge(
  238                     (JDBCCMP2xFieldBridge)createdTimeField, JDBCTypeFactory.EQUALS, false);
  239                  tableFields[createdTimeField.getTableIndex()] = createdTimeField;
  240               }
  241            }
  242            else
  243            {
  244               createdTimeField = null;
  245            }
  246   
  247            auditField = auditMetaData.getUpdatedPrincipalField();
  248            if(auditField != null)
  249            {
  250               updatedPrincipalField = getCMPFieldByName(auditField.getFieldName());
  251               if(updatedPrincipalField == null)
  252               {
  253                  updatedPrincipalField = new JDBCCMP2xUpdatedPrincipalFieldBridge(manager, auditField);
  254                  addCMPField(updatedPrincipalField);
  255               }
  256               else
  257               {
  258                  updatedPrincipalField = new JDBCCMP2xUpdatedPrincipalFieldBridge(
  259                     (JDBCCMP2xFieldBridge)updatedPrincipalField);
  260                  tableFields[updatedPrincipalField.getTableIndex()] = updatedPrincipalField;
  261               }
  262            }
  263            else
  264            {
  265               updatedPrincipalField = null;
  266            }
  267   
  268            auditField = auditMetaData.getUpdatedTimeField();
  269            if(auditField != null)
  270            {
  271               updatedTimeField = getCMPFieldByName(auditField.getFieldName());
  272               if(updatedTimeField == null)
  273               {
  274                  updatedTimeField = new JDBCCMP2xUpdatedTimeFieldBridge(manager, auditField);
  275                  addCMPField(updatedTimeField);
  276               }
  277               else
  278               {
  279                  updatedTimeField = new JDBCCMP2xUpdatedTimeFieldBridge((JDBCCMP2xFieldBridge)updatedTimeField);
  280                  tableFields[updatedTimeField.getTableIndex()] = updatedTimeField;
  281               }
  282            }
  283            else
  284            {
  285               updatedTimeField = null;
  286            }
  287         }
  288   
  289         // ejbSelect methods
  290         loadSelectors(metadata);
  291      }
  292   
  293      public void resolveRelationships() throws DeploymentException
  294      {
  295         for(int i = 0; i < cmrFields.length; ++i)
  296            cmrFields[i].resolveRelationship();
  297   
  298         // load groups:  cannot be created until relationships have
  299         // been resolved because loadgroups must check for foreign keys
  300         loadLoadGroups(metadata);
  301         loadEagerLoadGroup(metadata);
  302         loadLazyLoadGroups(metadata);
  303      }
  304   
  305      /**
  306       * The third phase of deployment. The method is called when relationships are already resolved.
  307       * @throws DeploymentException
  308       */
  309      public void start() throws DeploymentException
  310      {
  311         for(int i = 0; i < cmrFields.length; ++i)
  312         {
  313            cmrFields[i].start();
  314         }
  315      }
  316   
  317      public boolean removeFromRelations(EntityEnterpriseContext ctx, Object[] oldRelations)
  318      {
  319         boolean removed = false;
  320         for(int i = 0; i < cmrFields.length; ++i)
  321         {
  322            if(cmrFields[i].removeFromRelations(ctx, oldRelations))
  323               removed = true;
  324         }
  325         return removed;
  326      }
  327   
  328      public void cascadeDelete(EntityEnterpriseContext ctx, Map oldRelations)
  329         throws RemoveException, RemoteException
  330      {
  331         for(int i = 0; i < cmrFields.length; ++i)
  332         {
  333            JDBCCMRFieldBridge cmrField = cmrFields[i];
  334            Object value = oldRelations.get(cmrField);
  335            if(value != null)
  336               cmrField.cascadeDelete(ctx, (List)value);
  337         }
  338      }
  339   
  340      public String getEntityName()
  341      {
  342         return metadata.getName();
  343      }
  344   
  345      public String getAbstractSchemaName()
  346      {
  347         return metadata.getAbstractSchemaName();
  348      }
  349   
  350      public Class getRemoteInterface()
  351      {
  352         return metadata.getRemoteClass();
  353      }
  354   
  355      public Class getLocalInterface()
  356      {
  357         return metadata.getLocalClass();
  358      }
  359   
  360      public JDBCEntityMetaData getMetaData()
  361      {
  362         return metadata;
  363      }
  364   
  365      public JDBCEntityPersistenceStore getManager()
  366      {
  367         return manager;
  368      }
  369   
  370      /**
  371       * Returns the datasource for this entity.
  372       */
  373      public DataSource getDataSource()
  374      {
  375         return dataSource;
  376      }
  377   
  378      public String getTableName()
  379      {
  380         return tableName;
  381      }
  382   
  383      public String getQualifiedTableName()
  384      {
  385         return qualifiedTableName;
  386      }
  387   
  388      public Class getPrimaryKeyClass()
  389      {
  390         return primaryKeyClass;
  391      }
  392   
  393      public int getListCacheMax()
  394      {
  395         return metadata.getListCacheMax();
  396      }
  397   
  398      public int getFetchSize()
  399      {
  400         return metadata.getFetchSize();
  401      }
  402   
  403      public Object createPrimaryKeyInstance()
  404      {
  405         if(primaryKeyFieldName == null)
  406         {
  407            try
  408            {
  409               return primaryKeyClass.newInstance();
  410            }
  411            catch(Exception e)
  412            {
  413               throw new EJBException("Error creating primary key instance: ", e);
  414            }
  415         }
  416         return null;
  417      }
  418   
  419      public JDBCFieldBridge[] getPrimaryKeyFields()
  420      {
  421         return primaryKeyFields;
  422      }
  423   
  424      /**
  425       * This method is called only at deployment time, not called at runtime.
  426       * @return the list of all the fields.
  427       */
  428      public List getFields()
  429      {
  430         int fieldsTotal = primaryKeyFields.length + cmpFields.length + cmrFields.length;
  431         JDBCFieldBridge[] fields = new JDBCFieldBridge[fieldsTotal];
  432         int position = 0;
  433         // primary key fields
  434         System.arraycopy(primaryKeyFields, 0, fields, position, primaryKeyFields.length);
  435         position += primaryKeyFields.length;
  436         // cmp fields
  437         System.arraycopy(cmpFields, 0, fields, position, cmpFields.length);
  438         position += cmpFields.length;
  439         // cmr fields
  440         System.arraycopy(cmrFields, 0, fields, position, cmrFields.length);
  441         return Arrays.asList(fields);
  442      }
  443   
  444      public FieldBridge getFieldByName(String name)
  445      {
  446         FieldBridge field = null;
  447         for(int i = 0; i < primaryKeyFields.length; ++i)
  448         {
  449            JDBCCMPFieldBridge primaryKeyField = primaryKeyFields[i];
  450            if(primaryKeyField.getFieldName().equals(name))
  451            {
  452               field = primaryKeyField;
  453               break;
  454            }
  455         }
  456         if(field == null)
  457         {
  458            field = getCMPFieldByName(name);
  459         }
  460         if(field == null)
  461         {
  462            field = getCMRFieldByName(name);
  463         }
  464         return field;
  465      }
  466   
  467      public boolean[] getEagerLoadMask()
  468      {
  469         return eagerLoadGroupMask;
  470      }
  471   
  472      public Iterator getLazyLoadGroupMasks()
  473      {
  474         return lazyLoadGroupMasks.iterator();
  475      }
  476   
  477      public boolean[] getLoadGroupMask(String name)
  478      {
  479         boolean[] mask = (boolean[])loadGroupMasks.get(name);
  480         if(mask == null)
  481         {
  482            throw new IllegalStateException(
  483               "Load group '" + name + "' is not defined. Defined load groups: " + loadGroupMasks.keySet()
  484            );
  485         }
  486         return mask;
  487      }
  488   
  489      public FieldIterator getLoadIterator(JDBCCMPFieldBridge requiredField,
  490                                           JDBCReadAheadMetaData readahead,
  491                                           EntityEnterpriseContext ctx)
  492      {
  493         boolean[] loadGroup;
  494         if(requiredField == null)
  495         {
  496            if(readahead != null && !readahead.isNone())
  497            {
  498               if(log.isTraceEnabled())
  499               {
  500                  log.trace("Eager-load for entity: readahead=" + readahead);
  501               }
  502               loadGroup = getLoadGroupMask(readahead.getEagerLoadGroup());
  503            }
  504            else
  505            {
  506               if(log.isTraceEnabled())
  507               {
  508                  log.trace("Default eager-load for entity: readahead=" + readahead);
  509               }
  510               loadGroup = eagerLoadGroupMask;
  511            }
  512         }
  513         else
  514         {
  515            loadGroup = new boolean[tableFields.length];
  516            int requiredInd = requiredField.getTableIndex();
  517            loadGroup[requiredInd] = true;
  518            for(Iterator groups = lazyLoadGroupMasks.iterator(); groups.hasNext();)
  519            {
  520               boolean[] lazyGroup = (boolean[])groups.next();
  521               if(lazyGroup[requiredInd])
  522               {
  523                  for(int i = 0; i < loadGroup.length; ++i)
  524                     loadGroup[i] = loadGroup[i] || lazyGroup[i];
  525               }
  526            }
  527         }
  528   
  529         FieldIterator loadIter;
  530         if(loadGroup != null)
  531         {
  532            // filter
  533            int fieldsToLoad = 0;
  534            EntityState entityState = getEntityState(ctx);
  535            for(int i = 0; i < tableFields.length; ++i)
  536            {
  537               JDBCCMPFieldBridge field = tableFields[i];
  538               if(loadGroup[i] && !field.isPrimaryKeyMember() && !field.isLoaded(ctx))
  539               {
  540                  entityState.setLoadRequired(i);
  541                  ++fieldsToLoad;
  542               }
  543            }
  544            loadIter = (fieldsToLoad > 0 ? entityState.getLoadIterator(ctx) : EMPTY_FIELD_ITERATOR);
  545         }
  546         else
  547         {
  548            loadIter = EMPTY_FIELD_ITERATOR;
  549         }
  550         return loadIter;
  551      }
  552   
  553      /**
  554       * @param name  CMP field name
  555       * @return  JDBCCMPFieldBridge instance or null if no field found.
  556       */
  557      public JDBCCMPFieldBridge getCMPFieldByName(String name)
  558      {
  559         for(int i = 0; i < primaryKeyFields.length; ++i)
  560         {
  561            JDBCCMPFieldBridge cmpField = primaryKeyFields[i];
  562            if(cmpField.getFieldName().equals(name))
  563               return cmpField;
  564         }
  565         for(int i = 0; i < cmpFields.length; ++i)
  566         {
  567            JDBCCMPFieldBridge cmpField = cmpFields[i];
  568            if(cmpField.getFieldName().equals(name))
  569               return cmpField;
  570         }
  571         return null;
  572      }
  573   
  574      public JDBCAbstractCMRFieldBridge[] getCMRFields()
  575      {
  576         return cmrFields;
  577      }
  578   
  579      public JDBCCMRFieldBridge getCMRFieldByName(String name)
  580      {
  581         for(int i = 0; i < cmrFields.length; ++i)
  582         {
  583            JDBCCMRFieldBridge cmrField = cmrFields[i];
  584            if(cmrField.getFieldName().equals(name))
  585               return cmrField;
  586         }
  587         return null;
  588      }
  589   
  590      public JDBCCMPFieldBridge getVersionField()
  591      {
  592         return versionField;
  593      }
  594   
  595      public JDBCCMPFieldBridge getCreatedPrincipalField()
  596      {
  597         return createdPrincipalField;
  598      }
  599   
  600      public JDBCCMPFieldBridge getCreatedTimeField()
  601      {
  602         return createdTimeField;
  603      }
  604   
  605      public JDBCCMPFieldBridge getUpdatedPrincipalField()
  606      {
  607         return updatedPrincipalField;
  608      }
  609   
  610      public JDBCCMPFieldBridge getUpdatedTimeField()
  611      {
  612         return updatedTimeField;
  613      }
  614   
  615      public Collection getSelectors()
  616      {
  617         return selectorsByMethod.values();
  618      }
  619   
  620      public void initInstance(EntityEnterpriseContext ctx)
  621      {
  622         for(int i = 0; i < tableFields.length; ++i)
  623            tableFields[i].initInstance(ctx);
  624         //for(int i = 0; i < primaryKeyFields.length; ++i)
  625         //   primaryKeyFields[i].initInstance(ctx);
  626         //for(int i = 0; i < cmpFields.length; ++i)
  627         //   cmpFields[i].initInstance(ctx);
  628         for(int i = 0; i < cmrFields.length; ++i)
  629         {
  630            JDBCCMRFieldBridge cmrField = cmrFields[i];
  631            cmrField.initInstance(ctx);
  632         }
  633      }
  634   
  635      public static boolean isEjbCreateDone(EntityEnterpriseContext ctx)
  636      {
  637         return getEntityState(ctx).ejbCreateDone;
  638      }
  639   
  640      public static void setCreated(EntityEnterpriseContext ctx)
  641      {
  642         getEntityState(ctx).setCreated();
  643      }
  644   
  645      public static void setEjbCreateDone(EntityEnterpriseContext ctx)
  646      {
  647         getEntityState(ctx).ejbCreateDone = true;
  648      }
  649   
  650      /**
  651       * This method is used to determined whether the instance was modified.
  652       * NOTE, even if the method returns true the isStoreRequired for this same instance
  653       * might return false, e.g. a CMR field that doesn't have a foreign key was modified.
  654       * @param ctx
  655       * @return
  656       */
  657      public boolean isModified(EntityEnterpriseContext ctx)
  658      {
  659         boolean invalidateCache = false;
  660         final EntityState entityState = getEntityState(ctx);
  661         if(entityState.isCreated())
  662         {
  663            invalidateCache = areCmpFieldsDirty(ctx, entityState);
  664            if(!invalidateCache)
  665            {
  666               for(int i = 0; i < cmrFields.length; ++i)
  667               {
  668                  if(cmrFields[i].invalidateCache(ctx))
  669                  {
  670                     invalidateCache = true;
  671                     break;
  672                  }
  673               }
  674            }
  675         }
  676         return invalidateCache;
  677      }
  678   
  679      public boolean isStoreRequired(EntityEnterpriseContext ctx)
  680      {
  681         boolean modified = false;
  682         final EntityState entityState = getEntityState(ctx);
  683         if(entityState.isCreated())
  684         {
  685            modified = areCmpFieldsDirty(ctx, entityState);
  686            if(!modified)
  687            {
  688               for(int i = 0; i < cmrFields.length; ++i)
  689               {
  690                  if(cmrFields[i].isDirty(ctx))
  691                  {
  692                     modified = true;
  693                     break;
  694                  }
  695               }
  696            }
  697         }
  698         return modified;
  699      }
  700   
  701      private boolean areCmpFieldsDirty(final EntityEnterpriseContext ctx,
  702                                        final EntityState entityState)
  703      {
  704         for(int i = 0; i < tableFields.length; ++i)
  705         {
  706            final JDBCCMPFieldBridge field = tableFields[i];
  707            if(entityState.isCheckDirty(i) && field.isDirty(ctx))
  708            {
  709               return true;
  710            }
  711         }
  712         return false;
  713      }
  714   
  715      public FieldIterator getDirtyIterator(EntityEnterpriseContext ctx)
  716      {
  717         int dirtyFields = 0;
  718         final EntityState entityState = getEntityState(ctx);
  719         for(int i = 0; i < tableFields.length; ++i)
  720         {
  721            JDBCCMPFieldBridge field = tableFields[i];
  722            if(entityState.isCheckDirty(i) && field.isDirty(ctx))
  723            {
  724               entityState.setUpdateRequired(i);
  725               ++dirtyFields;
  726            }
  727         }
  728   
  729         return dirtyFields > 0 ? getEntityState(ctx).getDirtyIterator(ctx) : EMPTY_FIELD_ITERATOR;
  730      }
  731   
  732      public boolean hasLockedFields(EntityEnterpriseContext ctx)
  733      {
  734         return getEntityState(ctx).hasLockedFields();
  735      }
  736   
  737      public FieldIterator getLockedIterator(EntityEnterpriseContext ctx)
  738      {
  739         return getEntityState(ctx).getLockedIterator(ctx);
  740      }
  741   
  742      public void initPersistenceContext(EntityEnterpriseContext ctx)
  743      {
  744         // If we have an EJB 2.0 dynaymic proxy,
  745         // notify the handler of the assigned context.
  746         Object instance = ctx.getInstance();
  747         if(instance instanceof Proxies.ProxyTarget)
  748         {
  749            InvocationHandler handler = ((Proxies.ProxyTarget)instance).getInvocationHandler();
  750            if(handler instanceof EntityBridgeInvocationHandler)
  751               ((EntityBridgeInvocationHandler)handler).setContext(ctx);
  752         }
  753         ctx.setPersistenceContext(new JDBCContext(jdbcContextSize, new EntityState()));
  754      }
  755   
  756      /**
  757       * This is only called in commit option B
  758       */
  759      public void resetPersistenceContext(EntityEnterpriseContext ctx)
  760      {
  761         for(int i = 0; i < primaryKeyFields.length; ++i)
  762            primaryKeyFields[i].resetPersistenceContext(ctx);
  763         for(int i = 0; i < cmpFields.length; ++i)
  764            cmpFields[i].resetPersistenceContext(ctx);
  765         for(int i = 0; i < cmrFields.length; ++i)
  766            cmrFields[i].resetPersistenceContext(ctx);
  767      }
  768   
  769   
  770      public static void destroyPersistenceContext(EntityEnterpriseContext ctx)
  771      {
  772         // If we have an EJB 2.0 dynaymic proxy,
  773         // notify the handler of the assigned context.
  774         Object instance = ctx.getInstance();
  775         if(instance instanceof Proxies.ProxyTarget)
  776         {
  777            InvocationHandler handler = ((Proxies.ProxyTarget)instance).getInvocationHandler();
  778            if(handler instanceof EntityBridgeInvocationHandler)
  779               ((EntityBridgeInvocationHandler)handler).setContext(null);
  780         }
  781         ctx.setPersistenceContext(null);
  782      }
  783   
  784      //
  785      // Commands to handle primary keys
  786      //
  787   
  788      public int setPrimaryKeyParameters(PreparedStatement ps, int parameterIndex, Object primaryKey)
  789      {
  790         for(int i = 0; i < primaryKeyFields.length; ++i)
  791            parameterIndex = primaryKeyFields[i].setPrimaryKeyParameters(ps, parameterIndex, primaryKey);
  792         return parameterIndex;
  793      }
  794   
  795      public int loadPrimaryKeyResults(ResultSet rs, int parameterIndex, Object[] pkRef)
  796      {
  797         pkRef[0] = createPrimaryKeyInstance();
  798         for(int i = 0; i < primaryKeyFields.length; ++i)
  799            parameterIndex = primaryKeyFields[i].loadPrimaryKeyResults(rs, parameterIndex, pkRef);
  800         return parameterIndex;
  801      }
  802   
  803      public Object extractPrimaryKeyFromInstance(EntityEnterpriseContext ctx)
  804      {
  805         try
  806         {
  807            Object pk = null;
  808            for(int i = 0; i < primaryKeyFields.length; ++i)
  809            {
  810               JDBCCMPFieldBridge pkField = primaryKeyFields[i];
  811               Object fieldValue = pkField.getInstanceValue(ctx);
  812   
  813               // updated pk object with return form set primary key value to
  814               // handle single valued non-composit pks and more complicated behivors.
  815               pk = pkField.setPrimaryKeyValue(pk, fieldValue);
  816            }
  817            return pk;
  818         }
  819         catch(EJBException e)
  820         {
  821            // to avoid double wrap of EJBExceptions
  822            throw e;
  823         }
  824         catch(Exception e)
  825         {
  826            // Non recoverable internal exception
  827            throw new EJBException("Internal error extracting primary key from " +
  828               "instance", e);
  829         }
  830      }
  831   
  832      public void injectPrimaryKeyIntoInstance(EntityEnterpriseContext ctx, Object pk)
  833      {
  834         for(int i = 0; i < primaryKeyFields.length; ++i)
  835         {
  836            JDBCCMPFieldBridge pkField = primaryKeyFields[i];
  837            Object fieldValue = pkField.getPrimaryKeyValue(pk);
  838            pkField.setInstanceValue(ctx, fieldValue);
  839         }
  840      }
  841   
  842      int getNextJDBCContextIndex()
  843      {
  844         return jdbcContextSize++;
  845      }
  846   
  847      int addTableField(JDBCCMPFieldBridge field)
  848      {
  849         JDBCCMPFieldBridge[] tmpFields = tableFields;
  850         if(tableFields == null)
  851         {
  852            tableFields = new JDBCCMPFieldBridge[1];
  853         }
  854         else
  855         {
  856            tableFields = new JDBCCMPFieldBridge[tableFields.length + 1];
  857            System.arraycopy(tmpFields, 0, tableFields, 0, tmpFields.length);
  858         }
  859         int index = tableFields.length - 1;
  860         tableFields[index] = field;
  861   
  862         return index;
  863      }
  864   
  865      public JDBCFieldBridge[] getTableFields()
  866      {
  867         return tableFields;
  868      }
  869   
  870      /**
  871       * Marks the context as removed.
  872       * @param ctx instance's context
  873       */
  874      public void setRemoved(EntityEnterpriseContext ctx)
  875      {
  876         getEntityState(ctx).setRemoved();
  877      }
  878   
  879      /**
  880       * @param ctx instance's context.
  881       * @return true if instance was removed.
  882       */
  883      public boolean isRemoved(EntityEnterpriseContext ctx)
  884      {
  885         return getEntityState(ctx).isRemoved();
  886      }
  887   
  888      /**
  889       * Marks an instance as being removed
  890       */
  891      public void setIsBeingRemoved(EntityEnterpriseContext ctx)
  892      {
  893         getEntityState(ctx).setIsBeingRemoved();
  894      }
  895      
  896      /**
  897       * @param ctx instance's context.
  898       * @return true if instance is being removed.
  899       */
  900      public boolean isBeingRemoved(EntityEnterpriseContext ctx)
  901      {
  902         return getEntityState(ctx).isBeingRemoved();
  903      }
  904   
  905      /**
  906       * Marks the instance as scheduled for cascade delete (not for batch cascade delete)
  907       * @param ctx instance's context.
  908       */
  909      public void scheduleForCascadeDelete(EntityEnterpriseContext ctx)
  910      {
  911         getEntityState(ctx).scheduleForCascadeDelete();
  912         if(log.isTraceEnabled())
  913            log.trace("Scheduled for cascade-delete: " + ctx.getId());
  914      }
  915   
  916      /**
  917       * @param ctx instance's context.
  918       * @return true if instance was scheduled for cascade delete (not for batch cascade delete)
  919       */
  920      public boolean isScheduledForCascadeDelete(EntityEnterpriseContext ctx)
  921      {
  922         return getEntityState(ctx).isScheduledForCascadeDelete();
  923      }
  924   
  925      /**
  926       * Marks the instance as scheduled for batch cascade delete (not for cascade delete)
  927       * @param ctx instance's context.
  928       */
  929      public void scheduleForBatchCascadeDelete(EntityEnterpriseContext ctx)
  930      {
  931         getEntityState(ctx).scheduleForBatchCascadeDelete();
  932         if(log.isTraceEnabled())
  933            log.trace("Scheduled for batch-cascade-delete: " + ctx.getId());
  934      }
  935   
  936      /**
  937       * @param ctx instance's context.
  938       * @return true if instance was scheduled for batch cascade delete (not for cascade delete)
  939       */
  940      public boolean isScheduledForBatchCascadeDelete(EntityEnterpriseContext ctx)
  941      {
  942         return getEntityState(ctx).isScheduledForBatchCascadeDelete();
  943      }
  944   
  945      private static EntityState getEntityState(EntityEnterpriseContext ctx)
  946      {
  947         JDBCContext jdbcCtx = (JDBCContext)ctx.getPersistenceContext();
  948         EntityState entityState = jdbcCtx.getEntityState();
  949         if(entityState == null)
  950            throw new IllegalStateException("Entity state is null.");
  951         return entityState;
  952      }
  953   
  954      private void loadCMPFields(JDBCEntityMetaData metadata)
  955         throws DeploymentException
  956      {
  957         // only non pk fields are stored here at first and then later
  958         // the pk fields are added to the front (makes sql easier to read)
  959         List cmpFieldsMD = metadata.getCMPFields();
  960         List cmpFieldsList = new ArrayList(cmpFieldsMD.size());
  961         // primary key cmp fields
  962         List pkFieldsList = new ArrayList(cmpFieldsMD.size());
  963   
  964         // create pk fields
  965         for(int i = 0; i < cmpFieldsMD.size(); ++i)
  966         {
  967            JDBCCMPFieldMetaData fieldMD = (JDBCCMPFieldMetaData)cmpFieldsMD.get(i);
  968            if(fieldMD.isPrimaryKeyMember())
  969            {
  970               JDBCCMPFieldBridge cmpField = createCMPField(metadata, fieldMD);
  971               pkFieldsList.add(cmpField);
  972            }
  973         }
  974   
  975         // create non-pk cmp fields
  976         for(int i = 0; i < cmpFieldsMD.size(); ++i)
  977         {
  978            JDBCCMPFieldMetaData fieldMD = (JDBCCMPFieldMetaData)cmpFieldsMD.get(i);
  979            if(!fieldMD.isPrimaryKeyMember())
  980            {
  981               JDBCCMPFieldBridge cmpField = createCMPField(metadata, fieldMD);
  982               cmpFieldsList.add(cmpField);
  983            }
  984         }
  985   
  986         // save the pk fields in the pk field array
  987         primaryKeyFields = new JDBCCMPFieldBridge[pkFieldsList.size()];
  988         for(int i = 0; i < pkFieldsList.size(); ++i)
  989            primaryKeyFields[i] = (JDBCCMPFieldBridge)pkFieldsList.get(i);
  990   
  991         // add the pk fields to the front of the cmp list, per guarantee above
  992         cmpFields = new JDBCCMPFieldBridge[cmpFieldsMD.size() - primaryKeyFields.length];
  993         int cmpFieldIndex = 0;
  994         for(int i = 0; i < cmpFieldsList.size(); ++i)
  995            cmpFields[cmpFieldIndex++] = (JDBCCMPFieldBridge)cmpFieldsList.get(i);
  996      }
  997   
  998      private void loadCMRFields(JDBCEntityMetaData metadata)
  999         throws DeploymentException
 1000      {
 1001         cmrFields = new JDBCCMRFieldBridge[metadata.getRelationshipRoles().size()];
 1002         // create each field
 1003         int cmrFieldIndex = 0;
 1004         for(Iterator iter = metadata.getRelationshipRoles().iterator(); iter.hasNext();)
 1005         {
 1006            JDBCRelationshipRoleMetaData relationshipRole = (JDBCRelationshipRoleMetaData)iter.next();
 1007            JDBCCMRFieldBridge cmrField = new JDBCCMRFieldBridge(this, manager, relationshipRole);
 1008            cmrFields[cmrFieldIndex++] = cmrField;
 1009         }
 1010      }
 1011   
 1012      private void loadLoadGroups(JDBCEntityMetaData metadata)
 1013         throws DeploymentException
 1014      {
 1015         loadGroupMasks = new HashMap();
 1016   
 1017         // load optimistic locking mask and add it to all the load group masks
 1018         JDBCOptimisticLockingMetaData olMD = metadata.getOptimisticLocking();
 1019         if(olMD != null)
 1020         {
 1021            if(versionField != null)
 1022            {
 1023               defaultLockGroupMask = new boolean[tableFields.length];
 1024               defaultLockGroupMask[versionField.getTableIndex()] = true;
 1025               versionField.setLockingStrategy(LockingStrategy.VERSION);
 1026            }
 1027            else if(olMD.getGroupName() != null)
 1028            {
 1029               defaultLockGroupMask = loadGroupMask(olMD.getGroupName(), null);
 1030               for(int i = 0; i < tableFields.length; ++i)
 1031               {
 1032                  if(defaultLockGroupMask[i])
 1033                  {
 1034                     JDBCCMPFieldBridge tableField = tableFields[i];
 1035                     tableField.setLockingStrategy(LockingStrategy.GROUP);
 1036                     tableField.addDefaultFlag(ADD_TO_WHERE_ON_UPDATE);
 1037                  }
 1038               }
 1039            }
 1040            else // read or modified strategy
 1041            {
 1042               LockingStrategy strategy =
 1043                  (olMD.getLockingStrategy() == JDBCOptimisticLockingMetaData.READ_STRATEGY ?
 1044                  LockingStrategy.READ : LockingStrategy.MODIFIED
 1045                  );
 1046               for(int i = 0; i < tableFields.length; ++i)
 1047               {
 1048                  JDBCCMPFieldBridge field = tableFields[i];
 1049                  if(!field.isPrimaryKeyMember())
 1050                     field.setLockingStrategy(strategy);
 1051               }
 1052            }
 1053         }
 1054   
 1055         // add the * load group
 1056         boolean[] defaultLoadGroup = new boolean[tableFields.length];
 1057         Arrays.fill(defaultLoadGroup, true);
 1058         for(int i = 0; i < primaryKeyFields.length; ++i)
 1059         {
 1060            int tableIndex = primaryKeyFields[i].getTableIndex();
 1061            defaultLoadGroup[tableIndex] = false;
 1062         }
 1063         loadGroupMasks.put(DEFAULT_LOADGROUP_NAME, defaultLoadGroup);
 1064   
 1065         // put each group in the load groups map by group name
 1066         Iterator groupNames = metadata.getLoadGroups().keySet().iterator();
 1067         while(groupNames.hasNext())
 1068         {
 1069            // get the group name
 1070            String groupName = (String)groupNames.next();
 1071            boolean[] loadGroup = loadGroupMask(groupName, defaultLockGroupMask);
 1072            loadGroupMasks.put(groupName, loadGroup);
 1073         }
 1074         loadGroupMasks = Collections.unmodifiableMap(loadGroupMasks);
 1075      }
 1076   
 1077      private boolean[] loadGroupMask(String groupName, boolean[] defaultGroup)
 1078         throws DeploymentException
 1079      {
 1080         List fieldNames = metadata.getLoadGroup(groupName);
 1081         boolean[] group = new boolean[tableFields.length];
 1082         if(defaultGroup != null)
 1083            System.arraycopy(defaultGroup, 0, group, 0, group.length);
 1084         for(Iterator iter = fieldNames.iterator(); iter.hasNext();)
 1085         {
 1086            String fieldName = (String)iter.next();
 1087            JDBCFieldBridge field = (JDBCFieldBridge)getFieldByName(fieldName);
 1088            if(field == null)
 1089               throw new DeploymentException(
 1090                  "Field " + fieldName + " not found for entity " + getEntityName());
 1091   
 1092            if(field instanceof JDBCCMRFieldBridge)
 1093            {
 1094               JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge)field;
 1095               if(cmrField.hasForeignKey())
 1096               {
 1097                  JDBCCMPFieldBridge[] fkFields = (JDBCCMPFieldBridge[]) cmrField.getForeignKeyFields();
 1098                  for(int i = 0; i < fkFields.length; ++i)
 1099                  {
 1100                     group[fkFields[i].getTableIndex()] = true;
 1101                  }
 1102               }
 1103               else
 1104               {
 1105                  throw new DeploymentException("Only CMR fields that have " +
 1106                     "a foreign-key may be a member of a load group: " +
 1107                     "fieldName=" + fieldName);
 1108               }
 1109            }
 1110            else
 1111            {
 1112               group[((JDBCCMPFieldBridge)field).getTableIndex()] = true;
 1113            }
 1114         }
 1115         return group;
 1116      }
 1117   
 1118      private void loadEagerLoadGroup(JDBCEntityMetaData metadata)
 1119      {
 1120         String eagerLoadGroupName = metadata.getEagerLoadGroup();
 1121         if(eagerLoadGroupName == null)
 1122         {
 1123            // can be null in case of <eager-load-group/>, meaning empty load group
 1124            eagerLoadGroupMask = defaultLockGroupMask;
 1125         }
 1126         else
 1127            eagerLoadGroupMask = (boolean[])loadGroupMasks.get(eagerLoadGroupName);
 1128      }
 1129   
 1130      private void loadLazyLoadGroups(JDBCEntityMetaData metadata)
 1131      {
 1132         List lazyGroupNames = metadata.getLazyLoadGroups();
 1133         lazyLoadGroupMasks = new ArrayList(lazyGroupNames.size());
 1134         for(Iterator lazyLoadGroupNames = lazyGroupNames.iterator(); lazyLoadGroupNames.hasNext();)
 1135         {
 1136            String lazyLoadGroupName = (String)lazyLoadGroupNames.next();
 1137            lazyLoadGroupMasks.add(loadGroupMasks.get(lazyLoadGroupName));
 1138         }
 1139         lazyLoadGroupMasks = Collections.unmodifiableList(lazyLoadGroupMasks);
 1140      }
 1141   
 1142      private JDBCCMPFieldBridge createCMPField(JDBCEntityMetaData metadata,
 1143                                                JDBCCMPFieldMetaData cmpFieldMetaData)
 1144         throws DeploymentException
 1145      {
 1146         JDBCCMPFieldBridge cmpField;
 1147         if(metadata.isCMP1x())
 1148            cmpField = new JDBCCMP1xFieldBridge(manager, cmpFieldMetaData);
 1149         else
 1150            cmpField = new JDBCCMP2xFieldBridge(manager, cmpFieldMetaData);
 1151         return cmpField;
 1152      }
 1153   
 1154      private void loadSelectors(JDBCEntityMetaData metadata)
 1155      {
 1156         // Don't know if this is the best way to do this.  Another way would be
 1157         // to deligate seletors to the JDBCFindEntitiesCommand, but this is
 1158         // easier now.
 1159         selectorsByMethod = new HashMap(metadata.getQueries().size());
 1160         Iterator definedFinders = manager.getMetaData().getQueries().iterator();
 1161         while(definedFinders.hasNext())
 1162         {
 1163            JDBCQueryMetaData q = (JDBCQueryMetaData)definedFinders.next();
 1164            if(q.getMethod().getName().startsWith("ejbSelect"))
 1165               selectorsByMethod.put(q.getMethod(), new JDBCSelectorBridge(manager, q));
 1166         }
 1167         selectorsByMethod = Collections.unmodifiableMap(selectorsByMethod);
 1168      }
 1169   
 1170      private void addCMPField(JDBCCMPFieldBridge field)
 1171      {
 1172         JDBCCMPFieldBridge[] tmpCMPFields = cmpFields;
 1173         cmpFields = new JDBCCMPFieldBridge[cmpFields.length + 1];
 1174         System.arraycopy(tmpCMPFields, 0, cmpFields, 0, tmpCMPFields.length);
 1175         cmpFields[tmpCMPFields.length] = field;
 1176      }
 1177   
 1178      public class EntityState
 1179      {
 1180         private static final byte REMOVED = 1;
 1181         private static final byte SCHEDULED_FOR_CASCADE_DELETE = 2;
 1182         private static final byte SCHEDULED_FOR_BATCH_CASCADE_DELETE = 4;
 1183         private static final byte IS_BEING_REMOVED = 8;
 1184   
 1185         /** indicates whether ejbCreate method was executed */
 1186         private boolean ejbCreateDone = false;
 1187         /** indicates whether ejbPostCreate method was executed */
 1188         private boolean ejbPostCreateDone = false;
 1189   
 1190         private byte entityFlags;
 1191   
 1192         /** array of field flags*/
 1193         private final byte[] fieldFlags = new byte[tableFields.length];
 1194   
 1195         public EntityState()
 1196         {
 1197            for(int i = 0; i < tableFields.length; ++i)
 1198            {
 1199               fieldFlags[i] = tableFields[i].getDefaultFlags();
 1200            }
 1201         }
 1202   
 1203         public void setRemoved()
 1204         {
 1205            entityFlags |= REMOVED;
 1206            entityFlags &= ~(SCHEDULED_FOR_CASCADE_DELETE | SCHEDULED_FOR_BATCH_CASCADE_DELETE | IS_BEING_REMOVED);
 1207         }
 1208   
 1209         public boolean isRemoved()
 1210         {
 1211            return (entityFlags & REMOVED) > 0;
 1212         }
 1213   
 1214         public void setIsBeingRemoved()
 1215         {
 1216            entityFlags |= IS_BEING_REMOVED;
 1217         }
 1218   
 1219         public boolean isBeingRemoved()
 1220         {
 1221            return (entityFlags & IS_BEING_REMOVED) > 0;
 1222         }
 1223   
 1224         public void scheduleForCascadeDelete()
 1225         {
 1226            entityFlags |= SCHEDULED_FOR_CASCADE_DELETE;
 1227         }
 1228   
 1229         public boolean isScheduledForCascadeDelete()
 1230         {
 1231            return (entityFlags & SCHEDULED_FOR_CASCADE_DELETE) > 0;
 1232         }
 1233   
 1234         public void scheduleForBatchCascadeDelete()
 1235         {
 1236            entityFlags |= SCHEDULED_FOR_BATCH_CASCADE_DELETE | SCHEDULED_FOR_CASCADE_DELETE;
 1237         }
 1238   
 1239         public boolean isScheduledForBatchCascadeDelete()
 1240         {
 1241            return (entityFlags & SCHEDULED_FOR_BATCH_CASCADE_DELETE) > 0;
 1242         }
 1243   
 1244         public void setCreated()
 1245         {
 1246            ejbCreateDone = true;
 1247            ejbPostCreateDone = true;
 1248         }
 1249   
 1250         public boolean isCreated()
 1251         {
 1252            return ejbCreateDone && ejbPostCreateDone;
 1253         }
 1254   
 1255         /**
 1256          * @param fieldIndex  index of the field
 1257          * @return true if the field is loaded
 1258          */
 1259         public boolean isLoaded(int fieldIndex)
 1260         {
 1261            return (fieldFlags[fieldIndex] & LOADED) > 0;
 1262         }
 1263   
 1264         /**
 1265          * Marks the field as loaded.
 1266          * @param fieldIndex  index of the field.
 1267          */
 1268         public void setLoaded(int fieldIndex)
 1269         {
 1270            fieldFlags[fieldIndex] |= LOADED;
 1271         }
 1272   
 1273         /**
 1274          * Marks the field to be loaded.
 1275          * @param fieldIndex  index of the field.
 1276          */
 1277         public void setLoadRequired(int fieldIndex)
 1278         {
 1279            fieldFlags[fieldIndex] |= LOAD_REQUIRED;
 1280         }
 1281   
 1282         /**
 1283          * Marks the field to be updated.
 1284          * @param fieldIndex  index of the field.
 1285          */
 1286         public void setUpdateRequired(int fieldIndex)
 1287         {
 1288            fieldFlags[fieldIndex] |= DIRTY;
 1289         }
 1290   
 1291         /**
 1292          * The field will be checked for dirty state at commit.
 1293          * @param fieldIndex  index of the field.
 1294          */
 1295         public void setCheckDirty(int fieldIndex)
 1296         {
 1297            fieldFlags[fieldIndex] |= CHECK_DIRTY;
 1298         }
 1299   
 1300         /**
 1301          * @param fieldIndex the index of the field that should be checked for dirty state.
 1302          * @return true if the field should be checked for dirty state.
 1303          */
 1304         public boolean isCheckDirty(int fieldIndex)
 1305         {
 1306            return (fieldFlags[fieldIndex] & CHECK_DIRTY) > 0;
 1307         }
 1308   
 1309         /**
 1310          * Marks the field as clean.
 1311          * @param fieldIndex  nextIndex of the field.
 1312          */
 1313         public void setClean(int fieldIndex)
 1314         {
 1315            fieldFlags[fieldIndex] &= ~(CHECK_DIRTY | DIRTY | LOCKED);
 1316         }
 1317   
 1318         /**
 1319          * Resets field flags.
 1320          * @param fieldIndex  nextIndex of the field.
 1321          */
 1322         public void resetFlags(int fieldIndex)
 1323         {
 1324            fieldFlags[fieldIndex] = tableFields[fieldIndex].getDefaultFlags();
 1325         }
 1326   
 1327         public FieldIterator getDirtyIterator(EntityEnterpriseContext ctx)
 1328         {
 1329            return new MaskFieldIterator((byte)(DIRTY | ADD_TO_SET_ON_UPDATE));
 1330         }
 1331   
 1332         public boolean hasLockedFields()
 1333         {
 1334            boolean result = false;
 1335            for(int i = 0; i < fieldFlags.length; ++i)
 1336            {
 1337               if((fieldFlags[i] & (LOCKED | ADD_TO_WHERE_ON_UPDATE)) > 0)
 1338               {
 1339                  result = true;
 1340                  break;
 1341               }
 1342            }
 1343            return result;
 1344         }
 1345   
 1346         public FieldIterator getLockedIterator(EntityEnterpriseContext ctx)
 1347         {
 1348            return new MaskFieldIterator((byte)(LOCKED | ADD_TO_WHERE_ON_UPDATE));
 1349         }
 1350   
 1351         public boolean lockValue(int fieldIndex)
 1352         {
 1353            boolean lock = false;
 1354            byte fieldFlag = fieldFlags[fieldIndex];
 1355            if((fieldFlag & LOADED) > 0 && (fieldFlag & LOCKED) == 0)
 1356            {
 1357               fieldFlags[fieldIndex] |= LOCKED;
 1358               lock = true;
 1359            }
 1360            return lock;
 1361         }
 1362   
 1363         public FieldIterator getLoadIterator(EntityEnterpriseContext ctx)
 1364         {
 1365            return new MaskFieldIterator(LOAD_REQUIRED);
 1366         }
 1367   
 1368         // Inner
 1369   
 1370         private class MaskFieldIterator implements FieldIterator
 1371         {
 1372            private final byte flagMask;
 1373            private int nextIndex = 0;
 1374            private int curIndex = -1;
 1375   
 1376            public MaskFieldIterator(byte flagMask)
 1377            {
 1378               this.flagMask = flagMask;
 1379            }
 1380   
 1381            public boolean hasNext()
 1382            {
 1383               while(nextIndex < fieldFlags.length)
 1384               {
 1385                  if((fieldFlags[nextIndex] & flagMask) > 0)
 1386                  {
 1387                     return true;
 1388                  }
 1389   
 1390                  ++nextIndex;
 1391               }
 1392   
 1393               return false;
 1394            }
 1395   
 1396            public JDBCCMPFieldBridge next()
 1397            {
 1398               if(!hasNext())
 1399                  throw new NoSuchElementException();
 1400               curIndex = nextIndex;
 1401               return tableFields[nextIndex++];
 1402            }
 1403   
 1404            public void remove()
 1405            {
 1406               fieldFlags[curIndex] &= ~flagMask;
 1407            }
 1408   
 1409            public void removeAll()
 1410            {
 1411               int inversedMask = ~flagMask;
 1412               for(int i = 0; i < fieldFlags.length; ++i)
 1413                  fieldFlags[i] &= inversedMask;
 1414            }
 1415   
 1416            public void reset()
 1417            {
 1418               nextIndex = 0;
 1419               curIndex = -1;
 1420            }
 1421         }
 1422      }
 1423   
 1424      public static final FieldIterator EMPTY_FIELD_ITERATOR = new FieldIterator()
 1425      {
 1426         public boolean hasNext()
 1427         {
 1428            return false;
 1429         }
 1430   
 1431         public JDBCCMPFieldBridge next()
 1432         {
 1433            throw new NoSuchElementException();
 1434         }
 1435   
 1436         public void remove()
 1437         {
 1438            throw new UnsupportedOperationException();
 1439         }
 1440   
 1441         public void removeAll()
 1442         {
 1443            throw new UnsupportedOperationException();
 1444         }
 1445   
 1446         public void reset()
 1447         {
 1448         }
 1449      };
 1450   
 1451      public static interface FieldIterator
 1452      {
 1453         /**
 1454          * @return true if there are more fields to iterate through.
 1455          */
 1456         boolean hasNext();
 1457   
 1458         /**
 1459          * @return the next field.
 1460          */
 1461         JDBCCMPFieldBridge next();
 1462   
 1463         /**
 1464          * Removes the current field from the iterator (not from the underlying array or another source)
 1465          */
 1466         void remove();
 1467   
 1468         /**
 1469          * Removes all the fields from the iterator (not from the underlying array or another source).
 1470          */
 1471         void removeAll();
 1472   
 1473         /**
 1474          * Resets the current position to the first field.
 1475          */
 1476         void reset();
 1477      }
 1478   }

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