Java Source Code: org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCCMRFieldBridge2


   1: /*
   2:  * JBoss, the OpenSource J2EE webOS
   3:  *
   4:  * Distributable under LGPL license.
   5:  * See terms of license at gnu.org.
   6:  */
   7: package org.jboss.ejb.plugins.cmp.jdbc2.bridge;
   8: 
   9: import org.jboss.ejb.plugins.cmp.bridge.EntityBridge;
  10: import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
  11: import org.jboss.ejb.plugins.cmp.jdbc2.JDBCStoreManager2;
  12: import org.jboss.ejb.plugins.cmp.jdbc2.schema.EntityTable;
  13: import org.jboss.ejb.plugins.cmp.jdbc2.schema.RelationTable;
  14: import org.jboss.ejb.plugins.cmp.jdbc2.PersistentContext;
  15: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
  16: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
  17: import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil;
  18: import org.jboss.ejb.plugins.cmp.jdbc.JDBCType;
  19: import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
  20: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
  21: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge;
  22: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
  23: import org.jboss.ejb.plugins.cmp.jdbc.bridge.CMRInvocation;
  24: import org.jboss.ejb.plugins.cmp.jdbc.bridge.CMRMessage;
  25: import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
  26: import org.jboss.ejb.plugins.lock.Entrancy;
  27: import org.jboss.ejb.EntityEnterpriseContext;
  28: import org.jboss.ejb.EntityContainer;
  29: import org.jboss.ejb.EntityCache;
  30: import org.jboss.ejb.LocalProxyFactory;
  31: import org.jboss.deployment.DeploymentException;
  32: import org.jboss.logging.Logger;
  33: import org.jboss.security.SecurityAssociation;
  34: import org.jboss.invocation.InvocationType;
  35: 
  36: import javax.ejb.EJBException;
  37: import javax.ejb.EJBLocalObject;
  38: import javax.ejb.RemoveException;
  39: import javax.transaction.Transaction;
  40: import javax.transaction.TransactionManager;
  41: import javax.transaction.SystemException;
  42: import java.util.Collection;
  43: import java.util.List;
  44: import java.util.ArrayList;
  45: import java.util.Map;
  46: import java.util.HashMap;
  47: import java.util.Iterator;
  48: import java.util.Set;
  49: import java.util.Collections;
  50: import java.util.HashSet;
  51: import java.util.ConcurrentModificationException;
  52: import java.sql.PreparedStatement;
  53: import java.sql.Connection;
  54: import java.sql.ResultSet;
  55: import java.sql.SQLException;
  56: import java.lang.reflect.Array;
  57: import java.security.AccessController;
  58: import java.security.PrivilegedAction;
  59: import java.security.Principal;
  60: 
  61: 
  62: /**
  63:  * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
  64:  * @version <tt>$Revision: 1.1.2.6 $</tt>
  65:  */
  66: public class JDBCCMRFieldBridge2
  67:    extends JDBCAbstractCMRFieldBridge
  68:	  {
  69:   private final JDBCRelationshipRoleMetaData metadata;
  70:   private final JDBCStoreManager2 manager;
  71:   private final JDBCEntityBridge2 entity;
  72:   private final int cmrIndex;
  73:   private final Logger log;
  74:
  75:   private JDBCEntityBridge2 relatedEntity;
  76:   private JDBCCMRFieldBridge2 relatedCMRField;
  77:   private EntityContainer relatedContainer;
  78:
  79:   private JDBCCMPFieldBridge2[] tableKeyFields;
  80:   private JDBCCMPFieldBridge2[] foreignKeyFields;
  81:   private JDBCCMPFieldBridge2[] relatedPKFields;
  82:
  83:   private CMRFieldLoader loader;
  84:   private RelationTable relationTable;
  85:
  86:   private final TransactionManager tm;
  87:
  88:   public JDBCCMRFieldBridge2(JDBCEntityBridge2 entityBridge,
  89:                              JDBCStoreManager2 manager,
  90:                              JDBCRelationshipRoleMetaData metadata)
  91:	     {
  92:      this.manager = manager;
  93:      this.entity = entityBridge;
  94:      this.metadata = metadata;
  95:      cmrIndex = entity.getNextCMRIndex();
  96:      tm = manager.getContainer().getTransactionManager();
  97:
  98:      log = Logger.getLogger(getClass().getName() + "." + entity.getEntityName() + "#" + getFieldName());
  99:   }
 100:
 101:   // Public
 102:
 103:   public void resolveRelationship() throws DeploymentException
 104:	     {
 105:      //
 106:      // Set handles to the related entity's container, cache, manager, and invoker
 107:      //
 108:
 109:      // Related Entity Name
 110:      String relatedEntityName = metadata.getRelatedRole().getEntity().getName();
 111:
 112:      // Related Entity
 113:      Catalog catalog = (Catalog) manager.getApplicationData("CATALOG");
 114:      relatedEntity = (JDBCEntityBridge2) catalog.getEntityByEJBName(relatedEntityName);
 115:      if(relatedEntity == null)
 116:	        {
 117:         throw new DeploymentException("Related entity not found: "
 118:            +
 119:            "entity="
 120:            +
 121:            entity.getEntityName()
 122:            +
 123:            ", "
 124:            +
 125:            "cmrField="
 126:            +
 127:            getFieldName()
 128:            +
 129:            ", " +
 130:            "relatedEntity=" + relatedEntityName);
 131:      }
 132:
 133:      // Related CMR Field
 134:      JDBCCMRFieldBridge2[] cmrFields = relatedEntity.getCMRFields();
 135:      for(int i = 0; i < cmrFields.length; ++i)
 136:	        {
 137:         JDBCCMRFieldBridge2 cmrField = cmrFields[i];
 138:         if(metadata.getRelatedRole() == cmrField.getMetaData())
 139:	           {
 140:            relatedCMRField = cmrField;
 141:            break;
 142:         }
 143:      }
 144:
 145:      // if we didn't find the related CMR field throw an exception with a detailed message
 146:      if(relatedCMRField == null)
 147:	        {
 148:         String message = "Related CMR field not found in " +
 149:            relatedEntity.getEntityName() + " for relationship from ";
 150:
 151:         message += entity.getEntityName() + ".";
 152:         if(getFieldName() != null)
 153:	           {
 154:            message += getFieldName();
 155:         }
 156:         else
 157:	           {
 158:            message += "<no-field>";
 159:         }
 160:
 161:         message += " to ";
 162:         message += relatedEntityName + ".";
 163:         if(metadata.getRelatedRole().getCMRFieldName() != null)
 164:	           {
 165:            message += metadata.getRelatedRole().getCMRFieldName();
 166:         }
 167:         else
 168:	           {
 169:            message += "<no-field>";
 170:         }
 171:
 172:         throw new DeploymentException(message);
 173:      }
 174:
 175:      // Related Container
 176:      relatedContainer = relatedEntity.getContainer();
 177:
 178:      //
 179:      // Initialize the key fields
 180:      //
 181:      if(metadata.getRelationMetaData().isTableMappingStyle())
 182:	        {
 183:         // initialize relation table key fields
 184:         Collection tableKeys = metadata.getKeyFields();
 185:         List keyFieldsList = new ArrayList(tableKeys.size());
 186:
 187:         // first phase is to create fk fields
 188:         Map pkFieldsToFKFields = new HashMap(tableKeys.size());
 189:         for(Iterator i = tableKeys.iterator(); i.hasNext();)
 190:	           {
 191:            JDBCCMPFieldMetaData cmpFieldMetaData = (JDBCCMPFieldMetaData) i.next();
 192:            FieldBridge pkField = entity.getFieldByName(cmpFieldMetaData.getFieldName());
 193:            if(pkField == null)
 194:	              {
 195:               throw new DeploymentException("Primary key not found for key-field " + cmpFieldMetaData.getFieldName());
 196:            }
 197:            pkFieldsToFKFields.put(pkField, new JDBCCMPFieldBridge2(manager, entity, cmpFieldMetaData, -1));
 198:         }
 199:
 200:         // second step is to order fk fields to match the order of pk fields
 201:         JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
 202:         for(int i = 0; i < pkFields.length; ++i)
 203:	           {
 204:            Object fkField = pkFieldsToFKFields.get(pkFields[i]);
 205:            if(fkField == null)
 206:	              {
 207:               throw new DeploymentException("Primary key " + pkFields[i].getFieldName() + " is not mapped.");
 208:            }
 209:            keyFieldsList.add(fkField);
 210:         }
 211:         tableKeyFields = (JDBCCMPFieldBridge2[]) keyFieldsList.toArray(new JDBCCMPFieldBridge2[keyFieldsList.size()]);
 212:      }
 213:      else
 214:	        {
 215:         initializeForeignKeyFields();
 216:      }
 217:   }
 218:
 219:   public void initLoader() throws DeploymentException
 220:	     {
 221:      if(metadata.getRelationMetaData().isTableMappingStyle())
 222:	        {
 223:         relationTable = relatedCMRField.getRelationTable();
 224:         loader = new RelationTableLoader();
 225:      }
 226:      else
 227:	        {
 228:         if(foreignKeyFields != null)
 229:	           {
 230:            loader = new ContextForeignKeyLoader();
 231:         }
 232:         else
 233:	           {
 234:            loader = new ForeignKeyLoader();
 235:         }
 236:      }
 237:   }
 238:
 239:   public JDBCRelationshipRoleMetaData getMetaData()
 240:	     {
 241:      return metadata;
 242:   }
 243:
 244:   public boolean removeRelatedId(EntityEnterpriseContext ctx, Object relatedId)
 245:	     {
 246:      FieldState state = getFieldState(ctx);
 247:      return state.removeRelatedId(ctx, relatedId);
 248:   }
 249:
 250:   public boolean addRelatedId(EntityEnterpriseContext ctx, Object relatedId)
 251:	     {
 252:      FieldState state = getFieldState(ctx);
 253:      return state.addRelatedId(ctx, relatedId);
 254:   }
 255:
 256:   public void remove(EntityEnterpriseContext ctx) throws RemoveException
 257:	     {
 258:      if(metadata.getRelatedRole().isCascadeDelete())
 259:	        {
 260:         FieldState state = getFieldState(ctx);
 261:         state.cascadeDelete(ctx);
 262:      }
 263:      else
 264:	        {
 265:         destroyExistingRelationships(ctx);
 266:      }
 267:   }
 268:
 269:   public void destroyExistingRelationships(EntityEnterpriseContext ctx)
 270:	     {
 271:      FieldState state = getFieldState(ctx);
 272:      state.destroyExistingRelationships(ctx);
 273:   }
 274:
 275:   public JDBCFieldBridge[] getTableKeyFields()
 276:	     {
 277:      return tableKeyFields;
 278:   }
 279:
 280:   public JDBCEntityPersistenceStore getManager()
 281:	     {
 282:      return manager;
 283:   }
 284:
 285:   public boolean hasForeignKey()
 286:	     {
 287:      return foreignKeyFields != null;
 288:   }
 289:
 290:   public JDBCAbstractCMRFieldBridge getRelatedCMRField()
 291:	     {
 292:      return this.relatedCMRField;
 293:   }
 294:
 295:   public JDBCFieldBridge[] getForeignKeyFields()
 296:	     {
 297:      return foreignKeyFields;
 298:   }
 299:
 300:   public JDBCCMRFieldBridge2 getRelatedField()
 301:	     {
 302:      return relatedCMRField;
 303:   }
 304:
 305:   public JDBCAbstractEntityBridge getEntity()
 306:	     {
 307:      return entity;
 308:   }
 309:
 310:   public String getTableName()
 311:	     {
 312:      throw new UnsupportedOperationException();
 313:   }
 314:
 315:   // JDBCFieldBridge implementation
 316:
 317:   public JDBCType getJDBCType()
 318:	     {
 319:      throw new UnsupportedOperationException();
 320:   }
 321:
 322:   public boolean isPrimaryKeyMember()
 323:	     {
 324:      throw new UnsupportedOperationException();
 325:   }
 326:
 327:   public boolean isReadOnly()
 328:	     {
 329:      throw new UnsupportedOperationException();
 330:   }
 331:
 332:   public boolean isReadTimedOut(EntityEnterpriseContext ctx)
 333:	     {
 334:      throw new UnsupportedOperationException();
 335:   }
 336:
 337:   public boolean isIndexed()
 338:	     {
 339:      throw new UnsupportedOperationException();
 340:   }
 341:
 342:   public boolean isLoaded(EntityEnterpriseContext ctx)
 343:	     {
 344:      throw new UnsupportedOperationException();
 345:   }
 346:
 347:   public void initInstance(EntityEnterpriseContext ctx)
 348:	     {
 349:      getFieldState(ctx).init();
 350:   }
 351:
 352:   public void resetPersistenceContext(EntityEnterpriseContext ctx)
 353:	     {
 354:      throw new UnsupportedOperationException();
 355:   }
 356:
 357:   public int setInstanceParameters(PreparedStatement ps, int parameterIndex, EntityEnterpriseContext ctx)
 358:	     {
 359:      throw new UnsupportedOperationException();
 360:   }
 361:
 362:   public Object getInstanceValue(EntityEnterpriseContext ctx)
 363:	     {
 364:      throw new UnsupportedOperationException();
 365:   }
 366:
 367:   public void setInstanceValue(EntityEnterpriseContext ctx, Object value)
 368:	     {
 369:      throw new UnsupportedOperationException();
 370:   }
 371:
 372:   public int loadInstanceResults(ResultSet rs, int parameterIndex, EntityEnterpriseContext ctx)
 373:	     {
 374:      throw new UnsupportedOperationException();
 375:   }
 376:
 377:   public int loadArgumentResults(ResultSet rs, int parameterIndex, Object[] argumentRef)
 378:	     {
 379:      throw new UnsupportedOperationException();
 380:   }
 381:
 382:   public boolean isDirty(EntityEnterpriseContext ctx)
 383:	     {
 384:      throw new UnsupportedOperationException();
 385:   }
 386:
 387:   public void setClean(EntityEnterpriseContext ctx)
 388:	     {
 389:      throw new UnsupportedOperationException();
 390:   }
 391:
 392:   public boolean isCMPField()
 393:	     {
 394:      return false;
 395:   }
 396:
 397:   // CMRFieldBridge implementation
 398:
 399:   public String getFieldName()
 400:	     {
 401:      return metadata.getCMRFieldName();
 402:   }
 403:
 404:   public Object getValue(EntityEnterpriseContext ctx)
 405:	     {
 406:      FieldState state = getFieldState(ctx);
 407:      return state.getValue(ctx);
 408:   }
 409:
 410:   public void setValue(EntityEnterpriseContext ctx, Object value)
 411:	     {
 412:      FieldState state = getFieldState(ctx);
 413:      state.setValue(ctx, value);
 414:   }
 415:
 416:   public boolean isSingleValued()
 417:	     {
 418:      return metadata.getRelatedRole().isMultiplicityOne();
 419:   }
 420:
 421:   public EntityBridge getRelatedEntity()
 422:	     {
 423:      return relatedEntity;
 424:   }
 425:
 426:   // Private
 427:
 428:   private void initializeForeignKeyFields() throws DeploymentException
 429:	     {
 430:      Collection foreignKeys = metadata.getRelatedRole().getKeyFields();
 431:
 432:      // temporary map used later to write fk fields in special order
 433:      Map fkFieldsByRelatedPKFields = new HashMap();
 434:      for(Iterator i = foreignKeys.iterator(); i.hasNext();)
 435:	        {
 436:         JDBCCMPFieldMetaData fkFieldMetaData = (JDBCCMPFieldMetaData) i.next();
 437:         JDBCCMPFieldBridge2 relatedPKField =
 438:            (JDBCCMPFieldBridge2) relatedEntity.getFieldByName(fkFieldMetaData.getFieldName());
 439:
 440:         // now determine whether the fk is mapped to a pk column
 441:         String fkColumnName = fkFieldMetaData.getColumnName();
 442:         JDBCCMPFieldBridge2 fkField = null;
 443:
 444:         // look among the CMP fields for the field with the same column name
 445:         JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields();
 446:         for(int tableInd = 0; tableInd < tableFields.length && fkField == null; ++tableInd)
 447:	           {
 448:            JDBCCMPFieldBridge2 cmpField = tableFields[tableInd];
 449:            if(fkColumnName.equals(cmpField.getColumnName()))
 450:	              {
 451:               // construct the foreign key field
 452:               fkField = new JDBCCMPFieldBridge2(cmpField, relatedPKField);
 453:               /*
 454:                  cmpField.getManager(), // this cmpField's manager
 455:                  relatedPKField.getFieldName(),
 456:                  relatedPKField.getFieldType(),
 457:                  cmpField.getJDBCType(), // this cmpField's jdbc type
 458:                  relatedPKField.isReadOnly(),
 459:                  relatedPKField.getReadTimeOut(),
 460:                  relatedPKField.getPrimaryKeyClass(),
 461:                  relatedPKField.getPrimaryKeyField(),
 462:                  cmpField, // CMP field I am mapped to
 463:                  this,
 464:                  fkColumnName
 465:               );
 466:               */
 467:            }
 468:         }
 469:
 470:         // if the fk is not a part of pk then create a new field
 471:         if(fkField == null)
 472:	           {
 473:            fkField = entity.addTableField(fkFieldMetaData);
 474:         }
 475:         fkFieldsByRelatedPKFields.put(relatedPKField, fkField); // temporary map
 476:      }
 477:
 478:      // Note: this important to order the foreign key fields so that their order matches
 479:      // the order of related entity's pk fields in case of complex primary keys.
 480:      // The order is important in fk-constraint generation and in SELECT when loading
 481:      if(fkFieldsByRelatedPKFields.size() > 0)
 482:	        {
 483:         JDBCFieldBridge[] pkFields = relatedEntity.getPrimaryKeyFields();
 484:         List fkList = new ArrayList(pkFields.length);
 485:         List relatedPKList = new ArrayList(pkFields.length);
 486:         for(int i = 0; i < pkFields.length; ++i)
 487:	           {
 488:            JDBCFieldBridge relatedPKField = pkFields[i];
 489:            JDBCFieldBridge fkField = (JDBCCMPFieldBridge2) fkFieldsByRelatedPKFields.remove(relatedPKField);
 490:            fkList.add(fkField);
 491:            relatedPKList.add(relatedPKField);
 492:         }
 493:         foreignKeyFields = (JDBCCMPFieldBridge2[]) fkList.toArray(new JDBCCMPFieldBridge2[fkList.size()]);
 494:         relatedPKFields = (JDBCCMPFieldBridge2[]) relatedPKList.toArray(new JDBCCMPFieldBridge2[relatedPKList.size()]);
 495:      }
 496:      else
 497:	        {
 498:         foreignKeyFields = null;
 499:         relatedPKFields = null;
 500:      }
 501:   }
 502:
 503:   private FieldState getFieldState(EntityEnterpriseContext ctx)
 504:	     {
 505:      PersistentContext pctx = (PersistentContext) ctx.getPersistenceContext();
 506:      FieldState state = pctx.getCMRState(cmrIndex);
 507:      if(state == null)
 508:	        {
 509:         if(isSingleValued())
 510:	           {
 511:            state = new SingleValuedFieldState();
 512:         }
 513:         else
 514:	           {
 515:            state = new CollectionValuedFieldState();
 516:         }
 517:         pctx.setCMRState(cmrIndex, state);
 518:      }
 519:      return state;
 520:   }
 521:
 522:   private void invokeRemoveRelatedId(Object myId, Object relatedId)
 523:	     {
 524:      ClassLoader oldCL = GetTCLAction.getContextClassLoader();
 525:      SetTCLAction.setContextClassLoader(manager.getContainer().getClassLoader());
 526:
 527:      try
 528:	        {
 529:         Transaction tx = getTransaction();
 530:         EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache();
 531:
 532:         /*
 533:         RelationInterceptor.RelationInvocation invocation =
 534:            new RelationInterceptor.RelationInvocation(RelationInterceptor.CMRMessage.REMOVE_RELATED_ID);
 535:         invocation.setId(instanceCache.createCacheKey(myId));
 536:         invocation.setArguments(new Object[]{this, relatedId});
 537:         invocation.setTransaction(tx);
 538:         invocation.setPrincipal(SecurityAssociation.getPrincipal());
 539:         invocation.setCredential(SecurityAssociation.getCredential());
 540:         invocation.setType(InvocationType.LOCAL);
 541:         */
 542:
 543:         CMRInvocation invocation = new CMRInvocation();
 544:         invocation.setCmrMessage(CMRMessage.REMOVE_RELATION);
 545:         invocation.setEntrancy(Entrancy.NON_ENTRANT);
 546:         invocation.setId(instanceCache.createCacheKey(myId));
 547:         invocation.setArguments(new Object[]{this, relatedId});
 548:         invocation.setTransaction(tx);
 549:         invocation.setPrincipal(GetPrincipalAction.getPrincipal());
 550:         invocation.setCredential(GetCredentialAction.getCredential());
 551:         invocation.setType(InvocationType.LOCAL);
 552:
 553:         manager.getContainer().invoke(invocation);
 554:      }
 555:      catch(EJBException e)
 556:	        {
 557:         throw e;
 558:      }
 559:      catch(Exception e)
 560:	        {
 561:         throw new EJBException("Error in invokeRemoveRelatedId()", e);
 562:      }
 563:      finally
 564:	        {
 565:         SetTCLAction.setContextClassLoader(oldCL);
 566:      }
 567:   }
 568:
 569:   private void invokeAddRelatedId(Object myId, Object relatedId)
 570:	     {
 571:      ClassLoader oldCL = GetTCLAction.getContextClassLoader();
 572:      SetTCLAction.setContextClassLoader(manager.getContainer().getClassLoader());
 573:
 574:      try
 575:	        {
 576:         Transaction tx = getTransaction();
 577:         EntityCache instanceCache = (EntityCache) manager.getContainer().getInstanceCache();
 578:         /*
 579:         RelationInterceptor.RelationInvocation invocation =
 580:            new RelationInterceptor.RelationInvocation(RelationInterceptor.CMRMessage.ADD_RELATED_ID);
 581:         invocation.setId(instanceCache.createCacheKey(myId));
 582:         invocation.setArguments(new Object[]{this, relatedId});
 583:         invocation.setTransaction(tx);
 584:         invocation.setPrincipal(SecurityAssociation.getPrincipal());
 585:         invocation.setCredential(SecurityAssociation.getCredential());
 586:         invocation.setType(InvocationType.LOCAL);
 587:         */
 588:         CMRInvocation invocation = new CMRInvocation();
 589:         invocation.setCmrMessage(CMRMessage.ADD_RELATION);
 590:         invocation.setEntrancy(Entrancy.NON_ENTRANT);
 591:         invocation.setId(instanceCache.createCacheKey(myId));
 592:         invocation.setArguments(new Object[]{this, relatedId});
 593:         invocation.setTransaction(tx);
 594:         invocation.setPrincipal(GetPrincipalAction.getPrincipal());
 595:         invocation.setCredential(GetCredentialAction.getCredential());
 596:         invocation.setType(InvocationType.LOCAL);
 597:
 598:         manager.getContainer().invoke(invocation);
 599:      }
 600:      catch(EJBException e)
 601:	        {
 602:         throw e;
 603:      }
 604:      catch(Exception e)
 605:	        {
 606:         throw new EJBException("Error in invokeAddRelatedId()", e);
 607:      }
 608:      finally
 609:	        {
 610:         SetTCLAction.setContextClassLoader(oldCL);
 611:      }
 612:   }
 613:
 614:   private Transaction getTransaction() throws SystemException
 615:	     {
 616:      return tm.getTransaction();
 617:   }
 618:
 619:   private RelationTable getRelationTable() throws DeploymentException
 620:	     {
 621:      if(relationTable == null)
 622:	        {
 623:         relationTable = manager.getSchema().createRelationTable(this, relatedCMRField);
 624:      }
 625:      return relationTable;
 626:   }
 627:
 628:   // Inner
 629:
 630:   public class SingleValuedFieldState
 631:      implements FieldState
 632:	     {
 633:      private boolean loaded;
 634:      private Object value;
 635:
 636:      public void init()
 637:	        {
 638:         loaded = true;
 639:      }
 640:
 641:      public Object getValue(EntityEnterpriseContext ctx)
 642:	        {
 643:         // todo: cache this value?
 644:         Object value = getLoadedValue(ctx);
 645:         return value == null ? null : relatedContainer.getLocalProxyFactory().getEntityEJBLocalObject(value);
 646:      }
 647:
 648:      public void setValue(EntityEnterpriseContext ctx, Object value)
 649:	        {
 650:         if(value != null)
 651:	           {
 652:            EJBLocalObject localObject = (EJBLocalObject) value;
 653:            Object relatedId = localObject.getPrimaryKey();
 654:
 655:            addRelatedId(ctx, relatedId);
 656:            relatedCMRField.invokeAddRelatedId(relatedId, ctx.getId());
 657:         }
 658:         else
 659:	           {
 660:            destroyExistingRelationships(ctx);
 661:         }
 662:      }
 663:
 664:      public void cascadeDelete(EntityEnterpriseContext ctx) throws RemoveException
 665:	        {
 666:         EJBLocalObject value = (EJBLocalObject) getValue(ctx);
 667:         if(value != null)
 668:	           {
 669:            value.remove();
 670:         }
 671:      }
 672:
 673:      public void destroyExistingRelationships(EntityEnterpriseContext ctx)
 674:	        {
 675:         Object value = getLoadedValue(ctx);
 676:         if(value != null)
 677:	           {
 678:            removeRelatedId(ctx, value);
 679:            relatedCMRField.invokeRemoveRelatedId(value, ctx.getId());
 680:         }
 681:      }
 682:
 683:      public boolean removeRelatedId(EntityEnterpriseContext ctx, Object relatedId)
 684:	        {
 685:         if(hasForeignKey())
 686:	           {
 687:            getLoadedValue(ctx);
 688:         }
 689:         else
 690:	           {
 691:            loaded = true;
 692:         }
 693:
 694:         this.value = null;
 695:         loader.removeRelatedId(ctx, relatedId);
 696:
 697:         return true;
 698:      }
 699:
 700:      public boolean addRelatedId(EntityEnterpriseContext ctx, Object relatedId)
 701:	        {
 702:         Object value = getLoadedValue(ctx);
 703:         if(value != null)
 704:	           {
 705:            relatedCMRField.invokeRemoveRelatedId(value, ctx.getId());
 706:         }
 707:
 708:         this.value = relatedId;
 709:         loader.addRelatedId(ctx, relatedId);
 710:
 711:         return true;
 712:      }
 713:
 714:      public void addLoadedPk(Object pk)
 715:	        {
 716:         if(loaded)
 717:	           {
 718:            throw new IllegalStateException(entity.getEntityName()
 719:               +
 720:               "."
 721:               +
 722:               getFieldName()
 723:               +
 724:               " single-valued CMR field is already loaded. Check the database for consistancy. "
 725:               + " current value=" + value + ", loaded value=" + pk);
 726:         }
 727:
 728:         value = pk;
 729:         loaded = true;
 730:      }
 731:
 732:      // Private
 733:
 734:      private Object getLoadedValue(EntityEnterpriseContext ctx)
 735:	        {
 736:         if(!loaded)
 737:	           {
 738:            loader.load(ctx, this);
 739:            loaded = true;
 740:         }
 741:         return value;
 742:      }
 743:   }
 744:
 745:   public class CollectionValuedFieldState
 746:      implements FieldState
 747:	     {
 748:      private boolean loaded;
 749:      private Set value;
 750:
 751:      private Set removedWhileNotLoaded;
 752:      private Set addedWhileNotLoaded;
 753:
 754:      public void init()
 755:	        {
 756:         loaded = true;
 757:         value = new HashSet();
 758:      }
 759:
 760:      public Object getValue(EntityEnterpriseContext ctx)
 761:	        {
 762:         // todo: cache this value?
 763:         return new CMRSet(ctx, this);
 764:      }
 765:
 766:      public void setValue(EntityEnterpriseContext ctx, Object value)
 767:	        {
 768:         if(value == null)
 769:	           {
 770:            throw new IllegalStateException("Can't set collection-valued CMR field to null: " +
 771:               entity.getEntityName() + "." + getFieldName());
 772:         }
 773:
 774:         Set newValue = (Set) value;
 775:         if(!newValue.isEmpty())
 776:	           {
 777:            Set copy = new HashSet(newValue);
 778:            for(Iterator iter = copy.iterator(); iter.hasNext();)
 779:	              {
 780:               EJBLocalObject localObject = (EJBLocalObject) iter.next();
 781:               Object relatedId = localObject.getPrimaryKey();
 782:
 783:               addRelatedId(ctx, relatedId);
 784:               relatedCMRField.invokeAddRelatedId(relatedId, ctx.getId());
 785:            }
 786:         }
 787:      }
 788:
 789:      public void cascadeDelete(EntityEnterpriseContext ctx) throws RemoveException
 790:	        {
 791:         Collection value = (Collection) getValue(ctx);
 792:         if(!value.isEmpty())
 793:	           {
 794:            EJBLocalObject[] locals = (EJBLocalObject[]) value.toArray();
 795:            for(int i = 0; i < locals.length; ++i)
 796:	              {
 797:               locals[i].remove();
 798:            }
 799:         }
 800:      }
 801:
 802:      public void destroyExistingRelationships(EntityEnterpriseContext ctx)
 803:	        {
 804:         Set value = getLoadedValue(ctx);
 805:         if(!value.isEmpty())
 806:	           {
 807:            Object[] copy = value.toArray();
 808:            for(int i = 0; i < copy.length; ++i)
 809:	              {
 810:               Object relatedId = copy[i];
 811:               removeRelatedId(ctx, relatedId);
 812:               relatedCMRField.invokeRemoveRelatedId(relatedId, ctx.getId());
 813:               loader.removeRelatedId(ctx, relatedId);
 814:            }
 815:         }
 816:      }
 817:
 818:      public boolean removeRelatedId(EntityEnterpriseContext ctx, Object relatedId)
 819:	        {
 820:         boolean modified = false;
 821:         if(loaded)
 822:	           {
 823:            Set value = getLoadedValue(ctx);
 824:            if(!value.isEmpty())
 825:	              {
 826:               modified = value.remove(relatedId);
 827:            }
 828:         }
 829:         else
 830:	           {
 831:            modified = removeWhileNotLoaded(relatedId);
 832:         }
 833:         return modified;
 834:      }
 835:
 836:      public boolean addRelatedId(EntityEnterpriseContext ctx, Object relatedId)
 837:	        {
 838:         boolean modified;
 839:         if(loaded)
 840:	           {
 841:            Set value = getLoadedValue(ctx);
 842:            modified = value.add(relatedId);
 843:         }
 844:         else
 845:	           {
 846:            modified = addWhileNotLoaded(relatedId);
 847:         }
 848:         return modified;
 849:      }
 850:
 851:      public void addLoadedPk(Object pk)
 852:	        {
 853:         if(loaded)
 854:	           {
 855:            throw new IllegalStateException(entity.getEntityName()
 856:               +
 857:               "."
 858:               +
 859:               getFieldName()
 860:               +
 861:               " collection-valued CMR field is already loaded. Check the database for consistancy. "
 862:               + " current value=" + value + ", loaded value=" + pk);
 863:         }
 864:
 865:         if(pk != null)
 866:	           {
 867:            value.add(pk);
 868:         }
 869:      }
 870:
 871:      // Private
 872:
 873:      private Set getLoadedValue(EntityEnterpriseContext ctx)
 874:	        {
 875:         if(!loaded)
 876:	           {
 877:            if(value == null || value == Collections.EMPTY_SET)
 878:	              {
 879:               value = new HashSet();
 880:            }
 881:
 882:            loader.load(ctx, this);
 883:
 884:            if(addedWhileNotLoaded != null)
 885:	              {
 886:               value.addAll(addedWhileNotLoaded);
 887:               addedWhileNotLoaded = null;
 888:            }
 889:
 890:            if(removedWhileNotLoaded != null)
 891:	              {
 892:               value.removeAll(removedWhileNotLoaded);
 893:               removedWhileNotLoaded = null;
 894:            }
 895:
 896:            loaded = true;
 897:         }
 898:         return value;
 899:      }
 900:
 901:      private boolean removeWhileNotLoaded(Object relatedId)
 902:	        {
 903:         boolean removed = false;
 904:         if(addedWhileNotLoaded != null)
 905:	           {
 906:            removed = addedWhileNotLoaded.remove(relatedId);
 907:         }
 908:
 909:         if(!removed)
 910:	           {
 911:            if(removedWhileNotLoaded == null)
 912:	              {
 913:               removedWhileNotLoaded = new HashSet();
 914:            }
 915:            removed = removedWhileNotLoaded.add(relatedId);
 916:         }
 917:
 918:         if(log.isTraceEnabled() && removed)
 919:	           {
 920:            log.trace("removed while not loaded: relatedId=" + relatedId);
 921:         }
 922:
 923:         return removed;
 924:      }
 925:
 926:      private boolean addWhileNotLoaded(Object relatedId)
 927:	        {
 928:         boolean added = false;
 929:         if(removedWhileNotLoaded != null)
 930:	           {
 931:            added = removedWhileNotLoaded.remove(relatedId);
 932:         }
 933:
 934:         if(!added)
 935:	           {
 936:            if(addedWhileNotLoaded == null)
 937:	              {
 938:               addedWhileNotLoaded = new HashSet();
 939:            }
 940:            added = addedWhileNotLoaded.add(relatedId);
 941:         }
 942:
 943:         if(log.isTraceEnabled() && added)
 944:	           {
 945:            log.trace("added while not loaded: relatedId=" + relatedId);
 946:         }
 947:
 948:         return added;
 949:      }
 950:   }
 951:
 952:   public interface FieldState
 953:	     {
 954:      void init();
 955:
 956:      Object getValue(EntityEnterpriseContext ctx);
 957:
 958:      void cascadeDelete(EntityEnterpriseContext ctx) throws RemoveException;
 959:
 960:      void destroyExistingRelationships(EntityEnterpriseContext ctx);
 961:
 962:      void setValue(EntityEnterpriseContext ctx, Object value);
 963:
 964:      boolean removeRelatedId(EntityEnterpriseContext ctx, Object relatedId);
 965:
 966:      boolean addRelatedId(EntityEnterpriseContext ctx, Object value);
 967:
 968:      void addLoadedPk(Object pk);
 969:   }
 970:
 971:   private class RelationTableLoader
 972:      implements CMRFieldLoader
 973:	     {
 974:      private final String loadSql;
 975:
 976:      public RelationTableLoader()
 977:	        {
 978:         StringBuffer sql = new StringBuffer();
 979:         sql.append("select ");
 980:
 981:         String relatedTable = relatedEntity.getTableName();
 982:         String relationTable = metadata.getRelationMetaData().getDefaultTableName();
 983:
 984:         relatedEntity.getTable().appendColumnNames((JDBCCMPFieldBridge2[]) relatedEntity.getTableFields(),
 985:            relatedTable,
 986:            sql);
 987:         sql.append(" from ")
 988:            .append(relatedTable)
 989:            .append(" inner join ")
 990:            .append(relationTable)
 991:            .append(" on ");
 992:
 993:         JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) relatedEntity.getPrimaryKeyFields();
 994:         for(int i = 0; i < pkFields.length; ++i)
 995:	           {
 996:            if(i > 0)
 997:	              {
 998:               sql.append(", ");
 999:            }
1000:
1001:            sql.append(relatedTable).append('.').append(pkFields[i].getColumnName())
1002:               .append('=')
1003:               .append(relationTable).append('.').append(relatedCMRField.tableKeyFields[i].getColumnName());
1004:         }
1005:
1006:         /*
1007:         sql.append(" inner join ")
1008:            .append(myTable)
1009:            .append(" on ");
1010:
1011:         String myTable = entity.getTableName();
1012:         pkFields = entity.getPrimaryKeyFields();
1013:         for(int i = 0; i < pkFields.length; ++i)
1014:	           {
1015:            if(i > 0)
1016:	              {
1017:               sql.append(", ");
1018:            }
1019:
1020:            sql.append(myTable).append('.').append(pkFields[i].getColumnName())
1021:               .append('=')
1022:               .append(relationTable).append('.').append(tableKeyFields[i].getColumnName());
1023:         }
1024:         */
1025:
1026:         sql.append(" where ");
1027:         for(int i = 0; i < tableKeyFields.length; ++i)
1028:	           {
1029:            if(i > 0)
1030:	              {
1031:               sql.append(", ");
1032:            }
1033:
1034:            sql.append(relationTable).append('.').append(tableKeyFields[i].getColumnName()).append("=?");
1035:         }
1036:
1037:         loadSql = sql.toString();
1038:
1039:         if(log.isTraceEnabled())
1040:	           {
1041:            log.trace("load sql: " + loadSql);
1042:         }
1043:      }
1044:
1045:      public void load(EntityEnterpriseContext ctx, FieldState state)
1046:	        {
1047:         Object value;
1048:         EntityTable relatedTable = relatedEntity.getTable();
1049:
1050:         Connection con = null;
1051:         PreparedStatement ps = null;
1052:         ResultSet rs = null;
1053:         try
1054:	           {
1055:            if(log.isDebugEnabled())
1056:	              {
1057:               log.debug("executing: " + loadSql);
1058:            }
1059:
1060:            con = relatedTable.getConnection();
1061:            ps = con.prepareStatement(loadSql);
1062:
1063:            JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields();
1064:
1065:            Object myPk = ctx.getId();
1066:            int paramInd = 1;
1067:            for(int i = 0; i < pkFields.length; ++i)
1068:	              {
1069:               JDBCCMPFieldBridge2 pkField = pkFields[i];
1070:               Object fieldValue = pkField.getPrimaryKeyValue(myPk);
1071:
1072:               JDBCCMPFieldBridge2 relatedFkField = tableKeyFields[i];
1073:               relatedFkField.setArgumentParameters(ps, paramInd++, fieldValue);
1074:            }
1075:
1076:            rs = ps.executeQuery();
1077:
1078:            while(rs.next())
1079:	              {
1080:               value = relatedTable.loadRow(rs);
1081:               state.addLoadedPk(value);
1082:            }
1083:         }
1084:         catch(SQLException e)
1085:	           {
1086:            log.error("Failed to load related role: ejb-name="
1087:               +
1088:               entity.getEntityName() +
1089:               ", cmr-field=" + getFieldName() + ": " + e.getMessage(), e);
1090:            throw new EJBException("Failed to load related role: ejb-name="
1091:               +
1092:               entity.getEntityName() +
1093:               ", cmr-field=" + getFieldName() + ": " + e.getMessage(), e);
1094:         }
1095:         finally
1096:	           {
1097:            JDBCUtil.safeClose(rs);
1098:            JDBCUtil.safeClose(ps);
1099:            JDBCUtil.safeClose(con);
1100:         }
1101:      }
1102:
1103:      public void removeRelatedId(EntityEnterpriseContext ctx, Object relatedId)
1104:	        {
1105:         relationTable.removeRelation(JDBCCMRFieldBridge2.this, ctx.getId(), relatedCMRField, relatedId);
1106:      }
1107:
1108:      public void addRelatedId(EntityEnterpriseContext ctx, Object relatedId)
1109:	        {
1110:         relationTable.addRelation(JDBCCMRFieldBridge2.this, ctx.getId(), relatedCMRField, relatedId);
1111:      }
1112:   }
1113:
1114:   private class ForeignKeyLoader
1115:      implements CMRFieldLoader
1116:	     {
1117:      private final String loadSql;
1118:
1119:      public ForeignKeyLoader()
1120:	        {
1121:         StringBuffer sql = new StringBuffer();
1122:         sql.append("select ");
1123:         relatedEntity.getTable().appendColumnNames((JDBCCMPFieldBridge2[]) relatedEntity.getTableFields(), null, sql);
1124:         sql.append(" from ").append(relatedEntity.getTableName()).append(" where ");
1125:
1126:         JDBCCMPFieldBridge2[] relatedFkFields = relatedCMRField.foreignKeyFields;
1127:         sql.append(relatedFkFields[0].getColumnName()).append("=?");
1128:         for(int i = 1; i < relatedFkFields.length; ++i)
1129:	           {
1130:            JDBCCMPFieldBridge2 relatedFkField = relatedFkFields[i];
1131:            sql.append(", ").append(relatedFkField.getColumnName()).append("=?");
1132:         }
1133:
1134:         loadSql = sql.toString();
1135:
1136:         if(log.isTraceEnabled())
1137:	           {
1138:            log.trace("load sql: " + loadSql);
1139:         }
1140:      }
1141:
1142:      public void load(EntityEnterpriseContext ctx, FieldState state)
1143:	        {
1144:         Object value;
1145:         EntityTable relatedTable = relatedEntity.getTable();
1146:
1147:         Connection con = null;
1148:         PreparedStatement ps = null;
1149:         ResultSet rs = null;
1150:         try
1151:	           {
1152:            if(log.isDebugEnabled())
1153:	              {
1154:               log.debug("executing: " + loadSql);
1155:            }
1156:
1157:            con = relatedTable.getConnection();
1158:            ps = con.prepareStatement(loadSql);
1159:
1160:            JDBCCMPFieldBridge2[] relatedFkFields = relatedCMRField.foreignKeyFields;
1161:            JDBCCMPFieldBridge2[] myPkFields = relatedCMRField.relatedPKFields;
1162:
1163:            Object myPk = ctx.getId();
1164:            int paramInd = 1;
1165:            for(int i = 0; i < relatedFkFields.length; ++i)
1166:	              {
1167:               JDBCCMPFieldBridge2 myPkField = myPkFields[i];
1168:               Object fieldValue = myPkField.getPrimaryKeyValue(myPk);
1169:
1170:               JDBCCMPFieldBridge2 relatedFkField = relatedFkFields[i];
1171:               relatedFkField.setArgumentParameters(ps, paramInd++, fieldValue);
1172:            }
1173:
1174:            rs = ps.executeQuery();
1175:
1176:            while(rs.next())
1177:	              {
1178:               value = relatedTable.loadRow(rs);
1179:               state.addLoadedPk(value);
1180:            }
1181:         }
1182:         catch(SQLException e)
1183:	           {
1184:            log.error("Failed to load related role: ejb-name="
1185:               +
1186:               entity.getEntityName() +
1187:               ", cmr-field=" + getFieldName() + ": " + e.getMessage(), e);
1188:            throw new EJBException("Failed to load related role: ejb-name="
1189:               +
1190:               entity.getEntityName() +
1191:               ", cmr-field=" + getFieldName() + ": " + e.getMessage(), e);
1192:         }
1193:         finally
1194:	           {
1195:            JDBCUtil.safeClose(rs);
1196:            JDBCUtil.safeClose(ps);
1197:            JDBCUtil.safeClose(con);
1198:         }
1199:      }
1200:
1201:      public void removeRelatedId(EntityEnterpriseContext ctx, Object relatedId)
1202:	        {
1203:      }
1204:
1205:      public void addRelatedId(EntityEnterpriseContext ctx, Object relatedId)
1206:	        {
1207:      }
1208:   }
1209:
1210:   private class ContextForeignKeyLoader
1211:      implements CMRFieldLoader
1212:	     {
1213:      public void load(EntityEnterpriseContext ctx, FieldState state)
1214:	        {
1215:         Object relatedId = null;
1216:         for(int i = 0; i < foreignKeyFields.length; ++i)
1217:	           {
1218:            JDBCCMPFieldBridge2 fkField = foreignKeyFields[i];
1219:            Object fkFieldValue = fkField.getValue(ctx);
1220:            if(fkFieldValue == null)
1221:	              {
1222:               break;
1223:            }
1224:
1225:            JDBCCMPFieldBridge2 relatedPKField = relatedPKFields[i];
1226:            relatedId = relatedPKField.setPrimaryKeyValue(relatedId, fkFieldValue);
1227:         }
1228:
1229:         state.addLoadedPk(relatedId);
1230:      }
1231:
1232:      public void removeRelatedId(EntityEnterpriseContext ctx, Object relatedId)
1233:	        {
1234:         for(int i = 0; i < foreignKeyFields.length; ++i)
1235:	           {
1236:            foreignKeyFields[i].setValueInternal(ctx, null, true);
1237:         }
1238:      }
1239:
1240:      public void addRelatedId(EntityEnterpriseContext ctx, Object relatedId)
1241:	        {
1242:         for(int i = 0; i < foreignKeyFields.length; ++i)
1243:	           {
1244:            JDBCCMPFieldBridge2 relatedPKField = relatedPKFields[i];
1245:            Object fieldValue = relatedPKField.getPrimaryKeyValue(relatedId);
1246:            foreignKeyFields[i].setValueInternal(ctx, fieldValue, true);
1247:         }
1248:      }
1249:   }
1250:
1251:   private interface CMRFieldLoader
1252:	     {
1253:      void load(EntityEnterpriseContext ctx, FieldState state);
1254:
1255:      void removeRelatedId(EntityEnterpriseContext ctx, Object relatedId);
1256:
1257:      void addRelatedId(EntityEnterpriseContext ctx, Object relatedId);
1258:   }
1259:
1260:   private class CMRSet implements Set
1261:	     {
1262:      private final EntityEnterpriseContext ctx;
1263:      private final CollectionValuedFieldState state;
1264:
1265:      public CMRSet(EntityEnterpriseContext ctx, CollectionValuedFieldState state)
1266:	        {
1267:         this.ctx = ctx;
1268:         this.state = state;
1269:      }
1270:
1271:      public int size()
1272:	        {
1273:         return state.getLoadedValue(ctx).size();
1274:      }
1275:
1276:      public void clear()
1277:	        {
1278:         destroyExistingRelationships(ctx);
1279:      }
1280:
1281:      public boolean isEmpty()
1282:	        {
1283:         return size() == 0;
1284:      }
1285:
1286:      public boolean add(Object o)
1287:	        {
1288:         EJBLocalObject local = memberArgumentValidation(o);
1289:         Object relatedId = local.getPrimaryKey();
1290:         boolean modified = addRelatedId(ctx, relatedId);
1291:
1292:         if(modified)
1293:	           {
1294:            relatedCMRField.invokeAddRelatedId(relatedId, ctx.getId());
1295:            loader.addRelatedId(ctx, relatedId);
1296:         }
1297:
1298:         return modified;
1299:      }
1300:
1301:      public boolean contains(Object o)
1302:	        {
1303:         EJBLocalObject local = memberArgumentValidation(o);
1304:         return state.getLoadedValue(ctx).contains(local.getPrimaryKey());
1305:      }
1306:
1307:      public boolean remove(Object o)
1308:	        {
1309:         EJBLocalObject local = memberArgumentValidation(o);
1310:         Object relatedId = local.getPrimaryKey();
1311:         return removeById(relatedId);
1312:      }
1313:
1314:      public boolean addAll(Collection c)
1315:	        {
1316:         if(c == null || c.isEmpty())
1317:	           {
1318:            return false;
1319:         }
1320:
1321:         boolean modified = false;
1322:         Object[] copy = c.toArray();
1323:         for(int i = 0; i < copy.length; ++i)
1324:	           {
1325:            // not modified || add()
1326:            modified = add(copy[i]) || modified;
1327:         }
1328:
1329:         return modified;
1330:      }
1331:
1332:      public boolean containsAll(Collection c)
1333:	        {
1334:         if(c == null || c.isEmpty())
1335:	           {
1336:            return true;
1337:         }
1338:
1339:         Set ids = argumentToIdSet(c);
1340:
1341:         return state.getLoadedValue(ctx).contains(ids);
1342:      }
1343:
1344:      public boolean removeAll(Collection c)
1345:	        {
1346:         if(c == null || c.isEmpty())
1347:	           {
1348:            return false;
1349:         }
1350:
1351:         boolean modified = false;
1352:         Object[] copy = c.toArray();
1353:         for(int i = 0; i < copy.length; ++i)
1354:	           {
1355:            modified = remove(copy[i]) || modified;
1356:         }
1357:
1358:         return modified;
1359:      }
1360:
1361:      public boolean retainAll(Collection c)
1362:	        {
1363:         Set value = state.getLoadedValue(ctx);
1364:         if(c == null || c.isEmpty())
1365:	           {
1366:            if(value.isEmpty())
1367:	              {
1368:               return false;
1369:            }
1370:            else
1371:	              {
1372:               clear();
1373:            }
1374:         }
1375:
1376:         boolean modified = false;
1377:         Set idSet = argumentToIdSet(c);
1378:         Object[] valueCopy = value.toArray();
1379:         for(int i = 0; i < valueCopy.length; ++i)
1380:	           {
1381:            Object id = valueCopy[i];
1382:            if(!idSet.contains(id))
1383:	              {
1384:               removeById(id);
1385:               modified = true;
1386:            }
1387:         }
1388:
1389:         return modified;
1390:      }
1391:
1392:      public Iterator iterator()
1393:	        {
1394:         return new Iterator()
1395:	           {
1396:            // todo get rid of copying
1397:            private final Iterator idIter = new HashSet(state.getLoadedValue(ctx)).iterator();
1398:            private Object curId;
1399:
1400:            public void remove()
1401:	              {
1402:               try
1403:	                 {
1404:                  idIter.remove();
1405:               }
1406:               catch(ConcurrentModificationException e)
1407:	                 {
1408:                  throw new IllegalStateException(e.getMessage());
1409:               }
1410:
1411:               removeById(curId);
1412:            }
1413:
1414:            public boolean hasNext()
1415:	              {
1416:               try
1417:	                 {
1418:                  return idIter.hasNext();
1419:               }
1420:               catch(ConcurrentModificationException e)
1421:	                 {
1422:                  throw new IllegalStateException(e.getMessage());
1423:               }
1424:            }
1425:
1426:            public Object next()
1427:	              {
1428:               try
1429:	                 {
1430:                  curId = idIter.next();
1431:               }
1432:               catch(ConcurrentModificationException e)
1433:	                 {
1434:                  throw new IllegalStateException(e.getMessage());
1435:               }
1436:
1437:               return relatedContainer.getLocalProxyFactory().getEntityEJBLocalObject(curId);
1438:            }
1439:         };
1440:      }
1441:
1442:      public Object[] toArray()
1443:	        {
1444:         Set value = state.getLoadedValue(ctx);
1445:
1446:         Object[] result = (Object[]) Array.newInstance(relatedEntity.getLocalInterface(), value.size());
1447:
1448:         LocalProxyFactory relatedPF = relatedContainer.getLocalProxyFactory();
1449:         int i = 0;
1450:         for(Iterator iter = value.iterator(); iter.hasNext();)
1451:	           {
1452:            Object id = iter.next();
1453:            result[i++] = relatedPF.getEntityEJBLocalObject(id);
1454:         }
1455:
1456:         return result;
1457:      }
1458:
1459:      public Object[] toArray(Object a[])
1460:	        {
1461:         Set value = state.getLoadedValue(ctx);
1462:         if(a == null || a.length < value.size())
1463:	           {
1464:            a = (Object[]) Array.newInstance(entity.getLocalInterface(), value.size());
1465:         }
1466:
1467:         LocalProxyFactory relatedPF = relatedContainer.getLocalProxyFactory();
1468:         int i = 0;
1469:         for(Iterator iter = value.iterator(); iter.hasNext();)
1470:	           {
1471:            Object id = iter.next();
1472:            a[i++] = relatedPF.getEntityEJBLocalObject(id);
1473:         }
1474:
1475:         return a;
1476:      }
1477:
1478:      public String toString()
1479:	        {
1480:         return state.getLoadedValue(ctx).toString();
1481:      }
1482:
1483:      // Private
1484:
1485:      private boolean removeById(Object relatedId)
1486:	        {
1487:         boolean modified = removeRelatedId(ctx, relatedId);
1488:         if(modified)
1489:	           {
1490:            relatedCMRField.invokeRemoveRelatedId(relatedId, ctx.getId());
1491:            loader.removeRelatedId(ctx, relatedId);
1492:         }
1493:         return modified;
1494:      }
1495:
1496:      private Set argumentToIdSet(Collection c)
1497:	        {
1498:         Set ids = new HashSet();
1499:         for(Iterator iter = c.iterator(); iter.hasNext();)
1500:	           {
1501:            EJBLocalObject local = memberArgumentValidation(iter.next());
1502:            ids.add(local.getPrimaryKey());
1503:         }
1504:         return ids;
1505:      }
1506:
1507:      private EJBLocalObject memberArgumentValidation(Object o)
1508:	        {
1509:         if(o == null)
1510:	           {
1511:            throw new IllegalStateException("This implementation does not support null members.");
1512:         }
1513:
1514:         if(!relatedEntity.getLocalInterface().isInstance(o))
1515:	           {
1516:            throw new IllegalStateException("Memebers must be of type " + entity.getLocalInterface().getName());
1517:         }
1518:
1519:         return (EJBLocalObject) o;
1520:      }
1521:   }
1522:
1523:   private static class GetTCLAction implements PrivilegedAction
1524:	     {
1525:      static PrivilegedAction ACTION = new GetTCLAction();
1526:      public Object run()
1527:	        {
1528:         ClassLoader loader = Thread.currentThread().getContextClassLoader();
1529:         return loader;
1530:      }
1531:      static ClassLoader getContextClassLoader()
1532:	        {
1533:         ClassLoader loader = (ClassLoader) AccessController.doPrivileged(ACTION);
1534:         return loader;
1535:      }
1536:   }
1537:   private static class SetTCLAction implements PrivilegedAction
1538:	     {
1539:      ClassLoader loader;
1540:      SetTCLAction(ClassLoader loader)
1541:	        {
1542:         this.loader = loader;
1543:      }
1544:      public Object run()
1545:	        {
1546:         Thread.currentThread().setContextClassLoader(loader);
1547:         loader = null;
1548:         return null;
1549:      }
1550:      static void setContextClassLoader(ClassLoader loader)
1551:	        {
1552:         PrivilegedAction action = new SetTCLAction(loader);
1553:         AccessController.doPrivileged(action);
1554:      }
1555:   }
1556:   
1557:   private static class GetPrincipalAction implements PrivilegedAction
1558:	     {
1559:      static PrivilegedAction ACTION = new GetPrincipalAction();
1560:      public Object run()
1561:	        {
1562:         Principal principal = SecurityAssociation.getPrincipal();
1563:         return principal;
1564:      }
1565:      static Principal getPrincipal()
1566:	        {
1567:         Principal principal = (Principal) AccessController.doPrivileged(ACTION);
1568:         return principal;
1569:      }
1570:   }
1571:
1572:   private static class GetCredentialAction implements PrivilegedAction
1573:	     {
1574:      static PrivilegedAction ACTION = new GetCredentialAction();
1575:      public Object run()
1576:	        {
1577:         Object credential = SecurityAssociation.getCredential();
1578:         return credential;
1579:      }
1580:      static Object getCredential()
1581:	        {
1582:         Object credential = AccessController.doPrivileged(ACTION);
1583:         return credential;
1584:      }
1585:   }
1586:
1587:}