Source Code Cross Referenced for JDBCCMRFieldBridge.java in  » EJB-Server-JBoss-4.2.1 » server » org » jboss » ejb » plugins » cmp » jdbc » bridge » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » EJB Server JBoss 4.2.1 » server » org.jboss.ejb.plugins.cmp.jdbc.bridge 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.lang.ref.WeakReference;
0025:        import java.lang.reflect.Method;
0026:        import javax.sql.DataSource;
0027:        import java.sql.PreparedStatement;
0028:        import java.sql.ResultSet;
0029:        import java.util.ArrayList;
0030:        import java.util.Collection;
0031:        import java.util.Collections;
0032:        import java.util.HashSet;
0033:        import java.util.Iterator;
0034:        import java.util.List;
0035:        import java.util.Map;
0036:        import java.util.Set;
0037:        import java.util.HashMap;
0038:        import java.util.Arrays;
0039:        import java.security.AccessController;
0040:        import java.security.PrivilegedAction;
0041:        import java.security.Principal;
0042:        import java.rmi.RemoteException;
0043:        import javax.ejb.EJBException;
0044:        import javax.ejb.EJBLocalObject;
0045:        import javax.ejb.EJBLocalHome;
0046:        import javax.ejb.RemoveException;
0047:        import javax.ejb.NoSuchObjectLocalException;
0048:        import javax.transaction.Status;
0049:        import javax.transaction.Synchronization;
0050:        import javax.transaction.SystemException;
0051:        import javax.transaction.Transaction;
0052:        import javax.transaction.TransactionManager;
0053:        import javax.transaction.RollbackException;
0054:
0055:        import org.jboss.deployment.DeploymentException;
0056:        import org.jboss.ejb.EntityCache;
0057:        import org.jboss.ejb.EntityContainer;
0058:        import org.jboss.ejb.EntityEnterpriseContext;
0059:        import org.jboss.ejb.LocalProxyFactory;
0060:        import org.jboss.ejb.plugins.cmp.bridge.EntityBridge;
0061:        import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
0062:        import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext;
0063:        import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager;
0064:        import org.jboss.ejb.plugins.cmp.jdbc.JDBCType;
0065:        import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
0066:        import org.jboss.ejb.plugins.cmp.jdbc.CascadeDeleteStrategy;
0067:        import org.jboss.ejb.plugins.cmp.jdbc.RelationData;
0068:        import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
0069:        import org.jboss.ejb.plugins.cmp.jdbc.JDBCParameterSetter;
0070:        import org.jboss.ejb.plugins.cmp.jdbc.JDBCResultSetReader;
0071:        import org.jboss.tm.TransactionLocal;
0072:        import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
0073:        import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
0074:        import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData;
0075:        import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
0076:        import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
0077:        import org.jboss.ejb.plugins.lock.Entrancy;
0078:        import org.jboss.invocation.InvocationType;
0079:        import org.jboss.logging.Logger;
0080:        import org.jboss.security.SecurityAssociation;
0081:
0082:        /**
0083:         * JDBCCMRFieldBridge a bean relationship. This class only supports
0084:         * relationships between entities managed by a JDBCStoreManager in the same
0085:         * application.
0086:         * <p/>
0087:         * Life-cycle:
0088:         * Tied to the EntityBridge.
0089:         * <p/>
0090:         * Multiplicity:
0091:         * One for each role that entity has.
0092:         *
0093:         * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
0094:         * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
0095:         * @version $Revision: 61754 $
0096:         */
0097:        public final class JDBCCMRFieldBridge extends
0098:                JDBCAbstractCMRFieldBridge {
0099:            /**
0100:             * The entity bridge to which this cmr field belongs.
0101:             */
0102:            private final JDBCEntityBridge entity;
0103:            /**
0104:             * The manager of this entity.
0105:             */
0106:            private final JDBCStoreManager manager;
0107:            /**
0108:             * Metadata of the relationship role that this field represents.
0109:             */
0110:            private final JDBCRelationshipRoleMetaData metadata;
0111:            /**
0112:             * The data source used to acess the relation table if relevant.
0113:             */
0114:            private DataSource dataSource;
0115:            /**
0116:             * The relation table name if relevent.
0117:             */
0118:            private String qualifiedTableName;
0119:            private String tableName;
0120:            /**
0121:             * The key fields that this entity maintains in the relation table.
0122:             */
0123:            private JDBCCMP2xFieldBridge[] tableKeyFields;
0124:            /**
0125:             * JDBCType for the foreign key fields. Basically, this is an ordered
0126:             * merge of the JDBCType of the foreign key field.
0127:             */
0128:            private JDBCType jdbcType;
0129:            /**
0130:             * The related entity's container.
0131:             */
0132:            private WeakReference relatedContainerRef;
0133:            /**
0134:             * The related entity's jdbc store manager
0135:             */
0136:            private JDBCStoreManager relatedManager;
0137:            /**
0138:             * The related entity.
0139:             */
0140:            private JDBCEntityBridge relatedEntity;
0141:            /**
0142:             * The related entity's cmr field for this relationship.
0143:             */
0144:            private JDBCCMRFieldBridge relatedCMRField;
0145:            /**
0146:             * da log.
0147:             */
0148:            private final Logger log;
0149:
0150:            /**
0151:             * Foreign key fields of this entity (i.e., related entities pk fields)
0152:             */
0153:            private JDBCCMP2xFieldBridge[] foreignKeyFields;
0154:            /**
0155:             * Indicates whether all FK fields are mapped to PK fields
0156:             */
0157:            private boolean allFKFieldsMappedToPKFields;
0158:            /**
0159:             * This map contains related PK fields that are mapped through FK fields to this entity's PK fields
0160:             */
0161:            private final Map relatedPKFieldsByMyPKFields = new HashMap();
0162:            /**
0163:             * This map contains related PK fields keyed by FK fields
0164:             */
0165:            private final Map relatedPKFieldsByMyFKFields = new HashMap();
0166:            /**
0167:             * Indicates whether there are foreign key fields mapped to CMP fields
0168:             */
0169:            private boolean hasFKFieldsMappedToCMPFields;
0170:
0171:            // Map for lists of related PK values keyed by this side's PK values.
0172:            // The values are put/removed by related entities when its fields representing
0173:            // foreign key are changed. When entity with this CMR is created, this map is checked
0174:            // for waiting for it entities. Relationship with waiting entities is established,
0175:            // removing waiting entities' primary keys from the map.
0176:            // NOTE: this map is used only for foreign key fields mapped to CMP fields.
0177:            private final TransactionLocal relatedPKValuesWaitingForMyPK = new TransactionLocal() {
0178:                protected Object initialValue() {
0179:                    return new HashMap();
0180:                }
0181:            };
0182:
0183:            /**
0184:             * FindByPrimaryKey method used to find related instances in case when FK fields mapped to PK fields
0185:             */
0186:            private Method relatedFindByPrimaryKey;
0187:
0188:            /**
0189:             * index of the field in the JDBCContext
0190:             */
0191:            private final int jdbcContextIndex;
0192:
0193:            /**
0194:             * cascade-delete strategy
0195:             */
0196:            private CascadeDeleteStrategy cascadeDeleteStrategy;
0197:
0198:            /**
0199:             * This CMR field and its related CMR field share the same RelationDataManager
0200:             */
0201:            private RelationDataManager relationManager;
0202:
0203:            /**
0204:             * Creates a cmr field for the entity based on the metadata.
0205:             */
0206:            public JDBCCMRFieldBridge(JDBCEntityBridge entity,
0207:                    JDBCStoreManager manager,
0208:                    JDBCRelationshipRoleMetaData metadata)
0209:                    throws DeploymentException {
0210:                this .entity = entity;
0211:                this .manager = manager;
0212:                this .metadata = metadata;
0213:                this .jdbcContextIndex = ((JDBCEntityBridge) manager
0214:                        .getEntityBridge()).getNextJDBCContextIndex();
0215:
0216:                //  Creat the log
0217:                String categoryName = this .getClass().getName() + "."
0218:                        + manager.getMetaData().getName() + ".";
0219:                if (metadata.getCMRFieldName() != null) {
0220:                    categoryName += metadata.getCMRFieldName();
0221:                } else {
0222:                    categoryName += metadata.getRelatedRole().getEntity()
0223:                            .getName()
0224:                            + "-" + metadata.getRelatedRole().getCMRFieldName();
0225:                }
0226:                this .log = Logger.getLogger(categoryName);
0227:            }
0228:
0229:            public RelationDataManager getRelationDataManager() {
0230:                return relationManager;
0231:            }
0232:
0233:            public void resolveRelationship() throws DeploymentException {
0234:                //
0235:                // Set handles to the related entity's container, cache,
0236:                // manager, and invoker
0237:                //
0238:
0239:                // Related Entity Name
0240:                String relatedEntityName = metadata.getRelatedRole()
0241:                        .getEntity().getName();
0242:
0243:                // Related Entity
0244:                Catalog catalog = (Catalog) manager
0245:                        .getApplicationData("CATALOG");
0246:                relatedEntity = (JDBCEntityBridge) catalog
0247:                        .getEntityByEJBName(relatedEntityName);
0248:                if (relatedEntity == null) {
0249:                    throw new DeploymentException("Related entity not found: "
0250:                            + "entity=" + entity.getEntityName() + ", "
0251:                            + "cmrField=" + getFieldName() + ", "
0252:                            + "relatedEntity=" + relatedEntityName);
0253:                }
0254:
0255:                // Related CMR Field
0256:                JDBCCMRFieldBridge[] cmrFields = (JDBCCMRFieldBridge[]) relatedEntity
0257:                        .getCMRFields();
0258:                for (int i = 0; i < cmrFields.length; ++i) {
0259:                    JDBCCMRFieldBridge cmrField = cmrFields[i];
0260:                    if (metadata.getRelatedRole() == cmrField.getMetaData()) {
0261:                        relatedCMRField = cmrField;
0262:                        break;
0263:                    }
0264:                }
0265:
0266:                // if we didn't find the related CMR field throw an exception
0267:                // with a detailed message
0268:                if (relatedCMRField == null) {
0269:                    String message = "Related CMR field not found in "
0270:                            + relatedEntity.getEntityName()
0271:                            + " for relationship from";
0272:
0273:                    message += entity.getEntityName() + ".";
0274:                    if (getFieldName() != null) {
0275:                        message += getFieldName();
0276:                    } else {
0277:                        message += "<no-field>";
0278:                    }
0279:
0280:                    message += " to ";
0281:                    message += relatedEntityName + ".";
0282:                    if (metadata.getRelatedRole().getCMRFieldName() != null) {
0283:                        message += metadata.getRelatedRole().getCMRFieldName();
0284:                    } else {
0285:                        message += "<no-field>";
0286:                    }
0287:
0288:                    throw new DeploymentException(message);
0289:                }
0290:
0291:                // Related Manager
0292:                relatedManager = (JDBCStoreManager) relatedEntity.getManager();
0293:
0294:                // Related Container
0295:                EntityContainer relatedContainer = relatedManager
0296:                        .getContainer();
0297:                this .relatedContainerRef = new WeakReference(relatedContainer);
0298:
0299:                // related findByPrimaryKey
0300:                Class homeClass = (relatedContainer.getLocalHomeClass() != null ? relatedContainer
0301:                        .getLocalHomeClass()
0302:                        : relatedContainer.getHomeClass());
0303:                try {
0304:                    relatedFindByPrimaryKey = homeClass.getMethod(
0305:                            "findByPrimaryKey", new Class[] { relatedEntity
0306:                                    .getPrimaryKeyClass() });
0307:                } catch (Exception e) {
0308:                    throw new DeploymentException("findByPrimaryKey("
0309:                            + relatedEntity.getPrimaryKeyClass().getName()
0310:                            + " pk) was not found in " + homeClass.getName());
0311:                }
0312:
0313:                //
0314:                // Initialize the key fields
0315:                //
0316:                if (metadata.getRelationMetaData().isTableMappingStyle()) {
0317:                    // initialize relation table key fields
0318:                    Collection tableKeys = metadata.getKeyFields();
0319:                    List keyFieldsList = new ArrayList(tableKeys.size());
0320:
0321:                    // first phase is to create fk fields
0322:                    Map pkFieldsToFKFields = new HashMap(tableKeys.size());
0323:                    for (Iterator i = tableKeys.iterator(); i.hasNext();) {
0324:                        JDBCCMPFieldMetaData cmpFieldMetaData = (JDBCCMPFieldMetaData) i
0325:                                .next();
0326:                        FieldBridge pkField = entity
0327:                                .getFieldByName(cmpFieldMetaData.getFieldName());
0328:                        if (pkField == null) {
0329:                            throw new DeploymentException(
0330:                                    "Primary key not found for key-field "
0331:                                            + cmpFieldMetaData.getFieldName());
0332:                        }
0333:                        pkFieldsToFKFields.put(pkField,
0334:                                new JDBCCMP2xFieldBridge(manager,
0335:                                        cmpFieldMetaData));
0336:                    }
0337:                    // second step is to order fk fields to match the order of pk fields
0338:                    JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
0339:                    for (int i = 0; i < pkFields.length; ++i) {
0340:                        Object fkField = pkFieldsToFKFields.get(pkFields[i]);
0341:                        if (fkField == null) {
0342:                            throw new DeploymentException("Primary key "
0343:                                    + pkFields[i].getFieldName()
0344:                                    + " is not mapped.");
0345:                        }
0346:                        keyFieldsList.add(fkField);
0347:                    }
0348:                    tableKeyFields = (JDBCCMP2xFieldBridge[]) keyFieldsList
0349:                            .toArray(new JDBCCMP2xFieldBridge[keyFieldsList
0350:                                    .size()]);
0351:
0352:                    dataSource = metadata.getRelationMetaData().getDataSource();
0353:                } else {
0354:                    initializeForeignKeyFields();
0355:                    dataSource = hasForeignKey() ? entity.getDataSource()
0356:                            : relatedEntity.getDataSource();
0357:                }
0358:
0359:                // Fix table name
0360:                //
0361:                // This code doesn't work here...  The problem each side will generate
0362:                // the table name and this will only work for simple generation.
0363:                qualifiedTableName = SQLUtil.fixTableName(metadata
0364:                        .getRelationMetaData().getDefaultTableName(),
0365:                        dataSource);
0366:                tableName = SQLUtil
0367:                        .getTableNameWithoutSchema(qualifiedTableName);
0368:
0369:                relationManager = relatedCMRField.initRelationManager(this );
0370:            }
0371:
0372:            /**
0373:             * The third phase of deployment. The method is called when relationships are already resolved.
0374:             *
0375:             * @throws DeploymentException
0376:             */
0377:            public void start() throws DeploymentException {
0378:                cascadeDeleteStrategy = CascadeDeleteStrategy
0379:                        .getCascadeDeleteStrategy(this );
0380:            }
0381:
0382:            public boolean removeFromRelations(EntityEnterpriseContext ctx,
0383:                    Object[] oldRelationsRef) {
0384:                load(ctx);
0385:
0386:                FieldState fieldState = getFieldState(ctx);
0387:                List value = fieldState.getValue();
0388:
0389:                boolean removed = false;
0390:                if (!value.isEmpty()) {
0391:                    if (hasFKFieldsMappedToCMPFields) {
0392:                        if (isForeignKeyValid(value.get(0))) {
0393:                            cascadeDeleteStrategy.removedIds(ctx,
0394:                                    oldRelationsRef, value);
0395:                            removed = true;
0396:                        }
0397:                    } else {
0398:                        cascadeDeleteStrategy.removedIds(ctx, oldRelationsRef,
0399:                                value);
0400:                        removed = true;
0401:                    }
0402:                }
0403:                return removed;
0404:            }
0405:
0406:            public void cascadeDelete(EntityEnterpriseContext ctx,
0407:                    List oldValues) throws RemoveException, RemoteException {
0408:                cascadeDeleteStrategy.cascadeDelete(ctx, oldValues);
0409:            }
0410:
0411:            public boolean isBatchCascadeDelete() {
0412:                return (cascadeDeleteStrategy instanceof  CascadeDeleteStrategy.BatchCascadeDeleteStrategy);
0413:            }
0414:
0415:            /**
0416:             * Gets the manager of this entity.
0417:             */
0418:            public JDBCStoreManager getJDBCStoreManager() {
0419:                return manager;
0420:            }
0421:
0422:            /**
0423:             * Gets bridge for this entity.
0424:             */
0425:            public JDBCAbstractEntityBridge getEntity() {
0426:                return entity;
0427:            }
0428:
0429:            /**
0430:             * Gets the metadata of the relationship role that this field represents.
0431:             */
0432:            public JDBCRelationshipRoleMetaData getMetaData() {
0433:                return metadata;
0434:            }
0435:
0436:            /**
0437:             * Gets the relation metadata.
0438:             */
0439:            public JDBCRelationMetaData getRelationMetaData() {
0440:                return metadata.getRelationMetaData();
0441:            }
0442:
0443:            /**
0444:             * Gets the name of this field.
0445:             */
0446:            public String getFieldName() {
0447:                return metadata.getCMRFieldName();
0448:            }
0449:
0450:            /**
0451:             * Gets the name of the relation table if relevent.
0452:             */
0453:            public String getQualifiedTableName() {
0454:                return qualifiedTableName;
0455:            }
0456:
0457:            public String getTableName() {
0458:                return tableName;
0459:            }
0460:
0461:            /**
0462:             * Gets the datasource of the relation table if relevent.
0463:             */
0464:            public DataSource getDataSource() {
0465:                return dataSource;
0466:            }
0467:
0468:            /**
0469:             * Gets the read ahead meta data.
0470:             */
0471:            public JDBCReadAheadMetaData getReadAhead() {
0472:                return metadata.getReadAhead();
0473:            }
0474:
0475:            public JDBCType getJDBCType() {
0476:                return jdbcType;
0477:            }
0478:
0479:            public boolean isPrimaryKeyMember() {
0480:                return false;
0481:            }
0482:
0483:            /**
0484:             * Does this cmr field have foreign keys.
0485:             */
0486:            public boolean hasForeignKey() {
0487:                return foreignKeyFields != null;
0488:            }
0489:
0490:            /**
0491:             * Returns true if all FK fields are mapped to PK fields
0492:             */
0493:            public boolean allFkFieldsMappedToPkFields() {
0494:                return allFKFieldsMappedToPKFields;
0495:            }
0496:
0497:            /**
0498:             * Is this a collection valued field.
0499:             */
0500:            public boolean isCollectionValued() {
0501:                return metadata.getRelatedRole().isMultiplicityMany();
0502:            }
0503:
0504:            /**
0505:             * Is this a single valued field.
0506:             */
0507:            public boolean isSingleValued() {
0508:                return metadata.getRelatedRole().isMultiplicityOne();
0509:            }
0510:
0511:            /**
0512:             * Gets the key fields that this entity maintains in the relation table.
0513:             */
0514:            public JDBCFieldBridge[] getTableKeyFields() {
0515:                return tableKeyFields;
0516:            }
0517:
0518:            /**
0519:             * Gets the foreign key fields of this entity (i.e., related entities pk fields)
0520:             */
0521:            public JDBCFieldBridge[] getForeignKeyFields() {
0522:                return foreignKeyFields;
0523:            }
0524:
0525:            /**
0526:             * The related entity's cmr field for this relationship.
0527:             */
0528:            public JDBCAbstractCMRFieldBridge getRelatedCMRField() {
0529:                return relatedCMRField;
0530:            }
0531:
0532:            /**
0533:             * The related manger.
0534:             */
0535:            public JDBCStoreManager getRelatedManager() {
0536:                return relatedManager;
0537:            }
0538:
0539:            /**
0540:             * The related entity.
0541:             */
0542:            public EntityBridge getRelatedEntity() {
0543:                return relatedEntity;
0544:            }
0545:
0546:            /**
0547:             * The related entity.
0548:             */
0549:            public JDBCEntityBridge getRelatedJDBCEntity() {
0550:                return relatedEntity;
0551:            }
0552:
0553:            /**
0554:             * The related container
0555:             */
0556:            private final EntityContainer getRelatedContainer() {
0557:                return (EntityContainer) relatedContainerRef.get();
0558:            }
0559:
0560:            /**
0561:             * The related entity's local home interface.
0562:             */
0563:            public final Class getRelatedLocalInterface() {
0564:                return getRelatedContainer().getLocalClass();
0565:            }
0566:
0567:            /**
0568:             * The related entity's local container invoker.
0569:             */
0570:            public final LocalProxyFactory getRelatedInvoker() {
0571:                return getRelatedContainer().getLocalProxyFactory();
0572:            }
0573:
0574:            /**
0575:             * @param ctx - entity's context
0576:             * @return true if entity is loaded, false - otherwise.
0577:             */
0578:            public boolean isLoaded(EntityEnterpriseContext ctx) {
0579:                return getFieldState(ctx).isLoaded;
0580:            }
0581:
0582:            /**
0583:             * Establishes relationships with related entities waited for passed in context
0584:             * to be created.
0585:             *
0586:             * @param ctx - entity's context.
0587:             */
0588:            public void addRelatedPKsWaitedForMe(EntityEnterpriseContext ctx) {
0589:                final Map relatedPKsMap = getRelatedPKsWaitingForMyPK();
0590:                synchronized (relatedPKsMap) {
0591:                    List relatedPKsWaitingForMe = (List) relatedPKsMap.get(ctx
0592:                            .getId());
0593:                    if (relatedPKsWaitingForMe != null) {
0594:                        for (Iterator waitingPKsIter = relatedPKsWaitingForMe
0595:                                .iterator(); waitingPKsIter.hasNext();) {
0596:                            Object waitingPK = waitingPKsIter.next();
0597:                            waitingPKsIter.remove();
0598:                            if (isForeignKeyValid(waitingPK)) {
0599:                                createRelationLinks(ctx, waitingPK);
0600:                            }
0601:                        }
0602:                    }
0603:                }
0604:            }
0605:
0606:            /**
0607:             * Is this field readonly?
0608:             */
0609:            public boolean isReadOnly() {
0610:                return getRelationMetaData().isReadOnly();
0611:            }
0612:
0613:            /**
0614:             * Had the read time expired?
0615:             */
0616:            public boolean isReadTimedOut(EntityEnterpriseContext ctx) {
0617:                // if we are read/write then we are always timed out
0618:                if (!isReadOnly()) {
0619:                    return true;
0620:                }
0621:
0622:                // if read-time-out is -1 then we never time out.
0623:                if (getRelationMetaData().getReadTimeOut() == -1) {
0624:                    return false;
0625:                }
0626:
0627:                long readInterval = System.currentTimeMillis()
0628:                        - getFieldState(ctx).getLastRead();
0629:                return readInterval > getRelationMetaData().getReadTimeOut();
0630:            }
0631:
0632:            /**
0633:             * @param ctx - entity's context.
0634:             * @return the value of this field.
0635:             */
0636:            public Object getValue(EntityEnterpriseContext ctx) {
0637:                // no user checks yet, but this is where they would go
0638:                return getInstanceValue(ctx);
0639:            }
0640:
0641:            /**
0642:             * Sets new value.
0643:             *
0644:             * @param ctx   - entity's context;
0645:             * @param value - new value.
0646:             */
0647:            public void setValue(EntityEnterpriseContext ctx, Object value) {
0648:                if (isReadOnly()) {
0649:                    throw new EJBException("Field is read-only: fieldName="
0650:                            + getFieldName());
0651:                }
0652:
0653:                if (!JDBCEntityBridge.isEjbCreateDone(ctx)) {
0654:                    throw new IllegalStateException(
0655:                            "A CMR field cannot be set "
0656:                                    + "in ejbCreate; this should be done in the ejbPostCreate "
0657:                                    + "method instead [EJB 2.0 Spec. 10.5.2].");
0658:                }
0659:
0660:                if (isCollectionValued() && value == null) {
0661:                    throw new IllegalArgumentException(
0662:                            "null cannot be assigned to a "
0663:                                    + "collection-valued cmr-field [EJB 2.0 Spec. 10.3.8].");
0664:                }
0665:                /*
0666:                if(allFKFieldsMappedToPKFields)
0667:                {
0668:                   throw new IllegalStateException(
0669:                      "Can't modify relationship: CMR field "
0670:                      + entity.getEntityName() + "." + getFieldName()
0671:                      + " has foreign key fields mapped to the primary key columns."
0672:                      + " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5].");
0673:                }
0674:                 */
0675:
0676:                setInstanceValue(ctx, value);
0677:            }
0678:
0679:            /**
0680:             * Gets the value of the cmr field for the instance associated with
0681:             * the context.
0682:             */
0683:            public Object getInstanceValue(EntityEnterpriseContext myCtx) {
0684:                load(myCtx);
0685:
0686:                FieldState fieldState = getFieldState(myCtx);
0687:                if (isCollectionValued()) {
0688:                    return fieldState.getRelationSet();
0689:                }
0690:
0691:                // only return one
0692:                try {
0693:                    List value = fieldState.getValue();
0694:                    if (!value.isEmpty()) {
0695:                        Object fk = value.get(0);
0696:                        return getRelatedEntityByFK(fk);
0697:                    } else if (foreignKeyFields != null) {
0698:                        // for those completely mapped to CMP fields and created in this current tx !!!
0699:                        Object relatedId = getRelatedIdFromContext(myCtx);
0700:                        if (relatedId != null) {
0701:                            return getRelatedEntityByFK(relatedId);
0702:                        }
0703:                    }
0704:                    return null;
0705:                } catch (EJBException e) {
0706:                    throw e;
0707:                } catch (Exception e) {
0708:                    throw new EJBException(e);
0709:                }
0710:            }
0711:
0712:            /**
0713:             * Returns related entity's local interface.
0714:             * If there are foreign key fields mapped to CMP fields, existence of related entity is checked
0715:             * with findByPrimaryKey and if, in this case, related instance is not found, null is returned.
0716:             * If foreign key fields mapped to its own columns then existence of related entity is not checked
0717:             * and just its local object is returned.
0718:             *
0719:             * @param fk - foreign key value.
0720:             * @return related local object instance.
0721:             */
0722:            public EJBLocalObject getRelatedEntityByFK(Object fk) {
0723:                EJBLocalObject relatedLocalObject = null;
0724:                final EntityContainer relatedContainer = getRelatedContainer();
0725:
0726:                if (hasFKFieldsMappedToCMPFields
0727:                        && relatedManager.getReadAheadCache()
0728:                                .getPreloadDataMap(fk, false) == null // not in preload cache
0729:                ) {
0730:                    EJBLocalHome relatedHome = relatedContainer
0731:                            .getLocalProxyFactory().getEJBLocalHome();
0732:                    try {
0733:                        relatedLocalObject = (EJBLocalObject) relatedFindByPrimaryKey
0734:                                .invoke(relatedHome, new Object[] { fk });
0735:                    } catch (Exception ignore) {
0736:                        // no such entity. it is ok to ignore
0737:                    }
0738:                } else {
0739:                    relatedLocalObject = relatedContainer
0740:                            .getLocalProxyFactory().getEntityEJBLocalObject(fk);
0741:                }
0742:
0743:                return relatedLocalObject;
0744:            }
0745:
0746:            /**
0747:             * This method is called only for CMR fields with foreign key fields mapped to CMP fields
0748:             * to check the validity of the foreign key value.
0749:             *
0750:             * @param fk the foreign key to check
0751:             * @return true if there is related entity with the equal primary key
0752:             */
0753:            public boolean isForeignKeyValid(Object fk) {
0754:                boolean valid;
0755:                if (relatedManager.getReadAheadCache().getPreloadDataMap(fk,
0756:                        false) != null) {
0757:                    valid = true;
0758:                } else {
0759:                    EJBLocalHome relatedHome = getRelatedContainer()
0760:                            .getLocalProxyFactory().getEJBLocalHome();
0761:                    try {
0762:                        relatedFindByPrimaryKey.invoke(relatedHome,
0763:                                new Object[] { fk });
0764:                        valid = true;
0765:                    } catch (Exception ignore) {
0766:                        // no such entity. it is ok to ignore
0767:                        valid = false;
0768:                    }
0769:                }
0770:                return valid;
0771:            }
0772:
0773:            /**
0774:             * Sets the value of the cmr field for the instance associated with
0775:             * the context.
0776:             */
0777:            public void setInstanceValue(EntityEnterpriseContext myCtx,
0778:                    Object newValue) {
0779:                // validate new value first
0780:                List newPks;
0781:                if (newValue instanceof  Collection) {
0782:                    Collection col = (Collection) newValue;
0783:                    if (!col.isEmpty()) {
0784:                        newPks = new ArrayList(col.size());
0785:                        for (Iterator iter = col.iterator(); iter.hasNext();) {
0786:                            Object localObject = iter.next();
0787:                            if (localObject != null) {
0788:                                Object relatedId = getRelatedPrimaryKey(localObject);
0789:
0790:                                // check whether new value modifies the primary key if there are FK fields mapped to PK fields
0791:                                if (relatedPKFieldsByMyPKFields.size() > 0) {
0792:                                    checkSetForeignKey(myCtx, relatedId);
0793:                                }
0794:
0795:                                newPks.add(relatedId);
0796:                            }
0797:                        }
0798:                    } else {
0799:                        newPks = Collections.EMPTY_LIST;
0800:                    }
0801:                } else {
0802:                    if (newValue != null) {
0803:                        newPks = Collections
0804:                                .singletonList(getRelatedPrimaryKey(newValue));
0805:                    } else {
0806:                        newPks = Collections.EMPTY_LIST;
0807:                    }
0808:                }
0809:
0810:                // load the current value
0811:                load(myCtx);
0812:                FieldState fieldState = getFieldState(myCtx);
0813:
0814:                // is this just setting our own relation set back
0815:                if (newValue == fieldState.getRelationSet()) {
0816:                    return;
0817:                }
0818:
0819:                try {
0820:                    // Remove old value(s)
0821:                    List value = fieldState.getValue();
0822:                    if (!value.isEmpty()) {
0823:                        Object[] curPks = value
0824:                                .toArray(new Object[value.size()]);
0825:                        for (int i = 0; i < curPks.length; ++i) {
0826:                            destroyRelationLinks(myCtx, curPks[i]);
0827:                        }
0828:                    }
0829:
0830:                    // Add new value(s)
0831:                    for (int i = 0; i < newPks.size(); ++i) {
0832:                        createRelationLinks(myCtx, newPks.get(i));
0833:                    }
0834:                } catch (RuntimeException e) {
0835:                    throw e;
0836:                } catch (Exception e) {
0837:                    throw new EJBException(e);
0838:                }
0839:            }
0840:
0841:            /**
0842:             * Checks whether new foreign key value conflicts with primary key value
0843:             * in case of foreign key to primary key mapping.
0844:             *
0845:             * @param myCtx    - entity's context;
0846:             * @param newValue - new foreign key value.
0847:             * @throws IllegalStateException - if new foreign key value changes
0848:             *                               primary key value, otherwise returns silently.
0849:             */
0850:            private void checkSetForeignKey(EntityEnterpriseContext myCtx,
0851:                    Object newValue) throws IllegalStateException {
0852:                JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
0853:                for (int i = 0; i < pkFields.length; ++i) {
0854:                    JDBCCMP2xFieldBridge pkField = (JDBCCMP2xFieldBridge) pkFields[i];
0855:                    JDBCCMP2xFieldBridge relatedPkField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyPKFields
0856:                            .get(pkField);
0857:                    if (relatedPkField != null) {
0858:                        Object comingValue = relatedPkField
0859:                                .getPrimaryKeyValue(newValue);
0860:                        Object currentValue = pkField.getInstanceValue(myCtx);
0861:
0862:                        // they shouldn't be null
0863:                        if (!comingValue.equals(currentValue)) {
0864:                            throw new IllegalStateException(
0865:                                    "Can't create relationship: CMR field "
0866:                                            + entity.getEntityName()
0867:                                            + "."
0868:                                            + getFieldName()
0869:                                            + " has foreign key fields mapped to the primary key columns."
0870:                                            + " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5]."
0871:                                            + " primary key value is "
0872:                                            + currentValue
0873:                                            + " overriding value is "
0874:                                            + comingValue);
0875:                        }
0876:                    }
0877:                }
0878:            }
0879:
0880:            /**
0881:             * Creates the relation links between the instance associated with the
0882:             * context and the related instance (just the id is passed in).
0883:             * <p/>
0884:             * This method calls a.addRelation(b) and b.addRelation(a)
0885:             */
0886:            public void createRelationLinks(EntityEnterpriseContext myCtx,
0887:                    Object relatedId) {
0888:                createRelationLinks(myCtx, relatedId, true);
0889:            }
0890:
0891:            public void createRelationLinks(EntityEnterpriseContext myCtx,
0892:                    Object relatedId, boolean updateForeignKey) {
0893:                if (isReadOnly()) {
0894:                    throw new EJBException("Field is read-only: "
0895:                            + getFieldName());
0896:                }
0897:
0898:                // If my multiplicity is one, then we need to free the new related context
0899:                // from its old relationship.
0900:                Transaction tx = getTransaction();
0901:                if (metadata.isMultiplicityOne()) {
0902:                    Object oldRelatedId = relatedCMRField.invokeGetRelatedId(
0903:                            tx, relatedId);
0904:                    if (oldRelatedId != null) {
0905:                        invokeRemoveRelation(tx, oldRelatedId, relatedId);
0906:                        relatedCMRField.invokeRemoveRelation(tx, relatedId,
0907:                                oldRelatedId);
0908:                    }
0909:                }
0910:
0911:                addRelation(myCtx, relatedId, updateForeignKey);
0912:                relatedCMRField.invokeAddRelation(tx, relatedId, myCtx.getId());
0913:            }
0914:
0915:            /**
0916:             * Destroys the relation links between the instance associated with the
0917:             * context and the related instance (just the id is passed in).
0918:             * <p/>
0919:             * This method calls a.removeRelation(b) and b.removeRelation(a)
0920:             */
0921:            public void destroyRelationLinks(EntityEnterpriseContext myCtx,
0922:                    Object relatedId) {
0923:                destroyRelationLinks(myCtx, relatedId, true);
0924:            }
0925:
0926:            /**
0927:             * Destroys the relation links between the instance associated with the
0928:             * context and the related instance (just the id is passed in).
0929:             * <p/>
0930:             * This method calls a.removeRelation(b) and b.removeRelation(a)
0931:             * <p/>
0932:             * If updateValueCollection is false, the related id collection is not
0933:             * updated. This form is only used by the RelationSet iterator.
0934:             */
0935:            public void destroyRelationLinks(EntityEnterpriseContext myCtx,
0936:                    Object relatedId, boolean updateValueCollection) {
0937:                destroyRelationLinks(myCtx, relatedId, updateValueCollection,
0938:                        true);
0939:            }
0940:
0941:            public void destroyRelationLinks(EntityEnterpriseContext myCtx,
0942:                    Object relatedId, boolean updateValueCollection,
0943:                    boolean updateForeignKey) {
0944:                if (isReadOnly()) {
0945:                    throw new EJBException("Field is read-only: "
0946:                            + getFieldName());
0947:                }
0948:
0949:                removeRelation(myCtx, relatedId, updateValueCollection,
0950:                        updateForeignKey);
0951:                relatedCMRField.invokeRemoveRelation(getTransaction(),
0952:                        relatedId, myCtx.getId());
0953:            }
0954:
0955:            /**
0956:             * Schedules children for cascade delete.
0957:             */
0958:            public void scheduleChildrenForCascadeDelete(
0959:                    EntityEnterpriseContext ctx) {
0960:                load(ctx);
0961:                FieldState fieldState = getFieldState(ctx);
0962:                List value = fieldState.getValue();
0963:                if (!value.isEmpty()) {
0964:                    Transaction tx = getTransaction();
0965:                    for (int i = 0; i < value.size(); ++i) {
0966:                        relatedCMRField.invokeScheduleForCascadeDelete(tx,
0967:                                value.get(i));
0968:                    }
0969:                }
0970:            }
0971:
0972:            /**
0973:             * Schedules children for batch cascade delete.
0974:             */
0975:            public void scheduleChildrenForBatchCascadeDelete(
0976:                    EntityEnterpriseContext ctx) {
0977:                load(ctx);
0978:                FieldState fieldState = getFieldState(ctx);
0979:                List value = fieldState.getValue();
0980:                if (!value.isEmpty()) {
0981:                    Transaction tx = getTransaction();
0982:                    for (int i = 0; i < value.size(); ++i) {
0983:                        relatedCMRField.invokeScheduleForBatchCascadeDelete(tx,
0984:                                value.get(i));
0985:                    }
0986:                }
0987:            }
0988:
0989:            /**
0990:             * Schedules the instance with myId for cascade delete.
0991:             */
0992:            private Object invokeScheduleForCascadeDelete(Transaction tx,
0993:                    Object myId) {
0994:                try {
0995:                    EntityCache instanceCache = (EntityCache) manager
0996:                            .getContainer().getInstanceCache();
0997:                    SecurityActions actions = SecurityActions.UTIL
0998:                            .getSecurityActions();
0999:
1000:                    CMRInvocation invocation = new CMRInvocation();
1001:                    invocation
1002:                            .setCmrMessage(CMRMessage.SCHEDULE_FOR_CASCADE_DELETE);
1003:                    invocation.setEntrancy(Entrancy.NON_ENTRANT);
1004:                    invocation.setId(instanceCache.createCacheKey(myId));
1005:                    invocation.setArguments(new Object[] { this  });
1006:                    invocation.setTransaction(tx);
1007:                    invocation.setPrincipal(actions.getPrincipal());
1008:                    invocation.setCredential(actions.getCredential());
1009:                    invocation.setType(InvocationType.LOCAL);
1010:                    return manager.getContainer().invoke(invocation);
1011:                } catch (EJBException e) {
1012:                    throw e;
1013:                } catch (Exception e) {
1014:                    throw new EJBException(
1015:                            "Error in scheduleForCascadeDelete()", e);
1016:                }
1017:            }
1018:
1019:            /**
1020:             * Schedules the instance with myId for batch cascade delete.
1021:             */
1022:            private Object invokeScheduleForBatchCascadeDelete(Transaction tx,
1023:                    Object myId) {
1024:                try {
1025:                    EntityCache instanceCache = (EntityCache) manager
1026:                            .getContainer().getInstanceCache();
1027:                    SecurityActions actions = SecurityActions.UTIL
1028:                            .getSecurityActions();
1029:
1030:                    CMRInvocation invocation = new CMRInvocation();
1031:                    invocation
1032:                            .setCmrMessage(CMRMessage.SCHEDULE_FOR_BATCH_CASCADE_DELETE);
1033:                    invocation.setEntrancy(Entrancy.NON_ENTRANT);
1034:                    invocation.setId(instanceCache.createCacheKey(myId));
1035:                    invocation.setArguments(new Object[] { this  });
1036:                    invocation.setTransaction(tx);
1037:                    invocation.setPrincipal(actions.getPrincipal());
1038:                    invocation.setCredential(actions.getCredential());
1039:                    invocation.setType(InvocationType.LOCAL);
1040:                    return manager.getContainer().invoke(invocation);
1041:                } catch (EJBException e) {
1042:                    throw e;
1043:                } catch (Exception e) {
1044:                    throw new EJBException(
1045:                            "Error in scheduleForBatchCascadeDelete()", e);
1046:                }
1047:            }
1048:
1049:            /**
1050:             * Invokes the getRelatedId on the related CMR field via the container
1051:             * invocation interceptor chain.
1052:             */
1053:            private Object invokeGetRelatedId(Transaction tx, Object myId) {
1054:                try {
1055:                    EntityCache instanceCache = (EntityCache) manager
1056:                            .getContainer().getInstanceCache();
1057:                    SecurityActions actions = SecurityActions.UTIL
1058:                            .getSecurityActions();
1059:
1060:                    CMRInvocation invocation = new CMRInvocation();
1061:                    invocation.setCmrMessage(CMRMessage.GET_RELATED_ID);
1062:                    invocation.setEntrancy(Entrancy.NON_ENTRANT);
1063:                    invocation.setId(instanceCache.createCacheKey(myId));
1064:                    invocation.setArguments(new Object[] { this  });
1065:                    invocation.setTransaction(tx);
1066:                    invocation.setPrincipal(actions.getPrincipal());
1067:                    invocation.setCredential(actions.getCredential());
1068:                    invocation.setType(InvocationType.LOCAL);
1069:                    return manager.getContainer().invoke(invocation);
1070:                } catch (EJBException e) {
1071:                    throw e;
1072:                } catch (Exception e) {
1073:                    throw new EJBException("Error in getRelatedId", e);
1074:                }
1075:            }
1076:
1077:            /**
1078:             * Invokes the addRelation on the related CMR field via the container
1079:             * invocation interceptor chain.
1080:             */
1081:            private void invokeAddRelation(Transaction tx, Object myId,
1082:                    Object relatedId) {
1083:                try {
1084:                    EntityCache instanceCache = (EntityCache) manager
1085:                            .getContainer().getInstanceCache();
1086:                    SecurityActions actions = SecurityActions.UTIL
1087:                            .getSecurityActions();
1088:
1089:                    CMRInvocation invocation = new CMRInvocation();
1090:                    invocation.setCmrMessage(CMRMessage.ADD_RELATION);
1091:                    invocation.setEntrancy(Entrancy.NON_ENTRANT);
1092:                    invocation.setId(instanceCache.createCacheKey(myId));
1093:                    invocation.setArguments(new Object[] { this , relatedId });
1094:                    invocation.setTransaction(tx);
1095:                    invocation.setPrincipal(actions.getPrincipal());
1096:                    invocation.setCredential(actions.getCredential());
1097:                    invocation.setType(InvocationType.LOCAL);
1098:                    manager.getContainer().invoke(invocation);
1099:                } catch (EJBException e) {
1100:                    throw e;
1101:                } catch (Exception e) {
1102:                    throw new EJBException("Error in addRelation", e);
1103:                }
1104:            }
1105:
1106:            /**
1107:             * Invokes the removeRelation on the related CMR field via the container
1108:             * invocation interceptor chain.
1109:             */
1110:            private void invokeRemoveRelation(Transaction tx, Object myId,
1111:                    Object relatedId) {
1112:                try {
1113:                    EntityCache instanceCache = (EntityCache) manager
1114:                            .getContainer().getInstanceCache();
1115:                    SecurityActions actions = SecurityActions.UTIL
1116:                            .getSecurityActions();
1117:
1118:                    CMRInvocation invocation = new CMRInvocation();
1119:                    invocation.setCmrMessage(CMRMessage.REMOVE_RELATION);
1120:                    invocation.setEntrancy(Entrancy.NON_ENTRANT);
1121:                    invocation.setId(instanceCache.createCacheKey(myId));
1122:                    invocation.setArguments(new Object[] { this , relatedId });
1123:                    invocation.setTransaction(tx);
1124:                    invocation.setPrincipal(actions.getPrincipal());
1125:                    invocation.setCredential(actions.getCredential());
1126:                    invocation.setType(InvocationType.LOCAL);
1127:                    manager.getContainer().invoke(invocation);
1128:                } catch (EJBException e) {
1129:                    throw e;
1130:                } catch (Exception e) {
1131:                    throw new EJBException("Error in removeRelation", e);
1132:                }
1133:            }
1134:
1135:            /**
1136:             * Get the related entity's id.  This only works on single valued cmr fields.
1137:             */
1138:            public Object getRelatedId(EntityEnterpriseContext myCtx) {
1139:                if (isCollectionValued()) {
1140:                    throw new EJBException(
1141:                            "getRelatedId may only be called on a cmr-field with a multiplicity of one.");
1142:                }
1143:
1144:                load(myCtx);
1145:                List value = getFieldState(myCtx).getValue();
1146:                return value.isEmpty() ? null : value.get(0);
1147:            }
1148:
1149:            /**
1150:             * Creates a new instance of related id based on foreign key value in the context.
1151:             *
1152:             * @param ctx - entity's context.
1153:             * @return related entity's id.
1154:             */
1155:            public Object getRelatedIdFromContext(EntityEnterpriseContext ctx) {
1156:                Object relatedId = null;
1157:                Object fkFieldValue;
1158:                for (int i = 0; i < foreignKeyFields.length; ++i) {
1159:                    JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1160:                    fkFieldValue = fkField.getInstanceValue(ctx);
1161:                    if (fkFieldValue == null) {
1162:                        return null;
1163:                    }
1164:                    JDBCCMP2xFieldBridge relatedPKField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyFKFields
1165:                            .get(fkField);
1166:                    relatedId = relatedPKField.setPrimaryKeyValue(relatedId,
1167:                            fkFieldValue);
1168:                }
1169:                return relatedId;
1170:            }
1171:
1172:            /**
1173:             * Adds the foreign key to the set of related ids, and updates any foreign key fields.
1174:             */
1175:            public void addRelation(EntityEnterpriseContext myCtx, Object fk) {
1176:                addRelation(myCtx, fk, true);
1177:                relationManager.addRelation(this , myCtx.getId(),
1178:                        relatedCMRField, fk);
1179:            }
1180:
1181:            private void addRelation(EntityEnterpriseContext myCtx, Object fk,
1182:                    boolean updateForeignKey) {
1183:                checkSetForeignKey(myCtx, fk);
1184:
1185:                if (isReadOnly()) {
1186:                    throw new EJBException("Field is read-only: "
1187:                            + getFieldName());
1188:                }
1189:
1190:                if (!JDBCEntityBridge.isEjbCreateDone(myCtx)) {
1191:                    throw new IllegalStateException(
1192:                            "A CMR field cannot be set or added "
1193:                                    + "to a relationship in ejbCreate; this should be done in the "
1194:                                    + "ejbPostCreate method instead [EJB 2.0 Spec. 10.5.2].");
1195:                }
1196:
1197:                // add to current related set
1198:                FieldState myState = getFieldState(myCtx);
1199:                myState.addRelation(fk);
1200:
1201:                // set the foreign key, if we have one.
1202:                if (hasForeignKey() && updateForeignKey) {
1203:                    setForeignKey(myCtx, fk);
1204:                }
1205:            }
1206:
1207:            /**
1208:             * Removes the foreign key to the set of related ids, and updates any foreign key fields.
1209:             */
1210:            public void removeRelation(EntityEnterpriseContext myCtx, Object fk) {
1211:                removeRelation(myCtx, fk, true, true);
1212:                relationManager.removeRelation(this , myCtx.getId(),
1213:                        relatedCMRField, fk);
1214:            }
1215:
1216:            private void removeRelation(EntityEnterpriseContext myCtx,
1217:                    Object fk, boolean updateValueCollection,
1218:                    boolean updateForeignKey) {
1219:                if (isReadOnly()) {
1220:                    throw new EJBException("Field is read-only: "
1221:                            + getFieldName());
1222:                }
1223:
1224:                // remove from current related set
1225:                if (updateValueCollection) {
1226:                    FieldState myState = getFieldState(myCtx);
1227:                    myState.removeRelation(fk);
1228:                }
1229:
1230:                // set the foreign key to null, if we have one.
1231:                if (hasForeignKey() && updateForeignKey) {
1232:                    setForeignKey(myCtx, null);
1233:                }
1234:            }
1235:
1236:            /**
1237:             * loads the collection of related ids
1238:             * NOTE: after loading, the field might not be in a clean state as we support adding and removing
1239:             * relations while the field is not loaded. The actual value of the field will be the value loaded
1240:             * plus added relations and minus removed relations while the field was not loaded.
1241:             */
1242:            private void load(EntityEnterpriseContext myCtx) {
1243:                // if we are already loaded we're done
1244:                FieldState fieldState = getFieldState(myCtx);
1245:                if (fieldState.isLoaded()) {
1246:                    return;
1247:                }
1248:
1249:                // check the preload cache
1250:                if (log.isTraceEnabled()) {
1251:                    log.trace("Read ahead cahce load: cmrField="
1252:                            + getFieldName() + " pk=" + myCtx.getId());
1253:                }
1254:
1255:                manager.getReadAheadCache().load(myCtx);
1256:                if (fieldState.isLoaded()) {
1257:                    return;
1258:                }
1259:
1260:                // load the value from the database
1261:                Collection values;
1262:                if (hasForeignKey()) {
1263:                    // WARN: this method will load foreign keys if they are not yet loaded and
1264:                    // changes relationship lazy loading in advanced training labs.
1265:                    // i.e. it will load lazy cmp fields first of this entity and then will lazy load the related entity
1266:                    // instead of loading this entity JOIN related entity in one query.
1267:                    //Object fk = getRelatedIdFromContext(myCtx);
1268:
1269:                    boolean loadWithManager = false;
1270:                    Object fk = null;
1271:                    for (int i = 0; i < foreignKeyFields.length; ++i) {
1272:                        JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1273:                        // if the field is not loaded then load relationship with manager
1274:                        if (!fkField.isLoaded(myCtx)) {
1275:                            loadWithManager = true;
1276:                            break;
1277:                        }
1278:
1279:                        Object fkFieldValue = fkField.getInstanceValue(myCtx);
1280:                        // if one of the fk is null, the whole fk is considered to be null
1281:                        if (fkFieldValue == null) {
1282:                            fk = null;
1283:                            break;
1284:                        }
1285:                        JDBCCMP2xFieldBridge relatedPKField = (JDBCCMP2xFieldBridge) relatedPKFieldsByMyFKFields
1286:                                .get(fkField);
1287:                        fk = relatedPKField
1288:                                .setPrimaryKeyValue(fk, fkFieldValue);
1289:                    }
1290:
1291:                    if (loadWithManager) {
1292:                        values = manager.loadRelation(this , myCtx.getId());
1293:                    } else {
1294:                        values = (fk == null ? Collections.EMPTY_LIST
1295:                                : Collections.singletonList(fk));
1296:                    }
1297:                } else {
1298:                    values = manager.loadRelation(this , myCtx.getId());
1299:                }
1300:                load(myCtx, values);
1301:            }
1302:
1303:            public void load(EntityEnterpriseContext myCtx, Collection values) {
1304:                // did we get more then one value for a single valued field
1305:                if (isSingleValued() && values.size() > 1) {
1306:                    throw new EJBException(
1307:                            "Data contains multiple values, but this cmr field is single valued: "
1308:                                    + values);
1309:                }
1310:
1311:                // add the new values
1312:                FieldState fieldState = getFieldState(myCtx);
1313:                fieldState.loadRelations(values);
1314:
1315:                // set the foreign key, if we have one.
1316:                if (hasForeignKey()) {
1317:                    // update the states and locked values of FK fields
1318:                    if (!values.isEmpty()) {
1319:                        Object loadedValue = values.iterator().next();
1320:                        for (int i = 0; i < foreignKeyFields.length; ++i) {
1321:                            JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1322:                            Object fieldValue = fkField
1323:                                    .getPrimaryKeyValue(loadedValue);
1324:                            fkField.updateState(myCtx, fieldValue);
1325:                        }
1326:                    }
1327:
1328:                    // set the real FK value
1329:                    List realValue = fieldState.getValue();
1330:                    Object fk = realValue.isEmpty() ? null : realValue.get(0);
1331:                    setForeignKey(myCtx, fk);
1332:                }
1333:
1334:                JDBCEntityBridge.setCreated(myCtx);
1335:            }
1336:
1337:            /**
1338:             * Sets the foreign key field value.
1339:             */
1340:            public void setForeignKey(EntityEnterpriseContext myCtx, Object fk) {
1341:                if (!hasForeignKey()) {
1342:                    throw new EJBException(getFieldName()
1343:                            + " CMR field does not have a foreign key to set.");
1344:                }
1345:
1346:                for (int i = 0; i < foreignKeyFields.length; ++i) {
1347:                    JDBCCMP2xFieldBridge fkField = foreignKeyFields[i];
1348:                    Object fieldValue = fkField.getPrimaryKeyValue(fk);
1349:                    fkField.setInstanceValue(myCtx, fieldValue);
1350:                }
1351:            }
1352:
1353:            /**
1354:             * Initialized the foreign key fields.
1355:             */
1356:            public void initInstance(EntityEnterpriseContext ctx) {
1357:                // mark this field as loaded
1358:                getFieldState(ctx).loadRelations(Collections.EMPTY_SET);
1359:
1360:                if (foreignKeyFields == null) {
1361:                    return;
1362:                }
1363:
1364:                for (int i = 0; i < foreignKeyFields.length; ++i) {
1365:                    JDBCCMP2xFieldBridge foreignKeyField = foreignKeyFields[i];
1366:                    if (!foreignKeyField.isFKFieldMappedToCMPField()) {
1367:                        foreignKeyField.setInstanceValue(ctx, null);
1368:                    }
1369:                }
1370:            }
1371:
1372:            /**
1373:             * resets the persistence context of the foreign key fields
1374:             */
1375:            public void resetPersistenceContext(EntityEnterpriseContext ctx) {
1376:                // only resetStats if the read has timed out
1377:                if (!isReadTimedOut(ctx)) {
1378:                    return;
1379:                }
1380:
1381:                // clear the field state
1382:                JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
1383:                // invalidate current field state
1384:                /*
1385:                FieldState currentFieldState = (FieldState) jdbcCtx.getFieldState(jdbcContextIndex);
1386:                if(currentFieldState != null)
1387:                   currentFieldState.invalidate();
1388:                 */
1389:                jdbcCtx.setFieldState(jdbcContextIndex, null);
1390:
1391:                if (foreignKeyFields == null) {
1392:                    return;
1393:                }
1394:
1395:                for (int i = 0; i < foreignKeyFields.length; ++i) {
1396:                    JDBCCMP2xFieldBridge foreignKeyField = foreignKeyFields[i];
1397:                    if (!foreignKeyField.isFKFieldMappedToCMPField()) {
1398:                        foreignKeyField.resetPersistenceContext(ctx);
1399:                    }
1400:                }
1401:            }
1402:
1403:            public int setInstanceParameters(PreparedStatement ps,
1404:                    int parameterIndex, EntityEnterpriseContext ctx) {
1405:                if (foreignKeyFields == null) {
1406:                    return parameterIndex;
1407:                }
1408:
1409:                List value = getFieldState(ctx).getValue();
1410:                Object fk = (value.isEmpty() ? null : value.get(0));
1411:
1412:                for (int i = 0; i < foreignKeyFields.length; ++i) {
1413:                    parameterIndex = foreignKeyFields[i]
1414:                            .setPrimaryKeyParameters(ps, parameterIndex, fk);
1415:                }
1416:
1417:                return parameterIndex;
1418:            }
1419:
1420:            public int loadInstanceResults(ResultSet rs, int parameterIndex,
1421:                    EntityEnterpriseContext ctx) {
1422:                if (!hasForeignKey()) {
1423:                    return parameterIndex;
1424:                }
1425:
1426:                // load the value from the database
1427:                Object[] ref = new Object[1];
1428:                parameterIndex = loadArgumentResults(rs, parameterIndex, ref);
1429:
1430:                // only actually set the value if the state is not already loaded
1431:                FieldState fieldState = getFieldState(ctx);
1432:                if (!fieldState.isLoaded()) {
1433:                    if (ref[0] != null) {
1434:                        load(ctx, Collections.singleton(ref[0]));
1435:                    } else {
1436:                        load(ctx, Collections.EMPTY_SET);
1437:                    }
1438:                }
1439:                return parameterIndex;
1440:            }
1441:
1442:            public int loadArgumentResults(ResultSet rs, int parameterIndex,
1443:                    Object[] fkRef) {
1444:                if (foreignKeyFields == null) {
1445:                    return parameterIndex;
1446:                }
1447:
1448:                boolean fkIsNull = false;
1449:
1450:                // value of this field,  will be filled in below
1451:                Object[] argumentRef = new Object[1];
1452:                for (int i = 0; i < foreignKeyFields.length; ++i) {
1453:                    JDBCCMPFieldBridge field = foreignKeyFields[i];
1454:                    parameterIndex = field.loadArgumentResults(rs,
1455:                            parameterIndex, argumentRef);
1456:
1457:                    if (fkIsNull) {
1458:                        continue;
1459:                    }
1460:                    if (field.getPrimaryKeyField() != null) {
1461:                        // if there is a null field among FK fields, the whole FK field is considered null.
1462:                        // NOTE: don't throw exception in this case, it's ok if FK is partly mapped to a PK
1463:                        // NOTE2: we still need to iterate through foreign key fields and 'load' them to
1464:                        // return correct parameterIndex.
1465:                        if (argumentRef[0] == null) {
1466:                            fkRef[0] = null;
1467:                            fkIsNull = true;
1468:                        } else {
1469:                            // if we don't have a pk object yet create one
1470:                            if (fkRef[0] == null) {
1471:                                fkRef[0] = relatedEntity
1472:                                        .createPrimaryKeyInstance();
1473:                            }
1474:                            try {
1475:                                // Set this field's value into the primary key object.
1476:                                field.getPrimaryKeyField().set(fkRef[0],
1477:                                        argumentRef[0]);
1478:                            } catch (Exception e) {
1479:                                // Non recoverable internal exception
1480:                                throw new EJBException(
1481:                                        "Internal error setting foreign-key field "
1482:                                                + getFieldName(), e);
1483:                            }
1484:                        }
1485:                    } else {
1486:                        // This field is the primary key, so no extraction is necessary.
1487:                        fkRef[0] = argumentRef[0];
1488:                    }
1489:                }
1490:                return parameterIndex;
1491:            }
1492:
1493:            /**
1494:             * This method is never called.
1495:             * In case of a CMR with foreign key fields, only the foreign key fields are asked for the dirty state.
1496:             */
1497:            public boolean isDirty(EntityEnterpriseContext ctx) {
1498:                return foreignKeyFields == null ? relationManager.isDirty()
1499:                        : false;
1500:            }
1501:
1502:            public boolean invalidateCache(EntityEnterpriseContext ctx) {
1503:                JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
1504:                FieldState fieldState = (FieldState) jdbcCtx
1505:                        .getFieldState(jdbcContextIndex);
1506:                return fieldState == null ? false : fieldState.isChanged();
1507:            }
1508:
1509:            /**
1510:             * This method is never called.
1511:             * In case of a CMR
1512:             * - with foreign key fields, the foreign key fields are cleaned when necessary according to CMP fields'
1513:             * behaviour.
1514:             * - from m:m relationship, added/removed key pairs are cleared in application tx data map on sync.
1515:             */
1516:            public void setClean(EntityEnterpriseContext ctx) {
1517:                throw new UnsupportedOperationException();
1518:            }
1519:
1520:            public boolean isCMPField() {
1521:                return false;
1522:            }
1523:
1524:            public JDBCEntityPersistenceStore getManager() {
1525:                return manager;
1526:            }
1527:
1528:            public boolean hasFKFieldsMappedToCMPFields() {
1529:                return hasFKFieldsMappedToCMPFields;
1530:            }
1531:
1532:            public void addRelatedPKWaitingForMyPK(Object myPK, Object relatedPK) {
1533:                Map relatedPKsWaitingForMyPK = getRelatedPKsWaitingForMyPK();
1534:                synchronized (relatedPKsWaitingForMyPK) {
1535:                    List relatedPKs = (List) relatedPKsWaitingForMyPK.get(myPK);
1536:                    if (relatedPKs == null) {
1537:                        relatedPKs = new ArrayList(1);
1538:                        relatedPKsWaitingForMyPK.put(myPK, relatedPKs);
1539:                    }
1540:                    relatedPKs.add(relatedPK);
1541:                }
1542:            }
1543:
1544:            public void removeRelatedPKWaitingForMyPK(Object myPK,
1545:                    Object relatedPK) {
1546:                final Map relatedPKMap = getRelatedPKsWaitingForMyPK();
1547:                synchronized (relatedPKMap) {
1548:                    List relatedPKs = (List) relatedPKMap.get(myPK);
1549:                    if (relatedPKs != null) {
1550:                        relatedPKs.remove(relatedPK);
1551:                    }
1552:                }
1553:            }
1554:
1555:            /**
1556:             * Gets the field state object from the persistence context.
1557:             */
1558:            private FieldState getFieldState(EntityEnterpriseContext ctx) {
1559:                JDBCContext jdbcCtx = (JDBCContext) ctx.getPersistenceContext();
1560:                FieldState fieldState = (FieldState) jdbcCtx
1561:                        .getFieldState(jdbcContextIndex);
1562:                if (fieldState == null) {
1563:                    fieldState = new FieldState(ctx);
1564:                    jdbcCtx.setFieldState(jdbcContextIndex, fieldState);
1565:                }
1566:                return fieldState;
1567:            }
1568:
1569:            /**
1570:             * Initializes foreign key fields
1571:             *
1572:             * @throws DeploymentException
1573:             */
1574:            private void initializeForeignKeyFields()
1575:                    throws DeploymentException {
1576:                Collection foreignKeys = metadata.getRelatedRole()
1577:                        .getKeyFields();
1578:
1579:                // temporary map used later to write fk fields in special order
1580:                Map fkFieldsByRelatedPKFields = new HashMap();
1581:                for (Iterator i = foreignKeys.iterator(); i.hasNext();) {
1582:                    JDBCCMPFieldMetaData fkFieldMetaData = (JDBCCMPFieldMetaData) i
1583:                            .next();
1584:                    JDBCCMP2xFieldBridge relatedPKField = (JDBCCMP2xFieldBridge) relatedEntity
1585:                            .getFieldByName(fkFieldMetaData.getFieldName());
1586:
1587:                    // now determine whether the fk is mapped to a pk column
1588:                    String fkColumnName = fkFieldMetaData.getColumnName();
1589:                    JDBCCMP2xFieldBridge fkField = null;
1590:
1591:                    // look among the CMP fields for the field with the same column name
1592:                    JDBCFieldBridge[] tableFields = entity.getTableFields();
1593:                    for (int tableInd = 0; tableInd < tableFields.length
1594:                            && fkField == null; ++tableInd) {
1595:                        JDBCCMP2xFieldBridge cmpField = (JDBCCMP2xFieldBridge) tableFields[tableInd];
1596:                        if (fkColumnName.equals(cmpField.getColumnName())) {
1597:                            hasFKFieldsMappedToCMPFields = true;
1598:
1599:                            // construct the foreign key field
1600:                            fkField = new JDBCCMP2xFieldBridge(
1601:                                    (JDBCStoreManager) cmpField.getManager(), // this cmpField's manager
1602:                                    relatedPKField.getFieldName(),
1603:                                    relatedPKField.getFieldType(),
1604:                                    cmpField.getJDBCType(), // this cmpField's jdbc type
1605:                                    relatedPKField.isReadOnly(), relatedPKField
1606:                                            .getReadTimeOut(), relatedPKField
1607:                                            .getPrimaryKeyClass(),
1608:                                    relatedPKField.getPrimaryKeyField(),
1609:                                    cmpField, // CMP field I am mapped to
1610:                                    this , fkColumnName);
1611:
1612:                            if (cmpField.isPrimaryKeyMember()) {
1613:                                relatedPKFieldsByMyPKFields.put(cmpField,
1614:                                        relatedPKField);
1615:                            }
1616:                        }
1617:                    }
1618:
1619:                    // if the fk is not a part of pk then create a new field
1620:                    if (fkField == null) {
1621:                        fkField = new JDBCCMP2xFieldBridge(manager,
1622:                                fkFieldMetaData, manager.getJDBCTypeFactory()
1623:                                        .getJDBCType(fkFieldMetaData));
1624:                    }
1625:
1626:                    fkFieldsByRelatedPKFields.put(relatedPKField, fkField); // temporary map
1627:                    relatedPKFieldsByMyFKFields.put(fkField, relatedPKField);
1628:                }
1629:
1630:                // Note: this important to order the foreign key fields so that their order matches
1631:                // the order of related entity's pk fields in case of complex primary keys.
1632:                // The order is important in fk-constraint generation and in SELECT when loading
1633:                if (fkFieldsByRelatedPKFields.size() > 0) {
1634:                    JDBCFieldBridge[] relatedPKFields = relatedEntity
1635:                            .getPrimaryKeyFields();
1636:                    List fkList = new ArrayList(relatedPKFields.length);
1637:                    for (int i = 0; i < relatedPKFields.length; ++i) {
1638:                        JDBCCMPFieldBridge fkField = (JDBCCMPFieldBridge) fkFieldsByRelatedPKFields
1639:                                .remove(relatedPKFields[i]);
1640:                        fkList.add(fkField);
1641:                    }
1642:                    foreignKeyFields = (JDBCCMP2xFieldBridge[]) fkList
1643:                            .toArray(new JDBCCMP2xFieldBridge[fkList.size()]);
1644:                } else {
1645:                    foreignKeyFields = null;
1646:                }
1647:
1648:                // are all FK fields mapped to PK fields?
1649:                allFKFieldsMappedToPKFields = relatedPKFieldsByMyPKFields
1650:                        .size() > 0
1651:                        && relatedPKFieldsByMyPKFields.size() == foreignKeyFields.length;
1652:
1653:                if (foreignKeyFields != null) {
1654:                    jdbcType = new CMRJDBCType(Arrays.asList(foreignKeyFields));
1655:                }
1656:            }
1657:
1658:            private Transaction getTransaction() {
1659:                try {
1660:                    EntityContainer container = getJDBCStoreManager()
1661:                            .getContainer();
1662:                    TransactionManager tm = container.getTransactionManager();
1663:                    return tm.getTransaction();
1664:                } catch (SystemException e) {
1665:                    throw new EJBException(
1666:                            "Error getting transaction from the transaction manager",
1667:                            e);
1668:                }
1669:            }
1670:
1671:            /**
1672:             * @return Map of lists of waiting related PK values keyed by not yet created this side's PK value.
1673:             */
1674:            private Map getRelatedPKsWaitingForMyPK() {
1675:                return (Map) relatedPKValuesWaitingForMyPK.get();
1676:            }
1677:
1678:            private RelationDataManager initRelationManager(
1679:                    JDBCCMRFieldBridge relatedField) {
1680:                if (relationManager == null) {
1681:                    if (metadata.getRelationMetaData().isTableMappingStyle()) {
1682:                        relationManager = new M2MRelationManager(this ,
1683:                                relatedField);
1684:                    } else {
1685:                        relationManager = EMPTY_RELATION_MANAGER;
1686:                    }
1687:                }
1688:                return relationManager;
1689:            }
1690:
1691:            private Object getRelatedPrimaryKey(Object localObject) {
1692:                Object relatedId;
1693:                if (relatedEntity.getLocalInterface().isAssignableFrom(
1694:                        localObject.getClass())) {
1695:                    EJBLocalObject local = (EJBLocalObject) localObject;
1696:                    try {
1697:                        relatedId = local.getPrimaryKey();
1698:                    } catch (NoSuchObjectLocalException e) {
1699:                        throw new IllegalArgumentException(e.getMessage());
1700:                    }
1701:
1702:                    /*
1703:                    if(relatedManager.wasCascadeDeleted(relatedId))
1704:                    {
1705:                       throw new IllegalArgumentException("The instance was cascade-deleted: pk=" + relatedId);
1706:                    }
1707:                     */
1708:                } else {
1709:                    throw new IllegalArgumentException(
1710:                            "The values of this field must be of type "
1711:                                    + relatedEntity.getLocalInterface()
1712:                                            .getName());
1713:                }
1714:                return relatedId;
1715:            }
1716:
1717:            public String toString() {
1718:                return entity.getEntityName() + '.' + getFieldName();
1719:            }
1720:
1721:            private final class FieldState {
1722:                private final EntityEnterpriseContext ctx;
1723:                private List[] setHandle = new List[1];
1724:                private Set addedRelations;
1725:                private Set removedRelations;
1726:                private Set relationSet;
1727:                private boolean isLoaded = false;
1728:                private final long lastRead = -1;
1729:
1730:                private boolean changed;
1731:
1732:                public FieldState(EntityEnterpriseContext ctx) {
1733:                    this .ctx = ctx;
1734:                    setHandle[0] = new ArrayList();
1735:                }
1736:
1737:                /**
1738:                 * Get the current value (list of primary keys).
1739:                 */
1740:                public List getValue() {
1741:                    if (!isLoaded) {
1742:                        throw new EJBException("CMR field value not loaded yet");
1743:                    }
1744:                    return Collections.unmodifiableList(setHandle[0]);
1745:                }
1746:
1747:                /**
1748:                 * Has this relation been loaded.
1749:                 */
1750:                public boolean isLoaded() {
1751:                    return isLoaded;
1752:                }
1753:
1754:                /**
1755:                 * When was this value last read from the datastore.
1756:                 */
1757:                public long getLastRead() {
1758:                    return lastRead;
1759:                }
1760:
1761:                /**
1762:                 * Add this foreign to the relationship.
1763:                 */
1764:                public void addRelation(Object fk) {
1765:                    if (isLoaded) {
1766:                        setHandle[0].add(fk);
1767:                    } else {
1768:                        if (removedRelations == null) {
1769:                            removedRelations = new HashSet();
1770:                            addedRelations = new HashSet();
1771:                        }
1772:                        removedRelations.remove(fk);
1773:                        addedRelations.add(fk);
1774:                    }
1775:
1776:                    changed = true;
1777:                }
1778:
1779:                /**
1780:                 * Remove this foreign to the relationship.
1781:                 */
1782:                public void removeRelation(Object fk) {
1783:                    if (isLoaded) {
1784:                        setHandle[0].remove(fk);
1785:                    } else {
1786:                        if (removedRelations == null) {
1787:                            removedRelations = new HashSet();
1788:                            addedRelations = new HashSet();
1789:                        }
1790:                        addedRelations.remove(fk);
1791:                        removedRelations.add(fk);
1792:                    }
1793:
1794:                    changed = true;
1795:                }
1796:
1797:                /**
1798:                 * loads the collection of related ids
1799:                 */
1800:                public void loadRelations(Collection values) {
1801:                    // check if we are aleready loaded
1802:                    if (isLoaded) {
1803:                        throw new EJBException(
1804:                                "CMR field value is already loaded");
1805:                    }
1806:
1807:                    // just in the case where there are lingering values
1808:                    setHandle[0].clear();
1809:
1810:                    // add the new values
1811:                    setHandle[0].addAll(values);
1812:
1813:                    if (removedRelations != null) {
1814:                        // remove the already removed values
1815:                        setHandle[0].removeAll(removedRelations);
1816:                        removedRelations = null;
1817:                    }
1818:
1819:                    if (addedRelations != null) {
1820:                        // add the already added values
1821:                        // but remove FKs we are going to add to avoid duplication
1822:                        setHandle[0].removeAll(addedRelations);
1823:                        setHandle[0].addAll(addedRelations);
1824:                        addedRelations = null;
1825:                    }
1826:
1827:                    // mark the field loaded
1828:                    isLoaded = true;
1829:                }
1830:
1831:                /**
1832:                 * Get the current relation set or create a new one.
1833:                 */
1834:                public Set getRelationSet() {
1835:                    if (!isLoaded) {
1836:                        throw new EJBException("CMR field value not loaded yet");
1837:                    }
1838:
1839:                    if (ctx.isReadOnly()) {
1840:                        // we are in a read-only invocation, so return a snapshot set
1841:                        return new RelationSet(JDBCCMRFieldBridge.this , ctx,
1842:                                new List[] { new ArrayList(setHandle[0]) },
1843:                                true);
1844:                    }
1845:
1846:                    // if we already have a relationset use it
1847:                    if (relationSet != null) {
1848:                        return relationSet;
1849:                    }
1850:
1851:                    // construct a new relationshet
1852:                    try {
1853:                        // get the curent transaction
1854:                        EntityContainer container = getJDBCStoreManager()
1855:                                .getContainer();
1856:                        TransactionManager tm = container
1857:                                .getTransactionManager();
1858:                        Transaction tx = tm.getTransaction();
1859:
1860:                        // if whe have a valid transaction...
1861:                        if (tx != null
1862:                                && (tx.getStatus() == Status.STATUS_ACTIVE || tx
1863:                                        .getStatus() == Status.STATUS_PREPARING)) {
1864:                            // crete the relation set and register for a tx callback
1865:                            relationSet = new RelationSet(
1866:                                    JDBCCMRFieldBridge.this , ctx, setHandle,
1867:                                    false);
1868:                            TxSynchronization sync = new TxSynchronization(
1869:                                    FieldState.this );
1870:                            tx.registerSynchronization(sync);
1871:                        } else {
1872:                            // if there is no transaction create a pre-failed list
1873:                            relationSet = new RelationSet(
1874:                                    JDBCCMRFieldBridge.this , ctx, new List[1],
1875:                                    false);
1876:                        }
1877:
1878:                        return relationSet;
1879:                    } catch (SystemException e) {
1880:                        throw new EJBException(
1881:                                "Error while creating RelationSet", e);
1882:                    } catch (RollbackException e) {
1883:                        throw new EJBException(
1884:                                "Error while creating RelationSet", e);
1885:                    }
1886:                }
1887:
1888:                /**
1889:                 * Invalidate the current relationship set.
1890:                 */
1891:                public void invalidate() {
1892:                    // make a new set handle and copy the currentList to the new handle
1893:                    // this will cause old references to the relationSet to throw an
1894:                    // IllegalStateException if accesses, but will not cause a reload
1895:                    // in Commit Option A
1896:                    List currentList = null;
1897:                    if (setHandle != null && setHandle.length > 0) {
1898:                        currentList = setHandle[0];
1899:                        setHandle[0] = null;
1900:                    }
1901:                    setHandle = new List[1];
1902:                    setHandle[0] = currentList;
1903:
1904:                    relationSet = null;
1905:                    changed = false;
1906:                }
1907:
1908:                public boolean isChanged() {
1909:                    return changed;
1910:                }
1911:            }
1912:
1913:            private final static class CMRJDBCType implements  JDBCType {
1914:                private final String[] columnNames;
1915:                private final Class[] javaTypes;
1916:                private final int[] jdbcTypes;
1917:                private final String[] sqlTypes;
1918:                private final boolean[] notNull;
1919:
1920:                private CMRJDBCType(List fields) {
1921:                    List columnNamesList = new ArrayList();
1922:                    List javaTypesList = new ArrayList();
1923:                    List jdbcTypesList = new ArrayList();
1924:                    List sqlTypesList = new ArrayList();
1925:                    List notNullList = new ArrayList();
1926:
1927:                    for (Iterator iter = fields.iterator(); iter.hasNext();) {
1928:                        JDBCCMPFieldBridge field = (JDBCCMPFieldBridge) iter
1929:                                .next();
1930:                        JDBCType type = field.getJDBCType();
1931:                        for (int i = 0; i < type.getColumnNames().length; i++) {
1932:                            columnNamesList.add(type.getColumnNames()[i]);
1933:                            javaTypesList.add(type.getJavaTypes()[i]);
1934:                            jdbcTypesList.add(new Integer(
1935:                                    type.getJDBCTypes()[i]));
1936:                            sqlTypesList.add(type.getSQLTypes()[i]);
1937:                            notNullList.add(new Boolean(type.getNotNull()[i]));
1938:                        }
1939:                    }
1940:                    columnNames = (String[]) columnNamesList
1941:                            .toArray(new String[columnNamesList.size()]);
1942:                    javaTypes = (Class[]) javaTypesList
1943:                            .toArray(new Class[javaTypesList.size()]);
1944:                    sqlTypes = (String[]) sqlTypesList
1945:                            .toArray(new String[sqlTypesList.size()]);
1946:
1947:                    jdbcTypes = new int[jdbcTypesList.size()];
1948:                    for (int i = 0; i < jdbcTypes.length; i++) {
1949:                        jdbcTypes[i] = ((Integer) jdbcTypesList.get(i))
1950:                                .intValue();
1951:                    }
1952:
1953:                    notNull = new boolean[notNullList.size()];
1954:                    for (int i = 0; i < notNull.length; i++) {
1955:                        notNull[i] = ((Boolean) notNullList.get(i))
1956:                                .booleanValue();
1957:                    }
1958:                }
1959:
1960:                public String[] getColumnNames() {
1961:                    return columnNames;
1962:                }
1963:
1964:                public Class[] getJavaTypes() {
1965:                    return javaTypes;
1966:                }
1967:
1968:                public int[] getJDBCTypes() {
1969:                    return jdbcTypes;
1970:                }
1971:
1972:                public String[] getSQLTypes() {
1973:                    return sqlTypes;
1974:                }
1975:
1976:                public boolean[] getNotNull() {
1977:                    return notNull;
1978:                }
1979:
1980:                public boolean[] getAutoIncrement() {
1981:                    return new boolean[] { false };
1982:                }
1983:
1984:                public Object getColumnValue(int index, Object value) {
1985:                    throw new UnsupportedOperationException();
1986:                }
1987:
1988:                public Object setColumnValue(int index, Object value,
1989:                        Object columnValue) {
1990:                    throw new UnsupportedOperationException();
1991:                }
1992:
1993:                public boolean hasMapper() {
1994:                    throw new UnsupportedOperationException(
1995:                            "hasMapper is not implemented.");
1996:                }
1997:
1998:                public boolean isSearchable() {
1999:                    throw new UnsupportedOperationException(
2000:                            "isSearchable is not implemented.");
2001:                }
2002:
2003:                public JDBCResultSetReader[] getResultSetReaders() {
2004:                    // foreign key fields has their result set readers
2005:                    throw new UnsupportedOperationException();
2006:                }
2007:
2008:                public JDBCParameterSetter[] getParameterSetter() {
2009:                    throw new UnsupportedOperationException();
2010:                }
2011:            }
2012:
2013:            private final static class TxSynchronization implements 
2014:                    Synchronization {
2015:                private final WeakReference fieldStateRef;
2016:
2017:                private TxSynchronization(FieldState fieldState) {
2018:                    if (fieldState == null) {
2019:                        throw new IllegalArgumentException("fieldState is null");
2020:                    }
2021:                    this .fieldStateRef = new WeakReference(fieldState);
2022:                }
2023:
2024:                public void beforeCompletion() {
2025:                    // REVIEW: THIS WILL NOT BE INVOKED ON A ROLLBACK
2026:                    // Be Careful where you put this invalidate
2027:                    // If you put it in afterCompletion, the beanlock will probably
2028:                    // be released before the invalidate and you will have a race
2029:                    FieldState fieldState = (FieldState) fieldStateRef.get();
2030:                    if (fieldState != null) {
2031:                        fieldState.invalidate();
2032:                    }
2033:                }
2034:
2035:                public void afterCompletion(int status) {
2036:                }
2037:            }
2038:
2039:            public static interface RelationDataManager {
2040:                void addRelation(JDBCCMRFieldBridge field, Object id,
2041:                        JDBCCMRFieldBridge relatedField, Object relatedId);
2042:
2043:                void removeRelation(JDBCCMRFieldBridge field, Object id,
2044:                        JDBCCMRFieldBridge relatedField, Object relatedId);
2045:
2046:                boolean isDirty();
2047:
2048:                RelationData getRelationData();
2049:            }
2050:
2051:            private static final RelationDataManager EMPTY_RELATION_MANAGER = new RelationDataManager() {
2052:                public void addRelation(JDBCCMRFieldBridge field, Object id,
2053:                        JDBCCMRFieldBridge relatedField, Object relatedId) {
2054:                }
2055:
2056:                public void removeRelation(JDBCCMRFieldBridge field, Object id,
2057:                        JDBCCMRFieldBridge relatedField, Object relatedId) {
2058:                }
2059:
2060:                public boolean isDirty() {
2061:                    return false;
2062:                }
2063:
2064:                public RelationData getRelationData() {
2065:                    throw new UnsupportedOperationException();
2066:                }
2067:            };
2068:
2069:            public static class M2MRelationManager implements 
2070:                    RelationDataManager {
2071:                private final JDBCCMRFieldBridge leftField;
2072:                private final JDBCCMRFieldBridge rightField;
2073:
2074:                private final TransactionLocal relationData = new TransactionLocal() {
2075:                    protected Object initialValue() {
2076:                        return new RelationData(leftField, rightField);
2077:                    }
2078:                };
2079:
2080:                public M2MRelationManager(JDBCCMRFieldBridge leftField,
2081:                        JDBCCMRFieldBridge rightField) {
2082:                    this .leftField = leftField;
2083:                    this .rightField = rightField;
2084:                }
2085:
2086:                public void addRelation(JDBCCMRFieldBridge field, Object id,
2087:                        JDBCCMRFieldBridge relatedField, Object relatedId) {
2088:                    final RelationData local = getRelationData();
2089:                    local.addRelation(field, id, relatedField, relatedId);
2090:                }
2091:
2092:                public void removeRelation(JDBCCMRFieldBridge field, Object id,
2093:                        JDBCCMRFieldBridge relatedField, Object relatedId) {
2094:                    RelationData local = getRelationData();
2095:                    local.removeRelation(field, id, relatedField, relatedId);
2096:                }
2097:
2098:                public boolean isDirty() {
2099:                    RelationData local = getRelationData();
2100:                    return local.isDirty();
2101:                }
2102:
2103:                public RelationData getRelationData() {
2104:                    final RelationData local = (RelationData) relationData
2105:                            .get();
2106:                    return local;
2107:                }
2108:            }
2109:
2110:            interface SecurityActions {
2111:                class UTIL {
2112:                    static SecurityActions getSecurityActions() {
2113:                        return System.getSecurityManager() == null ? NON_PRIVILEGED
2114:                                : PRIVILEGED;
2115:                    }
2116:                }
2117:
2118:                SecurityActions NON_PRIVILEGED = new SecurityActions() {
2119:                    public Principal getPrincipal() {
2120:                        return SecurityAssociation.getPrincipal();
2121:                    }
2122:
2123:                    public Object getCredential() {
2124:                        return SecurityAssociation.getCredential();
2125:                    }
2126:                };
2127:
2128:                SecurityActions PRIVILEGED = new SecurityActions() {
2129:                    private final PrivilegedAction getPrincipalAction = new PrivilegedAction() {
2130:                        public Object run() {
2131:                            return SecurityAssociation.getPrincipal();
2132:                        }
2133:                    };
2134:
2135:                    private final PrivilegedAction getCredentialAction = new PrivilegedAction() {
2136:                        public Object run() {
2137:                            return SecurityAssociation.getCredential();
2138:                        }
2139:                    };
2140:
2141:                    public Principal getPrincipal() {
2142:                        return (Principal) AccessController
2143:                                .doPrivileged(getPrincipalAction);
2144:                    }
2145:
2146:                    public Object getCredential() {
2147:                        return AccessController
2148:                                .doPrivileged(getCredentialAction);
2149:                    }
2150:                };
2151:
2152:                Principal getPrincipal();
2153:
2154:                Object getCredential();
2155:            }
2156:        }
w___w___w___._j___a_v__a2_s__.c___o___m___ | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.