0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package org.jboss.ejb.plugins.cmp.jdbc2.bridge;
0023:
0024: import org.jboss.ejb.plugins.cmp.bridge.EntityBridge;
0025: import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
0026: import org.jboss.ejb.plugins.cmp.jdbc2.JDBCStoreManager2;
0027: import org.jboss.ejb.plugins.cmp.jdbc2.schema.EntityTable;
0028: import org.jboss.ejb.plugins.cmp.jdbc2.schema.RelationTable;
0029: import org.jboss.ejb.plugins.cmp.jdbc2.schema.Cache;
0030: import org.jboss.ejb.plugins.cmp.jdbc2.PersistentContext;
0031: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
0032: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
0033: import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil;
0034: import org.jboss.ejb.plugins.cmp.jdbc.JDBCType;
0035: import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
0036: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
0037: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge;
0038: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
0039: import org.jboss.ejb.plugins.cmp.jdbc.bridge.CMRInvocation;
0040: import org.jboss.ejb.plugins.cmp.jdbc.bridge.CMRMessage;
0041: import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
0042: import org.jboss.ejb.plugins.lock.Entrancy;
0043: import org.jboss.ejb.EntityEnterpriseContext;
0044: import org.jboss.ejb.EntityContainer;
0045: import org.jboss.ejb.EntityCache;
0046: import org.jboss.ejb.LocalProxyFactory;
0047: import org.jboss.deployment.DeploymentException;
0048: import org.jboss.logging.Logger;
0049: import org.jboss.security.SecurityAssociation;
0050: import org.jboss.invocation.InvocationType;
0051:
0052: import javax.ejb.EJBException;
0053: import javax.ejb.EJBLocalObject;
0054: import javax.ejb.RemoveException;
0055: import javax.ejb.NoSuchObjectLocalException;
0056: import javax.transaction.Transaction;
0057: import javax.transaction.TransactionManager;
0058: import javax.transaction.SystemException;
0059: import java.util.Collection;
0060: import java.util.List;
0061: import java.util.ArrayList;
0062: import java.util.Map;
0063: import java.util.HashMap;
0064: import java.util.Iterator;
0065: import java.util.Set;
0066: import java.util.Collections;
0067: import java.util.HashSet;
0068: import java.util.ConcurrentModificationException;
0069: import java.sql.PreparedStatement;
0070: import java.sql.Connection;
0071: import java.sql.ResultSet;
0072: import java.sql.SQLException;
0073: import java.lang.reflect.Array;
0074: import java.security.AccessController;
0075: import java.security.PrivilegedAction;
0076: import java.security.Principal;
0077:
0078: /**
0079: * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
0080: * @version <tt>$Revision: 62071 $</tt>
0081: */
0082: public class JDBCCMRFieldBridge2 extends JDBCAbstractCMRFieldBridge {
0083: private final JDBCRelationshipRoleMetaData metadata;
0084: private final JDBCStoreManager2 manager;
0085: private final JDBCEntityBridge2 entity;
0086: private final int cmrIndex;
0087: private final Logger log;
0088:
0089: private JDBCEntityBridge2 relatedEntity;
0090: private JDBCCMRFieldBridge2 relatedCMRField;
0091: private EntityContainer relatedContainer;
0092:
0093: private JDBCCMPFieldBridge2[] tableKeyFields;
0094: private JDBCCMPFieldBridge2[] foreignKeyFields;
0095: private JDBCCMPFieldBridge2[] relatedPKFields;
0096:
0097: private CMRFieldLoader loader;
0098: private RelationTable relationTable;
0099:
0100: private EntityTable.ForeignKeyConstraint fkConstraint;
0101:
0102: private final TransactionManager tm;
0103:
0104: public JDBCCMRFieldBridge2(JDBCEntityBridge2 entityBridge,
0105: JDBCStoreManager2 manager,
0106: JDBCRelationshipRoleMetaData metadata) {
0107: this .manager = manager;
0108: this .entity = entityBridge;
0109: this .metadata = metadata;
0110: cmrIndex = entity.getNextCMRIndex();
0111: tm = manager.getContainer().getTransactionManager();
0112:
0113: log = Logger.getLogger(getClass().getName() + "."
0114: + entity.getEntityName() + "#" + getFieldName());
0115: }
0116:
0117: // Public
0118:
0119: public void resolveRelationship() throws DeploymentException {
0120: //
0121: // Set handles to the related entity's container, cache, manager, and invoker
0122: //
0123:
0124: // Related Entity Name
0125: String relatedEntityName = metadata.getRelatedRole()
0126: .getEntity().getName();
0127:
0128: // Related Entity
0129: Catalog catalog = (Catalog) manager
0130: .getApplicationData("CATALOG");
0131: relatedEntity = (JDBCEntityBridge2) catalog
0132: .getEntityByEJBName(relatedEntityName);
0133: if (relatedEntity == null) {
0134: throw new DeploymentException("Related entity not found: "
0135: + "entity=" + entity.getEntityName() + ", "
0136: + "cmrField=" + getFieldName() + ", "
0137: + "relatedEntity=" + relatedEntityName);
0138: }
0139:
0140: // Related CMR Field
0141: JDBCCMRFieldBridge2[] cmrFields = (JDBCCMRFieldBridge2[]) relatedEntity
0142: .getCMRFields();
0143: for (int i = 0; i < cmrFields.length; ++i) {
0144: JDBCCMRFieldBridge2 cmrField = cmrFields[i];
0145: if (metadata.getRelatedRole() == cmrField.getMetaData()) {
0146: relatedCMRField = cmrField;
0147: break;
0148: }
0149: }
0150:
0151: // if we didn't find the related CMR field throw an exception with a detailed message
0152: if (relatedCMRField == null) {
0153: String message = "Related CMR field not found in "
0154: + relatedEntity.getEntityName()
0155: + " for relationship from ";
0156:
0157: message += entity.getEntityName() + ".";
0158: if (getFieldName() != null) {
0159: message += getFieldName();
0160: } else {
0161: message += "<no-field>";
0162: }
0163:
0164: message += " to ";
0165: message += relatedEntityName + ".";
0166: if (metadata.getRelatedRole().getCMRFieldName() != null) {
0167: message += metadata.getRelatedRole().getCMRFieldName();
0168: } else {
0169: message += "<no-field>";
0170: }
0171:
0172: throw new DeploymentException(message);
0173: }
0174:
0175: // Related Container
0176: relatedContainer = relatedEntity.getContainer();
0177:
0178: //
0179: // Initialize the key fields
0180: //
0181: if (metadata.getRelationMetaData().isTableMappingStyle()) {
0182: // initialize relation table key fields
0183: Collection tableKeys = metadata.getKeyFields();
0184: List keyFieldsList = new ArrayList(tableKeys.size());
0185:
0186: // first phase is to create fk fields
0187: Map pkFieldsToFKFields = new HashMap(tableKeys.size());
0188: for (Iterator i = tableKeys.iterator(); i.hasNext();) {
0189: JDBCCMPFieldMetaData cmpFieldMetaData = (JDBCCMPFieldMetaData) i
0190: .next();
0191: FieldBridge pkField = entity
0192: .getFieldByName(cmpFieldMetaData.getFieldName());
0193: if (pkField == null) {
0194: throw new DeploymentException(
0195: "Primary key not found for key-field "
0196: + cmpFieldMetaData.getFieldName());
0197: }
0198: pkFieldsToFKFields.put(pkField,
0199: new JDBCCMPFieldBridge2(manager, entity,
0200: cmpFieldMetaData, -1));
0201: }
0202:
0203: // second step is to order fk fields to match the order of pk fields
0204: JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
0205: for (int i = 0; i < pkFields.length; ++i) {
0206: Object fkField = pkFieldsToFKFields.get(pkFields[i]);
0207: if (fkField == null) {
0208: throw new DeploymentException("Primary key "
0209: + pkFields[i].getFieldName()
0210: + " is not mapped.");
0211: }
0212: keyFieldsList.add(fkField);
0213: }
0214: tableKeyFields = (JDBCCMPFieldBridge2[]) keyFieldsList
0215: .toArray(new JDBCCMPFieldBridge2[keyFieldsList
0216: .size()]);
0217: } else {
0218: initializeForeignKeyFields();
0219: }
0220: }
0221:
0222: public void initLoader() throws DeploymentException {
0223: if (metadata.getRelationMetaData().isTableMappingStyle()) {
0224: relationTable = relatedCMRField.getRelationTable();
0225: loader = new RelationTableLoader();
0226: } else {
0227: if (foreignKeyFields != null) {
0228: loader = new ContextForeignKeyLoader();
0229: } else {
0230: loader = new ForeignKeyLoader();
0231: }
0232: }
0233: }
0234:
0235: public JDBCRelationshipRoleMetaData getMetaData() {
0236: return metadata;
0237: }
0238:
0239: public boolean removeRelatedId(EntityEnterpriseContext ctx,
0240: Object relatedId) {
0241: FieldState state = getFieldState(ctx);
0242: return state.removeRelatedId(ctx, relatedId);
0243: }
0244:
0245: public boolean addRelatedId(EntityEnterpriseContext ctx,
0246: Object relatedId) {
0247: FieldState state = getFieldState(ctx);
0248: return state.addRelatedId(ctx, relatedId);
0249: }
0250:
0251: public void remove(EntityEnterpriseContext ctx)
0252: throws RemoveException {
0253: if (metadata.getRelatedRole().isCascadeDelete()) {
0254: FieldState state = getFieldState(ctx);
0255: state.cascadeDelete(ctx);
0256: } else {
0257: destroyExistingRelationships(ctx);
0258: }
0259: }
0260:
0261: public void destroyExistingRelationships(EntityEnterpriseContext ctx) {
0262: FieldState state = getFieldState(ctx);
0263: state.destroyExistingRelationships(ctx);
0264: }
0265:
0266: public JDBCFieldBridge[] getTableKeyFields() {
0267: return tableKeyFields;
0268: }
0269:
0270: public JDBCEntityPersistenceStore getManager() {
0271: return manager;
0272: }
0273:
0274: public boolean hasForeignKey() {
0275: return foreignKeyFields != null;
0276: }
0277:
0278: public JDBCAbstractCMRFieldBridge getRelatedCMRField() {
0279: return this .relatedCMRField;
0280: }
0281:
0282: public JDBCFieldBridge[] getForeignKeyFields() {
0283: return foreignKeyFields;
0284: }
0285:
0286: public JDBCCMRFieldBridge2 getRelatedField() {
0287: return relatedCMRField;
0288: }
0289:
0290: public JDBCAbstractEntityBridge getEntity() {
0291: return entity;
0292: }
0293:
0294: public String getQualifiedTableName() {
0295: return relationTable.getTableName();
0296: }
0297:
0298: public String getTableName() {
0299: throw new UnsupportedOperationException();
0300: }
0301:
0302: // JDBCFieldBridge implementation
0303:
0304: public JDBCType getJDBCType() {
0305: throw new UnsupportedOperationException();
0306: }
0307:
0308: public boolean isPrimaryKeyMember() {
0309: throw new UnsupportedOperationException();
0310: }
0311:
0312: public boolean isReadOnly() {
0313: throw new UnsupportedOperationException();
0314: }
0315:
0316: public boolean isReadTimedOut(EntityEnterpriseContext ctx) {
0317: throw new UnsupportedOperationException();
0318: }
0319:
0320: public boolean isLoaded(EntityEnterpriseContext ctx) {
0321: throw new UnsupportedOperationException();
0322: }
0323:
0324: public void initInstance(EntityEnterpriseContext ctx) {
0325: getFieldState(ctx).init();
0326: }
0327:
0328: public void resetPersistenceContext(EntityEnterpriseContext ctx) {
0329: throw new UnsupportedOperationException();
0330: }
0331:
0332: public int setInstanceParameters(PreparedStatement ps,
0333: int parameterIndex, EntityEnterpriseContext ctx) {
0334: throw new UnsupportedOperationException();
0335: }
0336:
0337: public Object getInstanceValue(EntityEnterpriseContext ctx) {
0338: throw new UnsupportedOperationException();
0339: }
0340:
0341: public void setInstanceValue(EntityEnterpriseContext ctx,
0342: Object value) {
0343: throw new UnsupportedOperationException();
0344: }
0345:
0346: public int loadInstanceResults(ResultSet rs, int parameterIndex,
0347: EntityEnterpriseContext ctx) {
0348: throw new UnsupportedOperationException();
0349: }
0350:
0351: public int loadArgumentResults(ResultSet rs, int parameterIndex,
0352: Object[] argumentRef) {
0353: throw new UnsupportedOperationException();
0354: }
0355:
0356: public boolean isDirty(EntityEnterpriseContext ctx) {
0357: return getFieldState(ctx).isModified();
0358: }
0359:
0360: public void setClean(EntityEnterpriseContext ctx) {
0361: throw new UnsupportedOperationException();
0362: }
0363:
0364: public boolean isCMPField() {
0365: return false;
0366: }
0367:
0368: // CMRFieldBridge implementation
0369:
0370: public String getFieldName() {
0371: return metadata.getCMRFieldName();
0372: }
0373:
0374: public Object getValue(EntityEnterpriseContext ctx) {
0375: FieldState state = getFieldState(ctx);
0376: return state.getValue(ctx);
0377: }
0378:
0379: public void setValue(EntityEnterpriseContext ctx, Object value) {
0380: FieldState state = getFieldState(ctx);
0381: state.setValue(ctx, value);
0382: state.cacheValue(ctx);
0383: }
0384:
0385: public boolean isSingleValued() {
0386: return metadata.getRelatedRole().isMultiplicityOne();
0387: }
0388:
0389: public EntityBridge getRelatedEntity() {
0390: return relatedEntity;
0391: }
0392:
0393: // Private
0394:
0395: private void initializeForeignKeyFields()
0396: throws DeploymentException {
0397: Collection foreignKeys = metadata.getRelatedRole()
0398: .getKeyFields();
0399:
0400: // temporary map used later to write fk fields in special order
0401: Map fkFieldsByRelatedPKFields = new HashMap();
0402: for (Iterator i = foreignKeys.iterator(); i.hasNext();) {
0403: JDBCCMPFieldMetaData fkFieldMetaData = (JDBCCMPFieldMetaData) i
0404: .next();
0405: JDBCCMPFieldBridge2 relatedPKField = (JDBCCMPFieldBridge2) relatedEntity
0406: .getFieldByName(fkFieldMetaData.getFieldName());
0407:
0408: // now determine whether the fk is mapped to a pk column
0409: String fkColumnName = fkFieldMetaData.getColumnName();
0410: JDBCCMPFieldBridge2 fkField = null;
0411:
0412: // look among the CMP fields for the field with the same column name
0413: JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity
0414: .getTableFields();
0415: for (int tableInd = 0; tableInd < tableFields.length
0416: && fkField == null; ++tableInd) {
0417: JDBCCMPFieldBridge2 cmpField = tableFields[tableInd];
0418: if (fkColumnName.equals(cmpField.getColumnName())) {
0419: // construct the foreign key field
0420: fkField = new JDBCCMPFieldBridge2(cmpField,
0421: relatedPKField);
0422: /*
0423: cmpField.getManager(), // this cmpField's manager
0424: relatedPKField.getFieldName(),
0425: relatedPKField.getFieldType(),
0426: cmpField.getJDBCType(), // this cmpField's jdbc type
0427: relatedPKField.isReadOnly(),
0428: relatedPKField.getReadTimeOut(),
0429: relatedPKField.getPrimaryKeyClass(),
0430: relatedPKField.getPrimaryKeyField(),
0431: cmpField, // CMP field I am mapped to
0432: this,
0433: fkColumnName
0434: );
0435: */
0436: }
0437: }
0438:
0439: // if the fk is not a part of pk then create a new field
0440: if (fkField == null) {
0441: fkField = entity.addTableField(fkFieldMetaData);
0442: }
0443: fkFieldsByRelatedPKFields.put(relatedPKField, fkField); // temporary map
0444: }
0445:
0446: // Note: this important to order the foreign key fields so that their order matches
0447: // the order of related entity's pk fields in case of complex primary keys.
0448: // The order is important in fk-constraint generation and in SELECT when loading
0449: if (fkFieldsByRelatedPKFields.size() > 0) {
0450: JDBCFieldBridge[] pkFields = relatedEntity
0451: .getPrimaryKeyFields();
0452: List fkList = new ArrayList(pkFields.length);
0453: List relatedPKList = new ArrayList(pkFields.length);
0454: for (int i = 0; i < pkFields.length; ++i) {
0455: JDBCFieldBridge relatedPKField = pkFields[i];
0456: JDBCFieldBridge fkField = (JDBCCMPFieldBridge2) fkFieldsByRelatedPKFields
0457: .remove(relatedPKField);
0458: fkList.add(fkField);
0459: relatedPKList.add(relatedPKField);
0460: }
0461: foreignKeyFields = (JDBCCMPFieldBridge2[]) fkList
0462: .toArray(new JDBCCMPFieldBridge2[fkList.size()]);
0463: relatedPKFields = (JDBCCMPFieldBridge2[]) relatedPKList
0464: .toArray(new JDBCCMPFieldBridge2[relatedPKList
0465: .size()]);
0466:
0467: if (metadata.hasForeignKeyConstraint()) {
0468: fkConstraint = entity.getTable().addFkConstraint(
0469: foreignKeyFields, relatedEntity.getTable());
0470: }
0471: } else {
0472: foreignKeyFields = null;
0473: relatedPKFields = null;
0474: }
0475: }
0476:
0477: private FieldState getFieldState(EntityEnterpriseContext ctx) {
0478: PersistentContext pctx = (PersistentContext) ctx
0479: .getPersistenceContext();
0480: FieldState state = pctx.getCMRState(cmrIndex);
0481: if (state == null) {
0482: if (isSingleValued()) {
0483: state = new SingleValuedFieldState();
0484: } else {
0485: state = new CollectionValuedFieldState();
0486: }
0487: pctx.setCMRState(cmrIndex, state);
0488: }
0489: return state;
0490: }
0491:
0492: private void invokeRemoveRelatedId(Object myId, Object relatedId) {
0493: try {
0494: Transaction tx = getTransaction();
0495: EntityCache instanceCache = (EntityCache) manager
0496: .getContainer().getInstanceCache();
0497:
0498: /*
0499: RelationInterceptor.RelationInvocation invocation =
0500: new RelationInterceptor.RelationInvocation(RelationInterceptor.CMRMessage.REMOVE_RELATED_ID);
0501: invocation.setId(instanceCache.createCacheKey(myId));
0502: invocation.setArguments(new Object[]{this, relatedId});
0503: invocation.setTransaction(tx);
0504: invocation.setPrincipal(SecurityAssociation.getPrincipal());
0505: invocation.setCredential(SecurityAssociation.getCredential());
0506: invocation.setType(InvocationType.LOCAL);
0507: */
0508:
0509: SecurityActions actions = SecurityActions.UTIL
0510: .getSecurityActions();
0511:
0512: CMRInvocation invocation = new CMRInvocation();
0513: invocation.setCmrMessage(CMRMessage.REMOVE_RELATION);
0514: invocation.setEntrancy(Entrancy.NON_ENTRANT);
0515: invocation.setId(instanceCache.createCacheKey(myId));
0516: invocation.setArguments(new Object[] { this , relatedId });
0517: invocation.setTransaction(tx);
0518: invocation.setPrincipal(actions.getPrincipal());
0519: invocation.setCredential(actions.getCredential());
0520: invocation.setType(InvocationType.LOCAL);
0521:
0522: manager.getContainer().invoke(invocation);
0523: } catch (EJBException e) {
0524: throw e;
0525: } catch (Exception e) {
0526: throw new EJBException("Error in invokeRemoveRelatedId()",
0527: e);
0528: }
0529: }
0530:
0531: private void invokeAddRelatedId(Object myId, Object relatedId) {
0532: try {
0533: Transaction tx = getTransaction();
0534: EntityCache instanceCache = (EntityCache) manager
0535: .getContainer().getInstanceCache();
0536: /*
0537: RelationInterceptor.RelationInvocation invocation =
0538: new RelationInterceptor.RelationInvocation(RelationInterceptor.CMRMessage.ADD_RELATED_ID);
0539: invocation.setId(instanceCache.createCacheKey(myId));
0540: invocation.setArguments(new Object[]{this, relatedId});
0541: invocation.setTransaction(tx);
0542: invocation.setPrincipal(SecurityAssociation.getPrincipal());
0543: invocation.setCredential(SecurityAssociation.getCredential());
0544: invocation.setType(InvocationType.LOCAL);
0545: */
0546: SecurityActions actions = SecurityActions.UTIL
0547: .getSecurityActions();
0548:
0549: CMRInvocation invocation = new CMRInvocation();
0550: invocation.setCmrMessage(CMRMessage.ADD_RELATION);
0551: invocation.setEntrancy(Entrancy.NON_ENTRANT);
0552: invocation.setId(instanceCache.createCacheKey(myId));
0553: invocation.setArguments(new Object[] { this , relatedId });
0554: invocation.setTransaction(tx);
0555: invocation.setPrincipal(actions.getPrincipal());
0556: invocation.setCredential(actions.getCredential());
0557: invocation.setType(InvocationType.LOCAL);
0558:
0559: manager.getContainer().invoke(invocation);
0560: } catch (EJBException e) {
0561: throw e;
0562: } catch (Exception e) {
0563: throw new EJBException("Error in invokeAddRelatedId()", e);
0564: }
0565: }
0566:
0567: private Transaction getTransaction() throws SystemException {
0568: return tm.getTransaction();
0569: }
0570:
0571: private RelationTable getRelationTable() throws DeploymentException {
0572: if (relationTable == null) {
0573: relationTable = manager.getSchema().createRelationTable(
0574: this , relatedCMRField);
0575: }
0576: return relationTable;
0577: }
0578:
0579: private Object getPrimaryKey(Object o) {
0580: if (o == null) {
0581: throw new IllegalArgumentException(
0582: "This implementation does not support null members.");
0583: }
0584:
0585: if (!relatedEntity.getLocalInterface().isInstance(o)) {
0586: throw new IllegalArgumentException(
0587: "Argument must be of type "
0588: + entity.getLocalInterface().getName());
0589: }
0590:
0591: EJBLocalObject local = (EJBLocalObject) o;
0592: try {
0593: return local.getPrimaryKey();
0594: } catch (NoSuchObjectLocalException e) {
0595: throw new IllegalArgumentException(e.getMessage());
0596: }
0597: }
0598:
0599: // Inner
0600:
0601: public class SingleValuedFieldState implements FieldState {
0602: private boolean loaded;
0603: private Object value;
0604: private EJBLocalObject localObject;
0605: private boolean modified;
0606:
0607: public void init() {
0608: loaded = true;
0609: }
0610:
0611: public Object getValue(EntityEnterpriseContext ctx) {
0612: Object value = getLoadedValue(ctx);
0613: if (value == null) {
0614: localObject = null;
0615: } else if (localObject == null) {
0616: localObject = relatedContainer.getLocalProxyFactory()
0617: .getEntityEJBLocalObject(value);
0618: }
0619: return localObject;
0620: }
0621:
0622: public void setValue(EntityEnterpriseContext ctx, Object value) {
0623: if (value != null) {
0624: Object relatedId = getPrimaryKey(value);
0625: addRelatedId(ctx, relatedId);
0626: relatedCMRField.invokeAddRelatedId(relatedId, ctx
0627: .getId());
0628: localObject = (EJBLocalObject) value;
0629: } else {
0630: destroyExistingRelationships(ctx);
0631: }
0632: }
0633:
0634: public void cascadeDelete(EntityEnterpriseContext ctx)
0635: throws RemoveException {
0636: if (manager.registerCascadeDelete(ctx.getId(), ctx.getId())) {
0637: EJBLocalObject value = (EJBLocalObject) getValue(ctx);
0638: if (value != null) {
0639: changeValue(null);
0640:
0641: final Object relatedId = value.getPrimaryKey();
0642: final JDBCStoreManager2 relatedManager = (JDBCStoreManager2) relatedEntity
0643: .getManager();
0644:
0645: if (!relatedManager.isCascadeDeleted(relatedId)) {
0646: value.remove();
0647: }
0648: }
0649:
0650: manager.unregisterCascadeDelete(ctx.getId());
0651: }
0652: }
0653:
0654: public void destroyExistingRelationships(
0655: EntityEnterpriseContext ctx) {
0656: Object value = getLoadedValue(ctx);
0657: if (value != null) {
0658: removeRelatedId(ctx, value);
0659: relatedCMRField.invokeRemoveRelatedId(value, ctx
0660: .getId());
0661: }
0662: }
0663:
0664: public boolean removeRelatedId(EntityEnterpriseContext ctx,
0665: Object relatedId) {
0666: if (hasForeignKey()) {
0667: getLoadedValue(ctx);
0668: }
0669:
0670: changeValue(null);
0671: loader.removeRelatedId(ctx, relatedId);
0672:
0673: cacheValue(ctx);
0674:
0675: modified = true;
0676:
0677: return true;
0678: }
0679:
0680: public boolean addRelatedId(EntityEnterpriseContext ctx,
0681: Object relatedId) {
0682: Object value = getLoadedValue(ctx);
0683: if (value != null) {
0684: relatedCMRField.invokeRemoveRelatedId(value, ctx
0685: .getId());
0686: }
0687:
0688: changeValue(relatedId);
0689: loader.addRelatedId(ctx, relatedId);
0690:
0691: cacheValue(ctx);
0692:
0693: modified = true;
0694:
0695: return true;
0696: }
0697:
0698: public void addLoadedPk(Object pk) {
0699: if (loaded) {
0700: throw new IllegalStateException(
0701: entity.getEntityName()
0702: + "."
0703: + getFieldName()
0704: + " single-valued CMR field is already loaded. Check the database for consistancy. "
0705: + " current value=" + value
0706: + ", loaded value=" + pk);
0707: }
0708:
0709: changeValue(pk);
0710: }
0711:
0712: public Object loadFromCache(Object value) {
0713: if (value != null) {
0714: changeValue(NULL_VALUE == value ? null : value);
0715: }
0716: return value;
0717: }
0718:
0719: public Object getCachedValue() {
0720: return value == null ? NULL_VALUE : value;
0721: }
0722:
0723: public void cacheValue(EntityEnterpriseContext ctx) {
0724: PersistentContext pctx = (PersistentContext) ctx
0725: .getPersistenceContext();
0726: pctx.cacheRelations(cmrIndex, this );
0727: }
0728:
0729: public boolean isModified() {
0730: return modified;
0731: }
0732:
0733: // Private
0734:
0735: private void changeValue(Object newValue) {
0736: this .value = newValue;
0737: this .localObject = null;
0738: loaded = true;
0739: }
0740:
0741: private Object getLoadedValue(EntityEnterpriseContext ctx) {
0742: if (!loaded) {
0743: PersistentContext pctx = (PersistentContext) ctx
0744: .getPersistenceContext();
0745: pctx.loadCachedRelations(cmrIndex, this );
0746: if (!loaded) {
0747: loader.load(ctx, this );
0748: loaded = true;
0749: cacheValue(ctx);
0750: }
0751: }
0752: return value;
0753: }
0754: }
0755:
0756: public class CollectionValuedFieldState implements FieldState {
0757: private boolean loaded;
0758: private Set value;
0759: private CMRSet cmrSet;
0760:
0761: private Set removedWhileNotLoaded;
0762: private Set addedWhileNotLoaded;
0763:
0764: private boolean modified;
0765:
0766: public void init() {
0767: loaded = true;
0768: value = new HashSet();
0769: }
0770:
0771: public Object getValue(EntityEnterpriseContext ctx) {
0772: if (cmrSet == null) {
0773: cmrSet = new CMRSet(ctx, this );
0774: }
0775: return cmrSet;
0776: }
0777:
0778: public void setValue(EntityEnterpriseContext ctx, Object value) {
0779: if (value == null) {
0780: throw new IllegalArgumentException(
0781: "Can't set collection-valued CMR field to null: "
0782: + entity.getEntityName() + "."
0783: + getFieldName());
0784: }
0785:
0786: destroyExistingRelationships(ctx);
0787:
0788: Collection newValue = (Collection) value;
0789: if (!newValue.isEmpty()) {
0790: Set copy = new HashSet(newValue);
0791: for (Iterator iter = copy.iterator(); iter.hasNext();) {
0792: Object relatedId = getPrimaryKey(iter.next());
0793: addRelatedId(ctx, relatedId);
0794: relatedCMRField.invokeAddRelatedId(relatedId, ctx
0795: .getId());
0796: loader.addRelatedId(ctx, relatedId);
0797: }
0798: }
0799: }
0800:
0801: public void cascadeDelete(EntityEnterpriseContext ctx)
0802: throws RemoveException {
0803: Collection value = (Collection) getValue(ctx);
0804: if (!value.isEmpty()) {
0805: EJBLocalObject[] locals = (EJBLocalObject[]) value
0806: .toArray();
0807: for (int i = 0; i < locals.length; ++i) {
0808: locals[i].remove();
0809: }
0810: }
0811: }
0812:
0813: public void destroyExistingRelationships(
0814: EntityEnterpriseContext ctx) {
0815: Set value = getLoadedValue(ctx);
0816: if (!value.isEmpty()) {
0817: Object[] copy = value.toArray();
0818: for (int i = 0; i < copy.length; ++i) {
0819: Object relatedId = copy[i];
0820: removeRelatedId(ctx, relatedId);
0821: relatedCMRField.invokeRemoveRelatedId(relatedId,
0822: ctx.getId());
0823: loader.removeRelatedId(ctx, relatedId);
0824: }
0825: }
0826: }
0827:
0828: public boolean removeRelatedId(EntityEnterpriseContext ctx,
0829: Object relatedId) {
0830: boolean removed = false;
0831: if (loaded) {
0832: Set value = getLoadedValue(ctx);
0833: if (!value.isEmpty()) {
0834: removed = value.remove(relatedId);
0835: }
0836: } else {
0837: loadOnlyFromCache(ctx);
0838: if (loaded) {
0839: Set value = getLoadedValue(ctx);
0840: if (!value.isEmpty()) {
0841: removed = value.remove(relatedId);
0842: }
0843: } else {
0844: removed = removeWhileNotLoaded(relatedId);
0845: }
0846: }
0847:
0848: modified = true;
0849:
0850: if (removed) {
0851: ((PersistentContext) ctx.getPersistenceContext())
0852: .setDirtyRelations();
0853: }
0854:
0855: return removed;
0856: }
0857:
0858: public boolean addRelatedId(EntityEnterpriseContext ctx,
0859: Object relatedId) {
0860: boolean added;
0861: if (loaded) {
0862: Set value = getLoadedValue(ctx);
0863: added = value.add(relatedId);
0864: } else {
0865: loadOnlyFromCache(ctx);
0866: if (loaded) {
0867: Set value = getLoadedValue(ctx);
0868: added = value.add(relatedId);
0869: } else {
0870: added = addWhileNotLoaded(relatedId);
0871: }
0872: }
0873:
0874: modified = true;
0875:
0876: if (added) {
0877: ((PersistentContext) ctx.getPersistenceContext())
0878: .setDirtyRelations();
0879: }
0880:
0881: return added;
0882: }
0883:
0884: public void addLoadedPk(Object pk) {
0885: if (loaded) {
0886: throw new IllegalStateException(
0887: entity.getEntityName()
0888: + "."
0889: + getFieldName()
0890: + " collection-valued CMR field is already loaded. Check the database for consistancy. "
0891: + " current value=" + value
0892: + ", loaded value=" + pk);
0893: }
0894:
0895: if (pk != null) {
0896: value.add(pk);
0897: }
0898: }
0899:
0900: public Object loadFromCache(Object value) {
0901: if (value != null) {
0902: value = this .value = new HashSet((Set) value);
0903: loaded = true;
0904: }
0905: return value;
0906: }
0907:
0908: public Object getCachedValue() {
0909: return value;
0910: }
0911:
0912: public void cacheValue(EntityEnterpriseContext ctx) {
0913: PersistentContext pctx = (PersistentContext) ctx
0914: .getPersistenceContext();
0915: pctx.cacheRelations(cmrIndex, this );
0916: }
0917:
0918: public boolean isModified() {
0919: return modified;
0920: }
0921:
0922: // Private
0923:
0924: private Set getLoadedValue(EntityEnterpriseContext ctx) {
0925: if (!loaded) {
0926: loadOnlyFromCache(ctx);
0927:
0928: if (!loaded) {
0929: if (value == null || value == Collections.EMPTY_SET) {
0930: value = new HashSet();
0931: }
0932:
0933: loader.load(ctx, this );
0934: cacheValue(ctx);
0935:
0936: loaded = true;
0937: }
0938:
0939: if (addedWhileNotLoaded != null) {
0940: value.addAll(addedWhileNotLoaded);
0941: addedWhileNotLoaded = null;
0942: }
0943:
0944: if (removedWhileNotLoaded != null) {
0945: value.removeAll(removedWhileNotLoaded);
0946: removedWhileNotLoaded = null;
0947: }
0948: }
0949: return value;
0950: }
0951:
0952: private void loadOnlyFromCache(EntityEnterpriseContext ctx) {
0953: PersistentContext pctx = (PersistentContext) ctx
0954: .getPersistenceContext();
0955: if (pctx == null) {
0956: throw new EJBException(
0957: "Persistence context is not available! Make sure the CMR collection is accessed in the transaction it was obtained.");
0958: }
0959: pctx.loadCachedRelations(cmrIndex, this );
0960: }
0961:
0962: private boolean removeWhileNotLoaded(Object relatedId) {
0963: boolean removed = false;
0964: if (addedWhileNotLoaded != null) {
0965: removed = addedWhileNotLoaded.remove(relatedId);
0966: }
0967:
0968: if (!removed) {
0969: if (removedWhileNotLoaded == null) {
0970: removedWhileNotLoaded = new HashSet();
0971: }
0972: removed = removedWhileNotLoaded.add(relatedId);
0973: }
0974:
0975: if (log.isTraceEnabled() && removed) {
0976: log.trace("removed while not loaded: relatedId="
0977: + relatedId);
0978: }
0979:
0980: return removed;
0981: }
0982:
0983: private boolean addWhileNotLoaded(Object relatedId) {
0984: boolean added = false;
0985: if (removedWhileNotLoaded != null) {
0986: added = removedWhileNotLoaded.remove(relatedId);
0987: }
0988:
0989: if (!added) {
0990: if (addedWhileNotLoaded == null) {
0991: addedWhileNotLoaded = new HashSet();
0992: }
0993: added = addedWhileNotLoaded.add(relatedId);
0994: }
0995:
0996: if (log.isTraceEnabled() && added) {
0997: log.trace("added while not loaded: relatedId="
0998: + relatedId);
0999: }
1000:
1001: return added;
1002: }
1003: }
1004:
1005: public interface FieldState extends Cache.CacheLoader {
1006: Object NULL_VALUE = new Object();
1007:
1008: void init();
1009:
1010: Object getValue(EntityEnterpriseContext ctx);
1011:
1012: void cascadeDelete(EntityEnterpriseContext ctx)
1013: throws RemoveException;
1014:
1015: void destroyExistingRelationships(EntityEnterpriseContext ctx);
1016:
1017: void setValue(EntityEnterpriseContext ctx, Object value);
1018:
1019: boolean removeRelatedId(EntityEnterpriseContext ctx,
1020: Object relatedId);
1021:
1022: boolean addRelatedId(EntityEnterpriseContext ctx, Object value);
1023:
1024: void addLoadedPk(Object pk);
1025:
1026: void cacheValue(EntityEnterpriseContext ctx);
1027:
1028: boolean isModified();
1029: }
1030:
1031: private class RelationTableLoader implements CMRFieldLoader {
1032: private final String loadSql;
1033:
1034: public RelationTableLoader() {
1035: StringBuffer sql = new StringBuffer();
1036: sql.append("select ");
1037:
1038: String relatedTable = relatedEntity.getQualifiedTableName();
1039: String relationTable = metadata.getRelationMetaData()
1040: .getDefaultTableName();
1041:
1042: relatedEntity.getTable().appendColumnNames(
1043: (JDBCCMPFieldBridge2[]) relatedEntity
1044: .getTableFields(), relatedTable, sql);
1045: sql.append(" from ").append(relatedTable).append(
1046: " inner join ").append(relationTable)
1047: .append(" on ");
1048:
1049: JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) relatedEntity
1050: .getPrimaryKeyFields();
1051: for (int i = 0; i < pkFields.length; ++i) {
1052: if (i > 0) {
1053: sql.append(" and ");
1054: }
1055:
1056: sql.append(relatedTable).append('.').append(
1057: pkFields[i].getColumnName()).append('=')
1058: .append(relationTable).append('.').append(
1059: relatedCMRField.tableKeyFields[i]
1060: .getColumnName());
1061: }
1062:
1063: /*
1064: sql.append(" inner join ")
1065: .append(myTable)
1066: .append(" on ");
1067:
1068: String myTable = entity.getQualifiedTableName();
1069: pkFields = entity.getPrimaryKeyFields();
1070: for(int i = 0; i < pkFields.length; ++i)
1071: {
1072: if(i > 0)
1073: {
1074: sql.append(", ");
1075: }
1076:
1077: sql.append(myTable).append('.').append(pkFields[i].getColumnName())
1078: .append('=')
1079: .append(relationTable).append('.').append(tableKeyFields[i].getColumnName());
1080: }
1081: */
1082:
1083: sql.append(" where ");
1084: for (int i = 0; i < tableKeyFields.length; ++i) {
1085: if (i > 0) {
1086: sql.append(" and ");
1087: }
1088:
1089: sql.append(relationTable).append('.').append(
1090: tableKeyFields[i].getColumnName()).append("=?");
1091: }
1092:
1093: loadSql = sql.toString();
1094:
1095: if (log.isTraceEnabled()) {
1096: log.trace("load sql: " + loadSql);
1097: }
1098: }
1099:
1100: public void load(EntityEnterpriseContext ctx, FieldState state) {
1101: Object value;
1102: EntityTable relatedTable = relatedEntity.getTable();
1103:
1104: Connection con = null;
1105: PreparedStatement ps = null;
1106: ResultSet rs = null;
1107: try {
1108: if (log.isDebugEnabled()) {
1109: log.debug("executing: " + loadSql);
1110: }
1111:
1112: con = relatedTable.getDataSource().getConnection();
1113: ps = con.prepareStatement(loadSql);
1114:
1115: JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity
1116: .getPrimaryKeyFields();
1117:
1118: Object myPk = ctx.getId();
1119: int paramInd = 1;
1120: for (int i = 0; i < pkFields.length; ++i) {
1121: JDBCCMPFieldBridge2 pkField = pkFields[i];
1122: Object fieldValue = pkField
1123: .getPrimaryKeyValue(myPk);
1124:
1125: JDBCCMPFieldBridge2 relatedFkField = tableKeyFields[i];
1126: relatedFkField.setArgumentParameters(ps,
1127: paramInd++, fieldValue);
1128: }
1129:
1130: rs = ps.executeQuery();
1131:
1132: while (rs.next()) {
1133: value = relatedTable.loadRow(rs, false);
1134: state.addLoadedPk(value);
1135: }
1136: } catch (SQLException e) {
1137: log.error("Failed to load related role: ejb-name="
1138: + entity.getEntityName() + ", cmr-field="
1139: + getFieldName() + ": " + e.getMessage(), e);
1140: throw new EJBException(
1141: "Failed to load related role: ejb-name="
1142: + entity.getEntityName()
1143: + ", cmr-field=" + getFieldName()
1144: + ": " + e.getMessage(), e);
1145: } finally {
1146: JDBCUtil.safeClose(rs);
1147: JDBCUtil.safeClose(ps);
1148: JDBCUtil.safeClose(con);
1149: }
1150: }
1151:
1152: public void removeRelatedId(EntityEnterpriseContext ctx,
1153: Object relatedId) {
1154: relationTable.removeRelation(JDBCCMRFieldBridge2.this , ctx
1155: .getId(), relatedCMRField, relatedId);
1156: }
1157:
1158: public void addRelatedId(EntityEnterpriseContext ctx,
1159: Object relatedId) {
1160: relationTable.addRelation(JDBCCMRFieldBridge2.this , ctx
1161: .getId(), relatedCMRField, relatedId);
1162: }
1163: }
1164:
1165: private class ForeignKeyLoader implements CMRFieldLoader {
1166: private final String loadSql;
1167:
1168: public ForeignKeyLoader() {
1169: StringBuffer sql = new StringBuffer();
1170: sql.append("select ");
1171: relatedEntity.getTable().appendColumnNames(
1172: (JDBCCMPFieldBridge2[]) relatedEntity
1173: .getTableFields(), null, sql);
1174: sql.append(" from ").append(
1175: relatedEntity.getQualifiedTableName()).append(
1176: " where ");
1177:
1178: JDBCCMPFieldBridge2[] relatedFkFields = relatedCMRField.foreignKeyFields;
1179: sql.append(relatedFkFields[0].getColumnName()).append("=?");
1180: for (int i = 1; i < relatedFkFields.length; ++i) {
1181: JDBCCMPFieldBridge2 relatedFkField = relatedFkFields[i];
1182: sql.append(" and ").append(
1183: relatedFkField.getColumnName()).append("=?");
1184: }
1185:
1186: loadSql = sql.toString();
1187:
1188: if (log.isTraceEnabled()) {
1189: log.trace("load sql: " + loadSql);
1190: }
1191: }
1192:
1193: public void load(EntityEnterpriseContext ctx, FieldState state) {
1194: Object value;
1195: EntityTable relatedTable = relatedEntity.getTable();
1196:
1197: Connection con = null;
1198: PreparedStatement ps = null;
1199: ResultSet rs = null;
1200: try {
1201: if (log.isDebugEnabled()) {
1202: log.debug("executing: " + loadSql);
1203: }
1204:
1205: con = relatedTable.getDataSource().getConnection();
1206: ps = con.prepareStatement(loadSql);
1207:
1208: JDBCCMPFieldBridge2[] relatedFkFields = relatedCMRField.foreignKeyFields;
1209: JDBCCMPFieldBridge2[] myPkFields = relatedCMRField.relatedPKFields;
1210:
1211: Object myPk = ctx.getId();
1212: int paramInd = 1;
1213: for (int i = 0; i < relatedFkFields.length; ++i) {
1214: JDBCCMPFieldBridge2 myPkField = myPkFields[i];
1215: Object fieldValue = myPkField
1216: .getPrimaryKeyValue(myPk);
1217:
1218: JDBCCMPFieldBridge2 relatedFkField = relatedFkFields[i];
1219: relatedFkField.setArgumentParameters(ps,
1220: paramInd++, fieldValue);
1221: }
1222:
1223: rs = ps.executeQuery();
1224:
1225: while (rs.next()) {
1226: value = relatedTable.loadRow(rs, false);
1227: state.addLoadedPk(value);
1228: }
1229: } catch (SQLException e) {
1230: log.error("Failed to load related role: ejb-name="
1231: + entity.getEntityName() + ", cmr-field="
1232: + getFieldName() + ": " + e.getMessage(), e);
1233: throw new EJBException(
1234: "Failed to load related role: ejb-name="
1235: + entity.getEntityName()
1236: + ", cmr-field=" + getFieldName()
1237: + ": " + e.getMessage(), e);
1238: } finally {
1239: JDBCUtil.safeClose(rs);
1240: JDBCUtil.safeClose(ps);
1241: JDBCUtil.safeClose(con);
1242: }
1243: }
1244:
1245: public void removeRelatedId(EntityEnterpriseContext ctx,
1246: Object relatedId) {
1247: }
1248:
1249: public void addRelatedId(EntityEnterpriseContext ctx,
1250: Object relatedId) {
1251: }
1252: }
1253:
1254: private class ContextForeignKeyLoader implements CMRFieldLoader {
1255: public void load(EntityEnterpriseContext ctx, FieldState state) {
1256: Object relatedId = null;
1257: for (int i = 0; i < foreignKeyFields.length; ++i) {
1258: JDBCCMPFieldBridge2 fkField = foreignKeyFields[i];
1259: Object fkFieldValue = fkField.getValue(ctx);
1260: if (fkFieldValue == null) {
1261: break;
1262: }
1263:
1264: JDBCCMPFieldBridge2 relatedPKField = relatedPKFields[i];
1265: relatedId = relatedPKField.setPrimaryKeyValue(
1266: relatedId, fkFieldValue);
1267: }
1268:
1269: state.addLoadedPk(relatedId);
1270: }
1271:
1272: public void removeRelatedId(EntityEnterpriseContext ctx,
1273: Object relatedId) {
1274: for (int i = 0; i < foreignKeyFields.length; ++i) {
1275: foreignKeyFields[i].setValueInternal(ctx, null,
1276: fkConstraint == null);
1277: }
1278:
1279: if (fkConstraint != null) {
1280: PersistentContext pctx = (PersistentContext) ctx
1281: .getPersistenceContext();
1282: pctx.nullForeignKey(fkConstraint);
1283: }
1284: }
1285:
1286: public void addRelatedId(EntityEnterpriseContext ctx,
1287: Object relatedId) {
1288: final boolean markDirty = relatedId != null
1289: || fkConstraint == null;
1290: for (int i = 0; i < foreignKeyFields.length; ++i) {
1291: JDBCCMPFieldBridge2 relatedPKField = relatedPKFields[i];
1292: Object fieldValue = relatedPKField
1293: .getPrimaryKeyValue(relatedId);
1294: foreignKeyFields[i].setValueInternal(ctx, fieldValue,
1295: markDirty);
1296: }
1297:
1298: if (fkConstraint != null) {
1299: PersistentContext pctx = (PersistentContext) ctx
1300: .getPersistenceContext();
1301: if (relatedId == null) {
1302: pctx.nullForeignKey(fkConstraint);
1303: } else {
1304: pctx.nonNullForeignKey(fkConstraint);
1305: }
1306: }
1307: }
1308: }
1309:
1310: private interface CMRFieldLoader {
1311: void load(EntityEnterpriseContext ctx, FieldState state);
1312:
1313: void removeRelatedId(EntityEnterpriseContext ctx,
1314: Object relatedId);
1315:
1316: void addRelatedId(EntityEnterpriseContext ctx, Object relatedId);
1317: }
1318:
1319: private class CMRSet implements Set {
1320: private final EntityEnterpriseContext ctx;
1321: private final CollectionValuedFieldState state;
1322:
1323: public CMRSet(EntityEnterpriseContext ctx,
1324: CollectionValuedFieldState state) {
1325: this .ctx = ctx;
1326: this .state = state;
1327: }
1328:
1329: public int size() {
1330: return state.getLoadedValue(ctx).size();
1331: }
1332:
1333: public void clear() {
1334: destroyExistingRelationships(ctx);
1335: }
1336:
1337: public boolean isEmpty() {
1338: return size() == 0;
1339: }
1340:
1341: public boolean add(Object o) {
1342: Object relatedId = getPrimaryKey(o);
1343: boolean modified = addRelatedId(ctx, relatedId);
1344:
1345: if (modified) {
1346: relatedCMRField.invokeAddRelatedId(relatedId, ctx
1347: .getId());
1348: loader.addRelatedId(ctx, relatedId);
1349: }
1350:
1351: return modified;
1352: }
1353:
1354: public boolean contains(Object o) {
1355: Object pk = getPrimaryKey(o);
1356: return state.getLoadedValue(ctx).contains(pk);
1357: }
1358:
1359: public boolean remove(Object o) {
1360: Object relatedId = getPrimaryKey(o);
1361: return removeById(relatedId);
1362: }
1363:
1364: public boolean addAll(Collection c) {
1365: if (c == null || c.isEmpty()) {
1366: return false;
1367: }
1368:
1369: boolean modified = false;
1370: Object[] copy = c.toArray();
1371: for (int i = 0; i < copy.length; ++i) {
1372: // not modified || add()
1373: modified = add(copy[i]) || modified;
1374: }
1375:
1376: return modified;
1377: }
1378:
1379: public boolean containsAll(Collection c) {
1380: if (c == null || c.isEmpty()) {
1381: return true;
1382: }
1383:
1384: Set ids = argumentToIdSet(c);
1385: return state.getLoadedValue(ctx).containsAll(ids);
1386: }
1387:
1388: public boolean removeAll(Collection c) {
1389: if (c == null || c.isEmpty()) {
1390: return false;
1391: }
1392:
1393: boolean modified = false;
1394: Object[] copy = c.toArray();
1395: for (int i = 0; i < copy.length; ++i) {
1396: modified = remove(copy[i]) || modified;
1397: }
1398:
1399: return modified;
1400: }
1401:
1402: public boolean retainAll(Collection c) {
1403: Set value = state.getLoadedValue(ctx);
1404: if (c == null || c.isEmpty()) {
1405: if (value.isEmpty()) {
1406: return false;
1407: } else {
1408: clear();
1409: }
1410: }
1411:
1412: boolean modified = false;
1413: Set idSet = argumentToIdSet(c);
1414: Object[] valueCopy = value.toArray();
1415: for (int i = 0; i < valueCopy.length; ++i) {
1416: Object id = valueCopy[i];
1417: if (!idSet.contains(id)) {
1418: removeById(id);
1419: modified = true;
1420: }
1421: }
1422:
1423: return modified;
1424: }
1425:
1426: public Iterator iterator() {
1427: return new Iterator() {
1428: // todo get rid of copying
1429: private final Iterator idIter = new HashSet(state
1430: .getLoadedValue(ctx)).iterator();
1431: private Object curId;
1432:
1433: public void remove() {
1434: try {
1435: idIter.remove();
1436: } catch (ConcurrentModificationException e) {
1437: throw new IllegalStateException(e.getMessage());
1438: }
1439:
1440: removeById(curId);
1441: }
1442:
1443: public boolean hasNext() {
1444: try {
1445: return idIter.hasNext();
1446: } catch (ConcurrentModificationException e) {
1447: throw new IllegalStateException(e.getMessage());
1448: }
1449: }
1450:
1451: public Object next() {
1452: try {
1453: curId = idIter.next();
1454: } catch (ConcurrentModificationException e) {
1455: throw new IllegalStateException(e.getMessage());
1456: }
1457:
1458: return relatedContainer.getLocalProxyFactory()
1459: .getEntityEJBLocalObject(curId);
1460: }
1461: };
1462: }
1463:
1464: public Object[] toArray() {
1465: Set value = state.getLoadedValue(ctx);
1466:
1467: Object[] result = (Object[]) Array.newInstance(
1468: relatedEntity.getLocalInterface(), value.size());
1469:
1470: LocalProxyFactory relatedPF = relatedContainer
1471: .getLocalProxyFactory();
1472: int i = 0;
1473: for (Iterator iter = value.iterator(); iter.hasNext();) {
1474: Object id = iter.next();
1475: result[i++] = relatedPF.getEntityEJBLocalObject(id);
1476: }
1477:
1478: return result;
1479: }
1480:
1481: public Object[] toArray(Object a[]) {
1482: Set value = state.getLoadedValue(ctx);
1483: if (a == null || a.length < value.size()) {
1484: a = (Object[]) Array.newInstance(entity
1485: .getLocalInterface(), value.size());
1486: }
1487:
1488: LocalProxyFactory relatedPF = relatedContainer
1489: .getLocalProxyFactory();
1490: int i = 0;
1491: for (Iterator iter = value.iterator(); iter.hasNext();) {
1492: Object id = iter.next();
1493: a[i++] = relatedPF.getEntityEJBLocalObject(id);
1494: }
1495:
1496: return a;
1497: }
1498:
1499: public String toString() {
1500: return state.getLoadedValue(ctx).toString();
1501: }
1502:
1503: // Private
1504:
1505: private boolean removeById(Object relatedId) {
1506: boolean modified = removeRelatedId(ctx, relatedId);
1507: if (modified) {
1508: relatedCMRField.invokeRemoveRelatedId(relatedId, ctx
1509: .getId());
1510: loader.removeRelatedId(ctx, relatedId);
1511: }
1512: return modified;
1513: }
1514:
1515: private Set argumentToIdSet(Collection c) {
1516: Set ids = new HashSet();
1517: for (Iterator iter = c.iterator(); iter.hasNext();) {
1518: Object pk = getPrimaryKey(iter.next());
1519: ids.add(pk);
1520: }
1521: return ids;
1522: }
1523: }
1524:
1525: interface SecurityActions {
1526: class UTIL {
1527: static SecurityActions getSecurityActions() {
1528: return System.getSecurityManager() == null ? NON_PRIVILEGED
1529: : PRIVILEGED;
1530: }
1531: }
1532:
1533: SecurityActions NON_PRIVILEGED = new SecurityActions() {
1534: public Principal getPrincipal() {
1535: return SecurityAssociation.getPrincipal();
1536: }
1537:
1538: public Object getCredential() {
1539: return SecurityAssociation.getCredential();
1540: }
1541: };
1542:
1543: SecurityActions PRIVILEGED = new SecurityActions() {
1544: private final PrivilegedAction getPrincipalAction = new PrivilegedAction() {
1545: public Object run() {
1546: return SecurityAssociation.getPrincipal();
1547: }
1548: };
1549:
1550: private final PrivilegedAction getCredentialAction = new PrivilegedAction() {
1551: public Object run() {
1552: return SecurityAssociation.getCredential();
1553: }
1554: };
1555:
1556: public Principal getPrincipal() {
1557: return (Principal) AccessController
1558: .doPrivileged(getPrincipalAction);
1559: }
1560:
1561: public Object getCredential() {
1562: return AccessController
1563: .doPrivileged(getCredentialAction);
1564: }
1565: };
1566:
1567: Principal getPrincipal();
1568:
1569: Object getCredential();
1570: }
1571: }
|