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.jdbc.bridge;
0023:
0024: import java.sql.PreparedStatement;
0025: import java.sql.ResultSet;
0026:
0027: import java.util.ArrayList;
0028: import java.util.Collection;
0029: import java.util.Collections;
0030: import java.util.HashMap;
0031: import java.util.Iterator;
0032: import java.util.List;
0033: import java.util.Map;
0034: import java.util.Arrays;
0035: import java.util.NoSuchElementException;
0036: import java.rmi.RemoteException;
0037:
0038: import javax.ejb.EJBException;
0039: import javax.ejb.RemoveException;
0040: import javax.sql.DataSource;
0041: import javax.naming.InitialContext;
0042: import javax.naming.NamingException;
0043:
0044: import org.jboss.deployment.DeploymentException;
0045: import org.jboss.ejb.EntityEnterpriseContext;
0046:
0047: import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext;
0048: import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager;
0049: import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
0050: import org.jboss.ejb.plugins.cmp.jdbc.LockingStrategy;
0051: import org.jboss.ejb.plugins.cmp.jdbc.JDBCTypeFactory;
0052: import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
0053:
0054: import org.jboss.ejb.plugins.cmp.bridge.EntityBridgeInvocationHandler;
0055: import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
0056:
0057: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCAuditMetaData;
0058: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
0059: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
0060: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData;
0061: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
0062: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCOptimisticLockingMetaData;
0063: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
0064: import org.jboss.proxy.compiler.Proxies;
0065: import org.jboss.proxy.compiler.InvocationHandler;
0066: import org.jboss.logging.Logger;
0067:
0068: /**
0069: * JDBCEntityBridge follows the Bridge pattern [Gamma et. al, 1995].
0070: * The main job of this class is to construct the bridge from entity meta data.
0071: *
0072: * Life-cycle:
0073: * Undefined. Should be tied to CMPStoreManager.
0074: *
0075: * Multiplicity:
0076: * One per cmp entity bean type.
0077: *
0078: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
0079: * @author <a href="mailto:loubyansky@ua.fm">Alex Loubyansky</a>
0080: * @author <a href="mailto:heiko.rupp@cellent.de">Heiko W. Rupp</a>
0081: * @version $Revision: 57209 $
0082: */
0083: public class JDBCEntityBridge implements JDBCAbstractEntityBridge {
0084: public final static byte LOADED = 1;
0085: public final static byte LOAD_REQUIRED = 2;
0086: public final static byte DIRTY = 4;
0087: public final static byte CHECK_DIRTY = 8;
0088: public final static byte LOCKED = 16;
0089: public final static byte ADD_TO_SET_ON_UPDATE = 32;
0090: public final static byte ADD_TO_WHERE_ON_UPDATE = 64;
0091:
0092: private static final String DEFAULT_LOADGROUP_NAME = "*";
0093:
0094: private JDBCEntityMetaData metadata;
0095: private JDBCStoreManager manager;
0096: private DataSource dataSource;
0097: private String qualifiedTableName;
0098: private String tableName;
0099:
0100: /** primary key fields (not added to cmpFields) */
0101: private final String primaryKeyFieldName;
0102: private final Class primaryKeyClass;
0103: private JDBCCMPFieldBridge[] primaryKeyFields;
0104: /** CMP fields */
0105: private JDBCCMPFieldBridge[] cmpFields;
0106: /** CMR fields */
0107: private JDBCCMRFieldBridge[] cmrFields;
0108: /** table fields */
0109: private JDBCCMPFieldBridge[] tableFields;
0110:
0111: /** used for optimistic locking. (added to cmpFields) */
0112: private JDBCCMPFieldBridge versionField;
0113:
0114: // Audit fields (added to cmpFields)
0115: private JDBCCMPFieldBridge createdPrincipalField;
0116: private JDBCCMPFieldBridge createdTimeField;
0117: private JDBCCMPFieldBridge updatedPrincipalField;
0118: private JDBCCMPFieldBridge updatedTimeField;
0119:
0120: private Map selectorsByMethod;
0121:
0122: /** Load group is a boolean array with tableFields.length elements. True means the element is in the group. */
0123: private Map loadGroupMasks;
0124: private List lazyLoadGroupMasks;
0125: private boolean[] eagerLoadGroupMask;
0126: private boolean[] defaultLockGroupMask;
0127:
0128: private int jdbcContextSize;
0129:
0130: private final Logger log;
0131:
0132: public JDBCEntityBridge(JDBCEntityMetaData metadata,
0133: JDBCStoreManager manager) throws DeploymentException {
0134: this .metadata = metadata;
0135: this .manager = manager;
0136: primaryKeyFieldName = metadata.getPrimaryKeyFieldName();
0137: primaryKeyClass = metadata.getPrimaryKeyClass();
0138: log = Logger.getLogger(this .getClass().getName() + "."
0139: + metadata.getName());
0140: }
0141:
0142: public void init() throws DeploymentException {
0143: try {
0144: InitialContext ic = new InitialContext();
0145: dataSource = (DataSource) ic.lookup(metadata
0146: .getDataSourceName());
0147: } catch (NamingException e) {
0148: throw new DeploymentException(
0149: "Error: can't find data source: "
0150: + metadata.getDataSourceName(), e);
0151: }
0152:
0153: qualifiedTableName = SQLUtil.fixTableName(metadata
0154: .getDefaultTableName(), dataSource);
0155: int dotIndex = qualifiedTableName.indexOf('.');
0156: tableName = dotIndex == -1 ? qualifiedTableName
0157: : qualifiedTableName.substring(dotIndex + 1);
0158:
0159: // CMP fields
0160: loadCMPFields(metadata);
0161:
0162: // CMR fields
0163: loadCMRFields(metadata);
0164:
0165: // create locking field
0166: JDBCOptimisticLockingMetaData lockMetaData = metadata
0167: .getOptimisticLocking();
0168: if (lockMetaData != null
0169: && lockMetaData.getLockingField() != null) {
0170: Integer strategy = lockMetaData.getLockingStrategy();
0171: JDBCCMPFieldMetaData versionMD = lockMetaData
0172: .getLockingField();
0173:
0174: versionField = getCMPFieldByName(versionMD.getFieldName());
0175: boolean hidden = versionField == null;
0176: if (strategy == JDBCOptimisticLockingMetaData.VERSION_COLUMN_STRATEGY) {
0177: if (hidden)
0178: versionField = new JDBCLongVersionFieldBridge(
0179: manager, versionMD);
0180: else
0181: versionField = new JDBCLongVersionFieldBridge(
0182: (JDBCCMP2xFieldBridge) versionField);
0183: } else if (strategy == JDBCOptimisticLockingMetaData.TIMESTAMP_COLUMN_STRATEGY) {
0184: if (hidden)
0185: versionField = new JDBCTimestampVersionFieldBridge(
0186: manager, versionMD);
0187: else
0188: versionField = new JDBCTimestampVersionFieldBridge(
0189: (JDBCCMP2xFieldBridge) versionField);
0190: } else if (strategy == JDBCOptimisticLockingMetaData.KEYGENERATOR_COLUMN_STRATEGY) {
0191: if (hidden)
0192: versionField = new JDBCKeyGenVersionFieldBridge(
0193: manager, versionMD, lockMetaData
0194: .getKeyGeneratorFactory());
0195: else
0196: versionField = new JDBCKeyGenVersionFieldBridge(
0197: (JDBCCMP2xFieldBridge) versionField,
0198: lockMetaData.getKeyGeneratorFactory());
0199: }
0200:
0201: if (hidden)
0202: addCMPField(versionField);
0203: else
0204: tableFields[versionField.getTableIndex()] = versionField;
0205: }
0206:
0207: // audit fields
0208: JDBCAuditMetaData auditMetaData = metadata.getAudit();
0209: if (auditMetaData != null) {
0210: JDBCCMPFieldMetaData auditField = auditMetaData
0211: .getCreatedPrincipalField();
0212: if (auditField != null) {
0213: createdPrincipalField = getCMPFieldByName(auditField
0214: .getFieldName());
0215: if (createdPrincipalField == null) {
0216: createdPrincipalField = new JDBCCMP2xFieldBridge(
0217: manager, auditField);
0218: addCMPField(createdPrincipalField);
0219: }
0220: } else {
0221: createdPrincipalField = null;
0222: }
0223:
0224: auditField = auditMetaData.getCreatedTimeField();
0225: if (auditField != null) {
0226: createdTimeField = getCMPFieldByName(auditField
0227: .getFieldName());
0228: if (createdTimeField == null) {
0229: createdTimeField = new JDBCCMP2xFieldBridge(
0230: manager, auditField,
0231: JDBCTypeFactory.EQUALS, false);
0232: addCMPField(createdTimeField);
0233: } else {
0234: // just to override state factory and check-dirty-after-get
0235: createdTimeField = new JDBCCMP2xFieldBridge(
0236: (JDBCCMP2xFieldBridge) createdTimeField,
0237: JDBCTypeFactory.EQUALS, false);
0238: tableFields[createdTimeField.getTableIndex()] = createdTimeField;
0239: }
0240: } else {
0241: createdTimeField = null;
0242: }
0243:
0244: auditField = auditMetaData.getUpdatedPrincipalField();
0245: if (auditField != null) {
0246: updatedPrincipalField = getCMPFieldByName(auditField
0247: .getFieldName());
0248: if (updatedPrincipalField == null) {
0249: updatedPrincipalField = new JDBCCMP2xUpdatedPrincipalFieldBridge(
0250: manager, auditField);
0251: addCMPField(updatedPrincipalField);
0252: } else {
0253: updatedPrincipalField = new JDBCCMP2xUpdatedPrincipalFieldBridge(
0254: (JDBCCMP2xFieldBridge) updatedPrincipalField);
0255: tableFields[updatedPrincipalField.getTableIndex()] = updatedPrincipalField;
0256: }
0257: } else {
0258: updatedPrincipalField = null;
0259: }
0260:
0261: auditField = auditMetaData.getUpdatedTimeField();
0262: if (auditField != null) {
0263: updatedTimeField = getCMPFieldByName(auditField
0264: .getFieldName());
0265: if (updatedTimeField == null) {
0266: updatedTimeField = new JDBCCMP2xUpdatedTimeFieldBridge(
0267: manager, auditField);
0268: addCMPField(updatedTimeField);
0269: } else {
0270: updatedTimeField = new JDBCCMP2xUpdatedTimeFieldBridge(
0271: (JDBCCMP2xFieldBridge) updatedTimeField);
0272: tableFields[updatedTimeField.getTableIndex()] = updatedTimeField;
0273: }
0274: } else {
0275: updatedTimeField = null;
0276: }
0277: }
0278:
0279: // ejbSelect methods
0280: loadSelectors(metadata);
0281: }
0282:
0283: public void resolveRelationships() throws DeploymentException {
0284: for (int i = 0; i < cmrFields.length; ++i)
0285: cmrFields[i].resolveRelationship();
0286:
0287: // load groups: cannot be created until relationships have
0288: // been resolved because loadgroups must check for foreign keys
0289: loadLoadGroups(metadata);
0290: loadEagerLoadGroup(metadata);
0291: loadLazyLoadGroups(metadata);
0292: }
0293:
0294: /**
0295: * The third phase of deployment. The method is called when relationships are already resolved.
0296: * @throws DeploymentException
0297: */
0298: public void start() throws DeploymentException {
0299: for (int i = 0; i < cmrFields.length; ++i) {
0300: cmrFields[i].start();
0301: }
0302: }
0303:
0304: public boolean removeFromRelations(EntityEnterpriseContext ctx,
0305: Object[] oldRelations) {
0306: boolean removed = false;
0307: for (int i = 0; i < cmrFields.length; ++i) {
0308: if (cmrFields[i].removeFromRelations(ctx, oldRelations))
0309: removed = true;
0310: }
0311: return removed;
0312: }
0313:
0314: public void cascadeDelete(EntityEnterpriseContext ctx,
0315: Map oldRelations) throws RemoveException, RemoteException {
0316: for (int i = 0; i < cmrFields.length; ++i) {
0317: JDBCCMRFieldBridge cmrField = cmrFields[i];
0318: Object value = oldRelations.get(cmrField);
0319: if (value != null)
0320: cmrField.cascadeDelete(ctx, (List) value);
0321: }
0322: }
0323:
0324: public String getEntityName() {
0325: return metadata.getName();
0326: }
0327:
0328: public String getAbstractSchemaName() {
0329: return metadata.getAbstractSchemaName();
0330: }
0331:
0332: public Class getRemoteInterface() {
0333: return metadata.getRemoteClass();
0334: }
0335:
0336: public Class getLocalInterface() {
0337: return metadata.getLocalClass();
0338: }
0339:
0340: public JDBCEntityMetaData getMetaData() {
0341: return metadata;
0342: }
0343:
0344: public JDBCEntityPersistenceStore getManager() {
0345: return manager;
0346: }
0347:
0348: /**
0349: * Returns the datasource for this entity.
0350: */
0351: public DataSource getDataSource() {
0352: return dataSource;
0353: }
0354:
0355: public String getTableName() {
0356: return tableName;
0357: }
0358:
0359: public String getQualifiedTableName() {
0360: return qualifiedTableName;
0361: }
0362:
0363: public Class getPrimaryKeyClass() {
0364: return primaryKeyClass;
0365: }
0366:
0367: public int getListCacheMax() {
0368: return metadata.getListCacheMax();
0369: }
0370:
0371: public int getFetchSize() {
0372: return metadata.getFetchSize();
0373: }
0374:
0375: public Object createPrimaryKeyInstance() {
0376: if (primaryKeyFieldName == null) {
0377: try {
0378: return primaryKeyClass.newInstance();
0379: } catch (Exception e) {
0380: throw new EJBException(
0381: "Error creating primary key instance: ", e);
0382: }
0383: }
0384: return null;
0385: }
0386:
0387: public JDBCFieldBridge[] getPrimaryKeyFields() {
0388: return primaryKeyFields;
0389: }
0390:
0391: /**
0392: * This method is called only at deployment time, not called at runtime.
0393: * @return the list of all the fields.
0394: */
0395: public List getFields() {
0396: int fieldsTotal = primaryKeyFields.length + cmpFields.length
0397: + cmrFields.length;
0398: JDBCFieldBridge[] fields = new JDBCFieldBridge[fieldsTotal];
0399: int position = 0;
0400: // primary key fields
0401: System.arraycopy(primaryKeyFields, 0, fields, position,
0402: primaryKeyFields.length);
0403: position += primaryKeyFields.length;
0404: // cmp fields
0405: System.arraycopy(cmpFields, 0, fields, position,
0406: cmpFields.length);
0407: position += cmpFields.length;
0408: // cmr fields
0409: System.arraycopy(cmrFields, 0, fields, position,
0410: cmrFields.length);
0411: return Arrays.asList(fields);
0412: }
0413:
0414: public FieldBridge getFieldByName(String name) {
0415: FieldBridge field = null;
0416: for (int i = 0; i < primaryKeyFields.length; ++i) {
0417: JDBCCMPFieldBridge primaryKeyField = primaryKeyFields[i];
0418: if (primaryKeyField.getFieldName().equals(name)) {
0419: field = primaryKeyField;
0420: break;
0421: }
0422: }
0423: if (field == null) {
0424: field = getCMPFieldByName(name);
0425: }
0426: if (field == null) {
0427: field = getCMRFieldByName(name);
0428: }
0429: return field;
0430: }
0431:
0432: public boolean[] getEagerLoadMask() {
0433: return eagerLoadGroupMask;
0434: }
0435:
0436: public Iterator getLazyLoadGroupMasks() {
0437: return lazyLoadGroupMasks.iterator();
0438: }
0439:
0440: public boolean[] getLoadGroupMask(String name) {
0441: boolean[] mask = (boolean[]) loadGroupMasks.get(name);
0442: if (mask == null) {
0443: throw new IllegalStateException("Load group '" + name
0444: + "' is not defined. Defined load groups: "
0445: + loadGroupMasks.keySet());
0446: }
0447: return mask;
0448: }
0449:
0450: public FieldIterator getLoadIterator(
0451: JDBCCMPFieldBridge requiredField,
0452: JDBCReadAheadMetaData readahead, EntityEnterpriseContext ctx) {
0453: boolean[] loadGroup;
0454: if (requiredField == null) {
0455: if (readahead != null && !readahead.isNone()) {
0456: if (log.isTraceEnabled()) {
0457: log.trace("Eager-load for entity: readahead="
0458: + readahead);
0459: }
0460: loadGroup = getLoadGroupMask(readahead
0461: .getEagerLoadGroup());
0462: } else {
0463: if (log.isTraceEnabled()) {
0464: log
0465: .trace("Default eager-load for entity: readahead="
0466: + readahead);
0467: }
0468: loadGroup = eagerLoadGroupMask;
0469: }
0470: } else {
0471: loadGroup = new boolean[tableFields.length];
0472: int requiredInd = requiredField.getTableIndex();
0473: loadGroup[requiredInd] = true;
0474: for (Iterator groups = lazyLoadGroupMasks.iterator(); groups
0475: .hasNext();) {
0476: boolean[] lazyGroup = (boolean[]) groups.next();
0477: if (lazyGroup[requiredInd]) {
0478: for (int i = 0; i < loadGroup.length; ++i)
0479: loadGroup[i] = loadGroup[i] || lazyGroup[i];
0480: }
0481: }
0482: }
0483:
0484: FieldIterator loadIter;
0485: if (loadGroup != null) {
0486: // filter
0487: int fieldsToLoad = 0;
0488: EntityState entityState = getEntityState(ctx);
0489: for (int i = 0; i < tableFields.length; ++i) {
0490: JDBCCMPFieldBridge field = tableFields[i];
0491: if (loadGroup[i] && !field.isPrimaryKeyMember()
0492: && !field.isLoaded(ctx)) {
0493: entityState.setLoadRequired(i);
0494: ++fieldsToLoad;
0495: }
0496: }
0497: loadIter = (fieldsToLoad > 0 ? entityState
0498: .getLoadIterator(ctx) : EMPTY_FIELD_ITERATOR);
0499: } else {
0500: loadIter = EMPTY_FIELD_ITERATOR;
0501: }
0502: return loadIter;
0503: }
0504:
0505: /**
0506: * @param name CMP field name
0507: * @return JDBCCMPFieldBridge instance or null if no field found.
0508: */
0509: public JDBCCMPFieldBridge getCMPFieldByName(String name) {
0510: for (int i = 0; i < primaryKeyFields.length; ++i) {
0511: JDBCCMPFieldBridge cmpField = primaryKeyFields[i];
0512: if (cmpField.getFieldName().equals(name))
0513: return cmpField;
0514: }
0515: for (int i = 0; i < cmpFields.length; ++i) {
0516: JDBCCMPFieldBridge cmpField = cmpFields[i];
0517: if (cmpField.getFieldName().equals(name))
0518: return cmpField;
0519: }
0520: return null;
0521: }
0522:
0523: public JDBCAbstractCMRFieldBridge[] getCMRFields() {
0524: return cmrFields;
0525: }
0526:
0527: public JDBCCMRFieldBridge getCMRFieldByName(String name) {
0528: for (int i = 0; i < cmrFields.length; ++i) {
0529: JDBCCMRFieldBridge cmrField = cmrFields[i];
0530: if (cmrField.getFieldName().equals(name))
0531: return cmrField;
0532: }
0533: return null;
0534: }
0535:
0536: public JDBCCMPFieldBridge getVersionField() {
0537: return versionField;
0538: }
0539:
0540: public JDBCCMPFieldBridge getCreatedPrincipalField() {
0541: return createdPrincipalField;
0542: }
0543:
0544: public JDBCCMPFieldBridge getCreatedTimeField() {
0545: return createdTimeField;
0546: }
0547:
0548: public JDBCCMPFieldBridge getUpdatedPrincipalField() {
0549: return updatedPrincipalField;
0550: }
0551:
0552: public JDBCCMPFieldBridge getUpdatedTimeField() {
0553: return updatedTimeField;
0554: }
0555:
0556: public Collection getSelectors() {
0557: return selectorsByMethod.values();
0558: }
0559:
0560: public void initInstance(EntityEnterpriseContext ctx) {
0561: for (int i = 0; i < tableFields.length; ++i)
0562: tableFields[i].initInstance(ctx);
0563: //for(int i = 0; i < primaryKeyFields.length; ++i)
0564: // primaryKeyFields[i].initInstance(ctx);
0565: //for(int i = 0; i < cmpFields.length; ++i)
0566: // cmpFields[i].initInstance(ctx);
0567: for (int i = 0; i < cmrFields.length; ++i) {
0568: JDBCCMRFieldBridge cmrField = cmrFields[i];
0569: cmrField.initInstance(ctx);
0570: }
0571: }
0572:
0573: public static boolean isEjbCreateDone(EntityEnterpriseContext ctx) {
0574: return getEntityState(ctx).ejbCreateDone;
0575: }
0576:
0577: public static void setCreated(EntityEnterpriseContext ctx) {
0578: getEntityState(ctx).setCreated();
0579: }
0580:
0581: public static void setEjbCreateDone(EntityEnterpriseContext ctx) {
0582: getEntityState(ctx).ejbCreateDone = true;
0583: }
0584:
0585: /**
0586: * This method is used to determined whether the instance was modified.
0587: * NOTE, even if the method returns true the isStoreRequired for this same instance
0588: * might return false, e.g. a CMR field that doesn't have a foreign key was modified.
0589: * @param ctx
0590: * @return
0591: */
0592: public boolean isModified(EntityEnterpriseContext ctx) {
0593: boolean invalidateCache = false;
0594: final EntityState entityState = getEntityState(ctx);
0595: if (entityState.isCreated()) {
0596: invalidateCache = areCmpFieldsDirty(ctx, entityState);
0597: if (!invalidateCache) {
0598: for (int i = 0; i < cmrFields.length; ++i) {
0599: if (cmrFields[i].invalidateCache(ctx)) {
0600: invalidateCache = true;
0601: break;
0602: }
0603: }
0604: }
0605: }
0606: return invalidateCache;
0607: }
0608:
0609: public boolean isStoreRequired(EntityEnterpriseContext ctx) {
0610: boolean modified = false;
0611: final EntityState entityState = getEntityState(ctx);
0612: if (entityState.isCreated()) {
0613: modified = areCmpFieldsDirty(ctx, entityState);
0614: if (!modified) {
0615: for (int i = 0; i < cmrFields.length; ++i) {
0616: if (cmrFields[i].isDirty(ctx)) {
0617: modified = true;
0618: break;
0619: }
0620: }
0621: }
0622: }
0623: return modified;
0624: }
0625:
0626: private boolean areCmpFieldsDirty(
0627: final EntityEnterpriseContext ctx,
0628: final EntityState entityState) {
0629: for (int i = 0; i < tableFields.length; ++i) {
0630: final JDBCCMPFieldBridge field = tableFields[i];
0631: if (entityState.isCheckDirty(i) && field.isDirty(ctx)) {
0632: return true;
0633: }
0634: }
0635: return false;
0636: }
0637:
0638: public FieldIterator getDirtyIterator(EntityEnterpriseContext ctx) {
0639: int dirtyFields = 0;
0640: final EntityState entityState = getEntityState(ctx);
0641: for (int i = 0; i < tableFields.length; ++i) {
0642: JDBCCMPFieldBridge field = tableFields[i];
0643: if (entityState.isCheckDirty(i) && field.isDirty(ctx)) {
0644: entityState.setUpdateRequired(i);
0645: ++dirtyFields;
0646: }
0647: }
0648:
0649: return dirtyFields > 0 ? getEntityState(ctx).getDirtyIterator(
0650: ctx) : EMPTY_FIELD_ITERATOR;
0651: }
0652:
0653: public boolean hasLockedFields(EntityEnterpriseContext ctx) {
0654: return getEntityState(ctx).hasLockedFields();
0655: }
0656:
0657: public FieldIterator getLockedIterator(EntityEnterpriseContext ctx) {
0658: return getEntityState(ctx).getLockedIterator(ctx);
0659: }
0660:
0661: public void initPersistenceContext(EntityEnterpriseContext ctx) {
0662: // If we have an EJB 2.0 dynaymic proxy,
0663: // notify the handler of the assigned context.
0664: Object instance = ctx.getInstance();
0665: if (instance instanceof Proxies.ProxyTarget) {
0666: InvocationHandler handler = ((Proxies.ProxyTarget) instance)
0667: .getInvocationHandler();
0668: if (handler instanceof EntityBridgeInvocationHandler)
0669: ((EntityBridgeInvocationHandler) handler)
0670: .setContext(ctx);
0671: }
0672: ctx.setPersistenceContext(new JDBCContext(jdbcContextSize,
0673: new EntityState()));
0674: }
0675:
0676: /**
0677: * This is only called in commit option B
0678: */
0679: public void resetPersistenceContext(EntityEnterpriseContext ctx) {
0680: for (int i = 0; i < primaryKeyFields.length; ++i)
0681: primaryKeyFields[i].resetPersistenceContext(ctx);
0682: for (int i = 0; i < cmpFields.length; ++i)
0683: cmpFields[i].resetPersistenceContext(ctx);
0684: for (int i = 0; i < cmrFields.length; ++i)
0685: cmrFields[i].resetPersistenceContext(ctx);
0686: }
0687:
0688: public static void destroyPersistenceContext(
0689: EntityEnterpriseContext ctx) {
0690: // If we have an EJB 2.0 dynaymic proxy,
0691: // notify the handler of the assigned context.
0692: Object instance = ctx.getInstance();
0693: if (instance instanceof Proxies.ProxyTarget) {
0694: InvocationHandler handler = ((Proxies.ProxyTarget) instance)
0695: .getInvocationHandler();
0696: if (handler instanceof EntityBridgeInvocationHandler)
0697: ((EntityBridgeInvocationHandler) handler)
0698: .setContext(null);
0699: }
0700: ctx.setPersistenceContext(null);
0701: }
0702:
0703: //
0704: // Commands to handle primary keys
0705: //
0706:
0707: public int setPrimaryKeyParameters(PreparedStatement ps,
0708: int parameterIndex, Object primaryKey) {
0709: for (int i = 0; i < primaryKeyFields.length; ++i)
0710: parameterIndex = primaryKeyFields[i]
0711: .setPrimaryKeyParameters(ps, parameterIndex,
0712: primaryKey);
0713: return parameterIndex;
0714: }
0715:
0716: public int loadPrimaryKeyResults(ResultSet rs, int parameterIndex,
0717: Object[] pkRef) {
0718: pkRef[0] = createPrimaryKeyInstance();
0719: for (int i = 0; i < primaryKeyFields.length; ++i)
0720: parameterIndex = primaryKeyFields[i].loadPrimaryKeyResults(
0721: rs, parameterIndex, pkRef);
0722: return parameterIndex;
0723: }
0724:
0725: public Object extractPrimaryKeyFromInstance(
0726: EntityEnterpriseContext ctx) {
0727: try {
0728: Object pk = null;
0729: for (int i = 0; i < primaryKeyFields.length; ++i) {
0730: JDBCCMPFieldBridge pkField = primaryKeyFields[i];
0731: Object fieldValue = pkField.getInstanceValue(ctx);
0732:
0733: // updated pk object with return form set primary key value to
0734: // handle single valued non-composit pks and more complicated behivors.
0735: pk = pkField.setPrimaryKeyValue(pk, fieldValue);
0736: }
0737: return pk;
0738: } catch (EJBException e) {
0739: // to avoid double wrap of EJBExceptions
0740: throw e;
0741: } catch (Exception e) {
0742: // Non recoverable internal exception
0743: throw new EJBException(
0744: "Internal error extracting primary key from "
0745: + "instance", e);
0746: }
0747: }
0748:
0749: public void injectPrimaryKeyIntoInstance(
0750: EntityEnterpriseContext ctx, Object pk) {
0751: for (int i = 0; i < primaryKeyFields.length; ++i) {
0752: JDBCCMPFieldBridge pkField = primaryKeyFields[i];
0753: Object fieldValue = pkField.getPrimaryKeyValue(pk);
0754: pkField.setInstanceValue(ctx, fieldValue);
0755: }
0756: }
0757:
0758: int getNextJDBCContextIndex() {
0759: return jdbcContextSize++;
0760: }
0761:
0762: int addTableField(JDBCCMPFieldBridge field) {
0763: JDBCCMPFieldBridge[] tmpFields = tableFields;
0764: if (tableFields == null) {
0765: tableFields = new JDBCCMPFieldBridge[1];
0766: } else {
0767: tableFields = new JDBCCMPFieldBridge[tableFields.length + 1];
0768: System.arraycopy(tmpFields, 0, tableFields, 0,
0769: tmpFields.length);
0770: }
0771: int index = tableFields.length - 1;
0772: tableFields[index] = field;
0773:
0774: return index;
0775: }
0776:
0777: public JDBCFieldBridge[] getTableFields() {
0778: return tableFields;
0779: }
0780:
0781: /**
0782: * Marks the context as removed.
0783: * @param ctx instance's context
0784: */
0785: public void setRemoved(EntityEnterpriseContext ctx) {
0786: getEntityState(ctx).setRemoved();
0787: }
0788:
0789: /**
0790: * @param ctx instance's context.
0791: * @return true if instance was removed.
0792: */
0793: public boolean isRemoved(EntityEnterpriseContext ctx) {
0794: return getEntityState(ctx).isRemoved();
0795: }
0796:
0797: /**
0798: * Marks an instance as being removed
0799: */
0800: public void setIsBeingRemoved(EntityEnterpriseContext ctx) {
0801: getEntityState(ctx).setIsBeingRemoved();
0802: }
0803:
0804: /**
0805: * @param ctx instance's context.
0806: * @return true if instance is being removed.
0807: */
0808: public boolean isBeingRemoved(EntityEnterpriseContext ctx) {
0809: return getEntityState(ctx).isBeingRemoved();
0810: }
0811:
0812: /**
0813: * Marks the instance as scheduled for cascade delete (not for batch cascade delete)
0814: * @param ctx instance's context.
0815: */
0816: public void scheduleForCascadeDelete(EntityEnterpriseContext ctx) {
0817: getEntityState(ctx).scheduleForCascadeDelete();
0818: if (log.isTraceEnabled())
0819: log.trace("Scheduled for cascade-delete: " + ctx.getId());
0820: }
0821:
0822: /**
0823: * @param ctx instance's context.
0824: * @return true if instance was scheduled for cascade delete (not for batch cascade delete)
0825: */
0826: public boolean isScheduledForCascadeDelete(
0827: EntityEnterpriseContext ctx) {
0828: return getEntityState(ctx).isScheduledForCascadeDelete();
0829: }
0830:
0831: /**
0832: * Marks the instance as scheduled for batch cascade delete (not for cascade delete)
0833: * @param ctx instance's context.
0834: */
0835: public void scheduleForBatchCascadeDelete(
0836: EntityEnterpriseContext ctx) {
0837: getEntityState(ctx).scheduleForBatchCascadeDelete();
0838: if (log.isTraceEnabled())
0839: log.trace("Scheduled for batch-cascade-delete: "
0840: + ctx.getId());
0841: }
0842:
0843: /**
0844: * @param ctx instance's context.
0845: * @return true if instance was scheduled for batch cascade delete (not for cascade delete)
0846: */
0847: public boolean isScheduledForBatchCascadeDelete(
0848: EntityEnterpriseContext ctx) {
0849: return getEntityState(ctx).isScheduledForBatchCascadeDelete();
0850: }
0851:
0852: private static EntityState getEntityState(
0853: EntityEnterpriseContext ctx) {
0854: JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
0855: EntityState entityState = jdbcCtx.getEntityState();
0856: if (entityState == null)
0857: throw new IllegalStateException("Entity state is null.");
0858: return entityState;
0859: }
0860:
0861: private void loadCMPFields(JDBCEntityMetaData metadata)
0862: throws DeploymentException {
0863: // only non pk fields are stored here at first and then later
0864: // the pk fields are added to the front (makes sql easier to read)
0865: List cmpFieldsMD = metadata.getCMPFields();
0866: List cmpFieldsList = new ArrayList(cmpFieldsMD.size());
0867: // primary key cmp fields
0868: List pkFieldsList = new ArrayList(cmpFieldsMD.size());
0869:
0870: // create pk fields
0871: for (int i = 0; i < cmpFieldsMD.size(); ++i) {
0872: JDBCCMPFieldMetaData fieldMD = (JDBCCMPFieldMetaData) cmpFieldsMD
0873: .get(i);
0874: if (fieldMD.isPrimaryKeyMember()) {
0875: JDBCCMPFieldBridge cmpField = createCMPField(metadata,
0876: fieldMD);
0877: pkFieldsList.add(cmpField);
0878: }
0879: }
0880:
0881: // create non-pk cmp fields
0882: for (int i = 0; i < cmpFieldsMD.size(); ++i) {
0883: JDBCCMPFieldMetaData fieldMD = (JDBCCMPFieldMetaData) cmpFieldsMD
0884: .get(i);
0885: if (!fieldMD.isPrimaryKeyMember()) {
0886: JDBCCMPFieldBridge cmpField = createCMPField(metadata,
0887: fieldMD);
0888: cmpFieldsList.add(cmpField);
0889: }
0890: }
0891:
0892: // save the pk fields in the pk field array
0893: primaryKeyFields = new JDBCCMPFieldBridge[pkFieldsList.size()];
0894: for (int i = 0; i < pkFieldsList.size(); ++i)
0895: primaryKeyFields[i] = (JDBCCMPFieldBridge) pkFieldsList
0896: .get(i);
0897:
0898: // add the pk fields to the front of the cmp list, per guarantee above
0899: cmpFields = new JDBCCMPFieldBridge[cmpFieldsMD.size()
0900: - primaryKeyFields.length];
0901: int cmpFieldIndex = 0;
0902: for (int i = 0; i < cmpFieldsList.size(); ++i)
0903: cmpFields[cmpFieldIndex++] = (JDBCCMPFieldBridge) cmpFieldsList
0904: .get(i);
0905: }
0906:
0907: private void loadCMRFields(JDBCEntityMetaData metadata)
0908: throws DeploymentException {
0909: cmrFields = new JDBCCMRFieldBridge[metadata
0910: .getRelationshipRoles().size()];
0911: // create each field
0912: int cmrFieldIndex = 0;
0913: for (Iterator iter = metadata.getRelationshipRoles().iterator(); iter
0914: .hasNext();) {
0915: JDBCRelationshipRoleMetaData relationshipRole = (JDBCRelationshipRoleMetaData) iter
0916: .next();
0917: JDBCCMRFieldBridge cmrField = new JDBCCMRFieldBridge(this ,
0918: manager, relationshipRole);
0919: cmrFields[cmrFieldIndex++] = cmrField;
0920: }
0921: }
0922:
0923: private void loadLoadGroups(JDBCEntityMetaData metadata)
0924: throws DeploymentException {
0925: loadGroupMasks = new HashMap();
0926:
0927: // load optimistic locking mask and add it to all the load group masks
0928: JDBCOptimisticLockingMetaData olMD = metadata
0929: .getOptimisticLocking();
0930: if (olMD != null) {
0931: if (versionField != null) {
0932: defaultLockGroupMask = new boolean[tableFields.length];
0933: defaultLockGroupMask[versionField.getTableIndex()] = true;
0934: versionField
0935: .setLockingStrategy(LockingStrategy.VERSION);
0936: } else if (olMD.getGroupName() != null) {
0937: defaultLockGroupMask = loadGroupMask(olMD
0938: .getGroupName(), null);
0939: for (int i = 0; i < tableFields.length; ++i) {
0940: if (defaultLockGroupMask[i]) {
0941: JDBCCMPFieldBridge tableField = tableFields[i];
0942: tableField
0943: .setLockingStrategy(LockingStrategy.GROUP);
0944: tableField
0945: .addDefaultFlag(ADD_TO_WHERE_ON_UPDATE);
0946: }
0947: }
0948: } else // read or modified strategy
0949: {
0950: LockingStrategy strategy = (olMD.getLockingStrategy() == JDBCOptimisticLockingMetaData.READ_STRATEGY ? LockingStrategy.READ
0951: : LockingStrategy.MODIFIED);
0952: for (int i = 0; i < tableFields.length; ++i) {
0953: JDBCCMPFieldBridge field = tableFields[i];
0954: if (!field.isPrimaryKeyMember())
0955: field.setLockingStrategy(strategy);
0956: }
0957: }
0958: }
0959:
0960: // add the * load group
0961: boolean[] defaultLoadGroup = new boolean[tableFields.length];
0962: Arrays.fill(defaultLoadGroup, true);
0963: for (int i = 0; i < primaryKeyFields.length; ++i) {
0964: int tableIndex = primaryKeyFields[i].getTableIndex();
0965: defaultLoadGroup[tableIndex] = false;
0966: }
0967: loadGroupMasks.put(DEFAULT_LOADGROUP_NAME, defaultLoadGroup);
0968:
0969: // put each group in the load groups map by group name
0970: Iterator groupNames = metadata.getLoadGroups().keySet()
0971: .iterator();
0972: while (groupNames.hasNext()) {
0973: // get the group name
0974: String groupName = (String) groupNames.next();
0975: boolean[] loadGroup = loadGroupMask(groupName,
0976: defaultLockGroupMask);
0977: loadGroupMasks.put(groupName, loadGroup);
0978: }
0979: loadGroupMasks = Collections.unmodifiableMap(loadGroupMasks);
0980: }
0981:
0982: private boolean[] loadGroupMask(String groupName,
0983: boolean[] defaultGroup) throws DeploymentException {
0984: List fieldNames = metadata.getLoadGroup(groupName);
0985: boolean[] group = new boolean[tableFields.length];
0986: if (defaultGroup != null)
0987: System.arraycopy(defaultGroup, 0, group, 0, group.length);
0988: for (Iterator iter = fieldNames.iterator(); iter.hasNext();) {
0989: String fieldName = (String) iter.next();
0990: JDBCFieldBridge field = (JDBCFieldBridge) getFieldByName(fieldName);
0991: if (field == null)
0992: throw new DeploymentException("Field " + fieldName
0993: + " not found for entity " + getEntityName());
0994:
0995: if (field instanceof JDBCCMRFieldBridge) {
0996: JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) field;
0997: if (cmrField.hasForeignKey()) {
0998: JDBCCMPFieldBridge[] fkFields = (JDBCCMPFieldBridge[]) cmrField
0999: .getForeignKeyFields();
1000: for (int i = 0; i < fkFields.length; ++i) {
1001: group[fkFields[i].getTableIndex()] = true;
1002: }
1003: } else {
1004: throw new DeploymentException(
1005: "Only CMR fields that have "
1006: + "a foreign-key may be a member of a load group: "
1007: + "fieldName=" + fieldName);
1008: }
1009: } else {
1010: group[((JDBCCMPFieldBridge) field).getTableIndex()] = true;
1011: }
1012: }
1013: return group;
1014: }
1015:
1016: private void loadEagerLoadGroup(JDBCEntityMetaData metadata) {
1017: String eagerLoadGroupName = metadata.getEagerLoadGroup();
1018: if (eagerLoadGroupName == null) {
1019: // can be null in case of <eager-load-group/>, meaning empty load group
1020: eagerLoadGroupMask = defaultLockGroupMask;
1021: } else
1022: eagerLoadGroupMask = (boolean[]) loadGroupMasks
1023: .get(eagerLoadGroupName);
1024: }
1025:
1026: private void loadLazyLoadGroups(JDBCEntityMetaData metadata) {
1027: List lazyGroupNames = metadata.getLazyLoadGroups();
1028: lazyLoadGroupMasks = new ArrayList(lazyGroupNames.size());
1029: for (Iterator lazyLoadGroupNames = lazyGroupNames.iterator(); lazyLoadGroupNames
1030: .hasNext();) {
1031: String lazyLoadGroupName = (String) lazyLoadGroupNames
1032: .next();
1033: lazyLoadGroupMasks.add(loadGroupMasks
1034: .get(lazyLoadGroupName));
1035: }
1036: lazyLoadGroupMasks = Collections
1037: .unmodifiableList(lazyLoadGroupMasks);
1038: }
1039:
1040: private JDBCCMPFieldBridge createCMPField(
1041: JDBCEntityMetaData metadata,
1042: JDBCCMPFieldMetaData cmpFieldMetaData)
1043: throws DeploymentException {
1044: JDBCCMPFieldBridge cmpField;
1045: if (metadata.isCMP1x())
1046: cmpField = new JDBCCMP1xFieldBridge(manager,
1047: cmpFieldMetaData);
1048: else
1049: cmpField = new JDBCCMP2xFieldBridge(manager,
1050: cmpFieldMetaData);
1051: return cmpField;
1052: }
1053:
1054: private void loadSelectors(JDBCEntityMetaData metadata) {
1055: // Don't know if this is the best way to do this. Another way would be
1056: // to deligate seletors to the JDBCFindEntitiesCommand, but this is
1057: // easier now.
1058: selectorsByMethod = new HashMap(metadata.getQueries().size());
1059: Iterator definedFinders = manager.getMetaData().getQueries()
1060: .iterator();
1061: while (definedFinders.hasNext()) {
1062: JDBCQueryMetaData q = (JDBCQueryMetaData) definedFinders
1063: .next();
1064: if (q.getMethod().getName().startsWith("ejbSelect"))
1065: selectorsByMethod.put(q.getMethod(),
1066: new JDBCSelectorBridge(manager, q));
1067: }
1068: selectorsByMethod = Collections
1069: .unmodifiableMap(selectorsByMethod);
1070: }
1071:
1072: private void addCMPField(JDBCCMPFieldBridge field) {
1073: JDBCCMPFieldBridge[] tmpCMPFields = cmpFields;
1074: cmpFields = new JDBCCMPFieldBridge[cmpFields.length + 1];
1075: System.arraycopy(tmpCMPFields, 0, cmpFields, 0,
1076: tmpCMPFields.length);
1077: cmpFields[tmpCMPFields.length] = field;
1078: }
1079:
1080: public class EntityState {
1081: private static final byte REMOVED = 1;
1082: private static final byte SCHEDULED_FOR_CASCADE_DELETE = 2;
1083: private static final byte SCHEDULED_FOR_BATCH_CASCADE_DELETE = 4;
1084: private static final byte IS_BEING_REMOVED = 8;
1085:
1086: /** indicates whether ejbCreate method was executed */
1087: private boolean ejbCreateDone = false;
1088: /** indicates whether ejbPostCreate method was executed */
1089: private boolean ejbPostCreateDone = false;
1090:
1091: private byte entityFlags;
1092:
1093: /** array of field flags*/
1094: private final byte[] fieldFlags = new byte[tableFields.length];
1095:
1096: public EntityState() {
1097: for (int i = 0; i < tableFields.length; ++i) {
1098: fieldFlags[i] = tableFields[i].getDefaultFlags();
1099: }
1100: }
1101:
1102: public void setRemoved() {
1103: entityFlags |= REMOVED;
1104: entityFlags &= ~(SCHEDULED_FOR_CASCADE_DELETE
1105: | SCHEDULED_FOR_BATCH_CASCADE_DELETE | IS_BEING_REMOVED);
1106: }
1107:
1108: public boolean isRemoved() {
1109: return (entityFlags & REMOVED) > 0;
1110: }
1111:
1112: public void setIsBeingRemoved() {
1113: entityFlags |= IS_BEING_REMOVED;
1114: }
1115:
1116: public boolean isBeingRemoved() {
1117: return (entityFlags & IS_BEING_REMOVED) > 0;
1118: }
1119:
1120: public void scheduleForCascadeDelete() {
1121: entityFlags |= SCHEDULED_FOR_CASCADE_DELETE;
1122: }
1123:
1124: public boolean isScheduledForCascadeDelete() {
1125: return (entityFlags & SCHEDULED_FOR_CASCADE_DELETE) > 0;
1126: }
1127:
1128: public void scheduleForBatchCascadeDelete() {
1129: entityFlags |= SCHEDULED_FOR_BATCH_CASCADE_DELETE
1130: | SCHEDULED_FOR_CASCADE_DELETE;
1131: }
1132:
1133: public boolean isScheduledForBatchCascadeDelete() {
1134: return (entityFlags & SCHEDULED_FOR_BATCH_CASCADE_DELETE) > 0;
1135: }
1136:
1137: public void setCreated() {
1138: ejbCreateDone = true;
1139: ejbPostCreateDone = true;
1140: }
1141:
1142: public boolean isCreated() {
1143: return ejbCreateDone && ejbPostCreateDone;
1144: }
1145:
1146: /**
1147: * @param fieldIndex index of the field
1148: * @return true if the field is loaded
1149: */
1150: public boolean isLoaded(int fieldIndex) {
1151: return (fieldFlags[fieldIndex] & LOADED) > 0;
1152: }
1153:
1154: /**
1155: * Marks the field as loaded.
1156: * @param fieldIndex index of the field.
1157: */
1158: public void setLoaded(int fieldIndex) {
1159: fieldFlags[fieldIndex] |= LOADED;
1160: }
1161:
1162: /**
1163: * Marks the field to be loaded.
1164: * @param fieldIndex index of the field.
1165: */
1166: public void setLoadRequired(int fieldIndex) {
1167: fieldFlags[fieldIndex] |= LOAD_REQUIRED;
1168: }
1169:
1170: /**
1171: * Marks the field to be updated.
1172: * @param fieldIndex index of the field.
1173: */
1174: public void setUpdateRequired(int fieldIndex) {
1175: fieldFlags[fieldIndex] |= DIRTY;
1176: }
1177:
1178: /**
1179: * The field will be checked for dirty state at commit.
1180: * @param fieldIndex index of the field.
1181: */
1182: public void setCheckDirty(int fieldIndex) {
1183: fieldFlags[fieldIndex] |= CHECK_DIRTY;
1184: }
1185:
1186: /**
1187: * @param fieldIndex the index of the field that should be checked for dirty state.
1188: * @return true if the field should be checked for dirty state.
1189: */
1190: public boolean isCheckDirty(int fieldIndex) {
1191: return (fieldFlags[fieldIndex] & CHECK_DIRTY) > 0;
1192: }
1193:
1194: /**
1195: * Marks the field as clean.
1196: * @param fieldIndex nextIndex of the field.
1197: */
1198: public void setClean(int fieldIndex) {
1199: fieldFlags[fieldIndex] &= ~(CHECK_DIRTY | DIRTY | LOCKED);
1200: }
1201:
1202: /**
1203: * Resets field flags.
1204: * @param fieldIndex nextIndex of the field.
1205: */
1206: public void resetFlags(int fieldIndex) {
1207: fieldFlags[fieldIndex] = tableFields[fieldIndex]
1208: .getDefaultFlags();
1209: }
1210:
1211: public FieldIterator getDirtyIterator(
1212: EntityEnterpriseContext ctx) {
1213: return new MaskFieldIterator(
1214: (byte) (DIRTY | ADD_TO_SET_ON_UPDATE));
1215: }
1216:
1217: public boolean hasLockedFields() {
1218: boolean result = false;
1219: for (int i = 0; i < fieldFlags.length; ++i) {
1220: if ((fieldFlags[i] & (LOCKED | ADD_TO_WHERE_ON_UPDATE)) > 0) {
1221: result = true;
1222: break;
1223: }
1224: }
1225: return result;
1226: }
1227:
1228: public FieldIterator getLockedIterator(
1229: EntityEnterpriseContext ctx) {
1230: return new MaskFieldIterator(
1231: (byte) (LOCKED | ADD_TO_WHERE_ON_UPDATE));
1232: }
1233:
1234: public boolean lockValue(int fieldIndex) {
1235: boolean lock = false;
1236: byte fieldFlag = fieldFlags[fieldIndex];
1237: if ((fieldFlag & LOADED) > 0 && (fieldFlag & LOCKED) == 0) {
1238: fieldFlags[fieldIndex] |= LOCKED;
1239: lock = true;
1240: }
1241: return lock;
1242: }
1243:
1244: public FieldIterator getLoadIterator(EntityEnterpriseContext ctx) {
1245: return new MaskFieldIterator(LOAD_REQUIRED);
1246: }
1247:
1248: // Inner
1249:
1250: private class MaskFieldIterator implements FieldIterator {
1251: private final byte flagMask;
1252: private int nextIndex = 0;
1253: private int curIndex = -1;
1254:
1255: public MaskFieldIterator(byte flagMask) {
1256: this .flagMask = flagMask;
1257: }
1258:
1259: public boolean hasNext() {
1260: while (nextIndex < fieldFlags.length) {
1261: if ((fieldFlags[nextIndex] & flagMask) > 0) {
1262: return true;
1263: }
1264:
1265: ++nextIndex;
1266: }
1267:
1268: return false;
1269: }
1270:
1271: public JDBCCMPFieldBridge next() {
1272: if (!hasNext())
1273: throw new NoSuchElementException();
1274: curIndex = nextIndex;
1275: return tableFields[nextIndex++];
1276: }
1277:
1278: public void remove() {
1279: fieldFlags[curIndex] &= ~flagMask;
1280: }
1281:
1282: public void removeAll() {
1283: int inversedMask = ~flagMask;
1284: for (int i = 0; i < fieldFlags.length; ++i)
1285: fieldFlags[i] &= inversedMask;
1286: }
1287:
1288: public void reset() {
1289: nextIndex = 0;
1290: curIndex = -1;
1291: }
1292: }
1293: }
1294:
1295: public static final FieldIterator EMPTY_FIELD_ITERATOR = new FieldIterator() {
1296: public boolean hasNext() {
1297: return false;
1298: }
1299:
1300: public JDBCCMPFieldBridge next() {
1301: throw new NoSuchElementException();
1302: }
1303:
1304: public void remove() {
1305: throw new UnsupportedOperationException();
1306: }
1307:
1308: public void removeAll() {
1309: throw new UnsupportedOperationException();
1310: }
1311:
1312: public void reset() {
1313: }
1314: };
1315:
1316: public static interface FieldIterator {
1317: /**
1318: * @return true if there are more fields to iterate through.
1319: */
1320: boolean hasNext();
1321:
1322: /**
1323: * @return the next field.
1324: */
1325: JDBCCMPFieldBridge next();
1326:
1327: /**
1328: * Removes the current field from the iterator (not from the underlying array or another source)
1329: */
1330: void remove();
1331:
1332: /**
1333: * Removes all the fields from the iterator (not from the underlying array or another source).
1334: */
1335: void removeAll();
1336:
1337: /**
1338: * Resets the current position to the first field.
1339: */
1340: void reset();
1341: }
1342: }
|