| 1 22 package org.jboss.ejb.plugins.cmp.jdbc.bridge; 23 24 import java.lang.ref.WeakReference ; 25 import java.lang.reflect.Method ; 26 import javax.sql.DataSource ; 27 import java.sql.PreparedStatement ; 28 import java.sql.ResultSet ; 29 import java.util.ArrayList ; 30 import java.util.Collection ; 31 import java.util.Collections ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.Map ; 36 import java.util.Set ; 37 import java.util.HashMap ; 38 import java.util.Arrays ; 39 import java.security.AccessController ; 40 import java.security.PrivilegedAction ; 41 import java.security.Principal ; 42 import java.rmi.RemoteException ; 43 import javax.ejb.EJBException ; 44 import javax.ejb.EJBLocalObject ; 45 import javax.ejb.EJBLocalHome ; 46 import javax.ejb.RemoveException ; 47 import javax.ejb.NoSuchObjectLocalException ; 48 import javax.transaction.Status ; 49 import javax.transaction.Synchronization ; 50 import javax.transaction.SystemException ; 51 import javax.transaction.Transaction ; 52 import javax.transaction.TransactionManager ; 53 import javax.transaction.RollbackException ; 54 55 import org.jboss.deployment.DeploymentException; 56 import org.jboss.ejb.EntityCache; 57 import org.jboss.ejb.EntityContainer; 58 import org.jboss.ejb.EntityEnterpriseContext; 59 import org.jboss.ejb.LocalProxyFactory; 60 import org.jboss.ejb.plugins.cmp.bridge.EntityBridge; 61 import org.jboss.ejb.plugins.cmp.bridge.FieldBridge; 62 import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext; 63 import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager; 64 import org.jboss.ejb.plugins.cmp.jdbc.JDBCType; 65 import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil; 66 import org.jboss.ejb.plugins.cmp.jdbc.CascadeDeleteStrategy; 67 import org.jboss.ejb.plugins.cmp.jdbc.RelationData; 68 import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore; 69 import org.jboss.ejb.plugins.cmp.jdbc.JDBCParameterSetter; 70 import org.jboss.ejb.plugins.cmp.jdbc.JDBCResultSetReader; 71 import org.jboss.tm.TransactionLocal; 72 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData; 73 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData; 74 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData; 75 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData; 76 import org.jboss.ejb.plugins.cmp.ejbql.Catalog; 77 import org.jboss.ejb.plugins.lock.Entrancy; 78 import org.jboss.invocation.InvocationType; 79 import org.jboss.logging.Logger; 80 import org.jboss.security.SecurityAssociation; 81 82 97 public final class JDBCCMRFieldBridge extends JDBCAbstractCMRFieldBridge 98 { 99 102 private final JDBCEntityBridge entity; 103 106 private final JDBCStoreManager manager; 107 110 private final JDBCRelationshipRoleMetaData metadata; 111 114 private DataSource dataSource; 115 118 private String qualifiedTableName; 119 private String tableName; 120 123 private JDBCCMP2xFieldBridge[] tableKeyFields; 124 128 private JDBCType jdbcType; 129 132 private WeakReference relatedContainerRef; 133 136 private JDBCStoreManager relatedManager; 137 140 private JDBCEntityBridge relatedEntity; 141 144 private JDBCCMRFieldBridge relatedCMRField; 145 148 private final Logger log; 149 150 153 private JDBCCMP2xFieldBridge[] foreignKeyFields; 154 157 private boolean allFKFieldsMappedToPKFields; 158 161 private final Map relatedPKFieldsByMyPKFields = new HashMap (); 162 165 private final Map relatedPKFieldsByMyFKFields = new HashMap (); 166 169 private boolean hasFKFieldsMappedToCMPFields; 170 171 private final TransactionLocal relatedPKValuesWaitingForMyPK = new TransactionLocal() 178 { 179 protected Object initialValue() 180 { 181 return new HashMap (); 182 } 183 }; 184 185 188 private Method relatedFindByPrimaryKey; 189 190 193 private final int jdbcContextIndex; 194 195 198 private CascadeDeleteStrategy cascadeDeleteStrategy; 199 200 203 private RelationDataManager relationManager; 204 205 208 public JDBCCMRFieldBridge(JDBCEntityBridge entity, 209 JDBCStoreManager manager, 210 JDBCRelationshipRoleMetaData metadata) 211 throws DeploymentException 212 { 213 this.entity = entity; 214 this.manager = manager; 215 this.metadata = metadata; 216 this.jdbcContextIndex = ((JDBCEntityBridge) manager.getEntityBridge()).getNextJDBCContextIndex(); 217 218 String categoryName = this.getClass().getName() + 220 "." + manager.getMetaData().getName() + "."; 221 if(metadata.getCMRFieldName() != null) 222 { 223 categoryName += metadata.getCMRFieldName(); 224 } 225 else 226 { 227 categoryName += metadata.getRelatedRole().getEntity().getName() + 228 "-" + metadata.getRelatedRole().getCMRFieldName(); 229 } 230 this.log = Logger.getLogger(categoryName); 231 } 232 233 public RelationDataManager getRelationDataManager() 234 { 235 return relationManager; 236 } 237 238 public void resolveRelationship() throws DeploymentException 239 { 240 245 String relatedEntityName = metadata.getRelatedRole().getEntity().getName(); 247 248 Catalog catalog = (Catalog) manager.getApplicationData("CATALOG"); 250 relatedEntity = (JDBCEntityBridge) catalog.getEntityByEJBName(relatedEntityName); 251 if(relatedEntity == null) 252 { 253 throw new DeploymentException("Related entity not found: " + 254 "entity=" + 255 entity.getEntityName() + 256 ", " + 257 "cmrField=" + 258 getFieldName() + 259 ", " + 260 "relatedEntity=" + relatedEntityName); 261 } 262 263 JDBCCMRFieldBridge[] cmrFields = (JDBCCMRFieldBridge[]) relatedEntity.getCMRFields(); 265 for(int i = 0; i < cmrFields.length; ++i) 266 { 267 JDBCCMRFieldBridge cmrField = cmrFields[i]; 268 if(metadata.getRelatedRole() == cmrField.getMetaData()) 269 { 270 relatedCMRField = cmrField; 271 break; 272 } 273 } 274 275 if(relatedCMRField == null) 278 { 279 String message = "Related CMR field not found in " + 280 relatedEntity.getEntityName() + " for relationship from"; 281 282 message += entity.getEntityName() + "."; 283 if(getFieldName() != null) 284 { 285 message += getFieldName(); 286 } 287 else 288 { 289 message += "<no-field>"; 290 } 291 292 message += " to "; 293 message += relatedEntityName + "."; 294 if(metadata.getRelatedRole().getCMRFieldName() != null) 295 { 296 message += metadata.getRelatedRole().getCMRFieldName(); 297 } 298 else 299 { 300 message += "<no-field>"; 301 } 302 303 throw new DeploymentException(message); 304 } 305 306 relatedManager = (JDBCStoreManager) relatedEntity.getManager(); 308 309 EntityContainer relatedContainer = relatedManager.getContainer(); 311 this.relatedContainerRef = new WeakReference (relatedContainer); 312 313 Class homeClass = (relatedContainer.getLocalHomeClass() != null ? 315 relatedContainer.getLocalHomeClass() : relatedContainer.getHomeClass()); 316 try 317 { 318 relatedFindByPrimaryKey = 319 homeClass.getMethod("findByPrimaryKey", new Class []{relatedEntity.getPrimaryKeyClass()}); 320 } 321 catch(Exception e) 322 { 323 throw new DeploymentException("findByPrimaryKey(" + 324 relatedEntity.getPrimaryKeyClass().getName() 325 + " pk) was not found in " + homeClass.getName()); 326 } 327 328 if(metadata.getRelationMetaData().isTableMappingStyle()) 332 { 333 Collection tableKeys = metadata.getKeyFields(); 335 List keyFieldsList = new ArrayList (tableKeys.size()); 336 337 Map pkFieldsToFKFields = new HashMap (tableKeys.size()); 339 for(Iterator i = tableKeys.iterator(); i.hasNext();) 340 { 341 JDBCCMPFieldMetaData cmpFieldMetaData = (JDBCCMPFieldMetaData) i.next(); 342 FieldBridge pkField = entity.getFieldByName(cmpFieldMetaData.getFieldName()); 343 if(pkField == null) 344 { 345 throw new DeploymentException("Primary key not found for key-field " + cmpFieldMetaData.getFieldName()); 346 } 347 pkFieldsToFKFields.put(pkField, new JDBCCMP2xFieldBridge(manager, cmpFieldMetaData)); 348 } 349 JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields(); 351 for(int i = 0; i < pkFields.length; ++i) 352 { 353 Object fkField = pkFieldsToFKFields.get(pkFields[i]); 354 if(fkField == null) 355 { 356 throw new DeploymentException("Primary key " + pkFields[i].getFieldName() + " is not mapped."); 357 } 358 keyFieldsList.add(fkField); 359 } 360 tableKeyFields = (JDBCCMP2xFieldBridge[]) keyFieldsList.toArray( 361 new JDBCCMP2xFieldBridge[keyFieldsList.size()]); 362 363 dataSource = metadata.getRelationMetaData().getDataSource(); 364 } 365 else 366 { 367 initializeForeignKeyFields(); 368 dataSource = hasForeignKey() ? entity.getDataSource() : relatedEntity.getDataSource(); 369 } 370 371 qualifiedTableName = SQLUtil.fixTableName(metadata.getRelationMetaData().getDefaultTableName(), dataSource); 376 tableName = SQLUtil.getTableNameWithoutSchema(qualifiedTableName); 377 378 relationManager = relatedCMRField.initRelationManager(this); 379 } 380 381 386 public void start() throws DeploymentException 387 { 388 cascadeDeleteStrategy = CascadeDeleteStrategy.getCascadeDeleteStrategy(this); 389 } 390 391 public boolean removeFromRelations(EntityEnterpriseContext ctx, Object [] oldRelationsRef) 392 { 393 load(ctx); 394 395 FieldState fieldState = getFieldState(ctx); 396 List value = fieldState.getValue(); 397 398 boolean removed = false; 399 if(!value.isEmpty()) 400 { 401 if(hasFKFieldsMappedToCMPFields) 402 { 403 if(isForeignKeyValid(value.get(0))) 404 { 405 cascadeDeleteStrategy.removedIds(ctx, oldRelationsRef, value); 406 removed = true; 407 } 408 } 409 else 410 { 411 cascadeDeleteStrategy.removedIds(ctx, oldRelationsRef, value); 412 removed = true; 413 } 414 } 415 return removed; 416 } 417 418 public void cascadeDelete(EntityEnterpriseContext ctx, List oldValues) 419 throws RemoveException , RemoteException  420 { 421 cascadeDeleteStrategy.cascadeDelete(ctx, oldValues); 422 } 423 424 public boolean isBatchCascadeDelete() 425 { 426 return (cascadeDeleteStrategy instanceof CascadeDeleteStrategy.BatchCascadeDeleteStrategy); 427 } 428 429 432 public JDBCStoreManager getJDBCStoreManager() 433 { 434 return manager; 435 } 436 437 440 public JDBCAbstractEntityBridge getEntity() 441 { 442 return entity; 443 } 444 445 448 public JDBCRelationshipRoleMetaData getMetaData() 449 { 450 return metadata; 451 } 452 453 456 public JDBCRelationMetaData getRelationMetaData() 457 { 458 return metadata.getRelationMetaData(); 459 } 460 461 464 public String getFieldName() 465 { 466 return metadata.getCMRFieldName(); 467 } 468 469 472 public String getQualifiedTableName() 473 { 474 return qualifiedTableName; 475 } 476 477 public String getTableName() 478 { 479 return tableName; 480 } 481 482 485 public DataSource getDataSource() 486 { 487 return dataSource; 488 } 489 490 493 public JDBCReadAheadMetaData getReadAhead() 494 { 495 return metadata.getReadAhead(); 496 } 497 498 public JDBCType getJDBCType() 499 { 500 return jdbcType; 501 } 502 503 public boolean isPrimaryKeyMember() 504 { 505 return false; 506 } 507 508 511 public boolean hasForeignKey() 512 { 513 return foreignKeyFields != null; 514 } 515 516 519 public boolean allFkFieldsMappedToPkFields() 520 { 521 return allFKFieldsMappedToPKFields; 522 } 523 524 527 public boolean isCollectionValued() 528 { 529 return metadata.getRelatedRole().isMultiplicityMany(); 530 } 531 532 535 public boolean isSingleValued() 536 { 537 return metadata.getRelatedRole().isMultiplicityOne(); 538 } 539 540 543 public JDBCFieldBridge[] getTableKeyFields() 544 { 545 return tableKeyFields; 546 } 547 548 551 public JDBCFieldBridge[] getForeignKeyFields() 552 { 553 return foreignKeyFields; 554 } 555 556 559 public JDBCAbstractCMRFieldBridge getRelatedCMRField() 560 { 561 return relatedCMRField; 562 } 563 564 567 public JDBCStoreManager getRelatedManager() 568 { 569 return relatedManager; 570 } 571 572 575 public EntityBridge getRelatedEntity() 576 { 577 return relatedEntity; 578 } 579 580 583 public JDBCEntityBridge getRelatedJDBCEntity() 584 { 585 return relatedEntity; 586 } 587 588 591 private final EntityContainer getRelatedContainer() 592 { 593 return (EntityContainer) relatedContainerRef.get(); 594 } 595 596 599 public final Class getRelatedLocalInterface() 600 { 601 return getRelatedContainer().getLocalClass(); 602 } 603 604 607 public final LocalProxyFactory getRelatedInvoker() 608 { 609 return getRelatedContainer().getLocalProxyFactory(); 610 } 611 612 616 public boolean isLoaded(EntityEnterpriseContext ctx) 617 { 618 return getFieldState(ctx).isLoaded; 619 } 620 621 627 public void addRelatedPKsWaitedForMe(EntityEnterpriseContext ctx) 628 { 629 final Map relatedPKsMap = getRelatedPKsWaitingForMyPK(); 630 synchronized(relatedPKsMap) 631 { 632 List relatedPKsWaitingForMe = (List ) relatedPKsMap.get(ctx.getId()); 633 if(relatedPKsWaitingForMe != null) 634 { 635 for(Iterator waitingPKsIter = relatedPKsWaitingForMe.iterator(); waitingPKsIter.hasNext();) 636 { 637 Object waitingPK = waitingPKsIter.next(); 638 waitingPKsIter.remove(); 639 if(isForeignKeyValid(waitingPK)) 640 { 641 createRelationLinks(ctx, waitingPK); 642 } 643 } 644 } 645 } 646 } 647 648 651 public boolean isReadOnly() 652 { 653 return getRelationMetaData().isReadOnly(); 654 } 655 656 659 public boolean isReadTimedOut(EntityEnterpriseContext ctx) 660 { 661 if(!isReadOnly()) 663 { 664 return true; 665 } 666 667 if(getRelationMetaData().getReadTimeOut() == -1) 669 { 670 return false; 671 } 672 673 long readInterval = System.currentTimeMillis() - getFieldState(ctx).getLastRead(); 674 return readInterval > getRelationMetaData().getReadTimeOut(); 675 } 676 677 681 public Object getValue(EntityEnterpriseContext ctx) 682 { 683 return getInstanceValue(ctx); 685 } 686 687 693 public void setValue(EntityEnterpriseContext ctx, Object value) 694 { 695 if(isReadOnly()) 696 { 697 throw new EJBException ("Field is read-only: fieldName=" + getFieldName()); 698 } 699 700 if(!JDBCEntityBridge.isEjbCreateDone(ctx)) 701 { 702 throw new IllegalStateException ("A CMR field cannot be set " + 703 "in ejbCreate; this should be done in the ejbPostCreate " + 704 "method instead [EJB 2.0 Spec. 10.5.2]."); 705 } 706 707 if(isCollectionValued() && value == null) 708 { 709 throw new IllegalArgumentException ("null cannot be assigned to a " + 710 "collection-valued cmr-field [EJB 2.0 Spec. 10.3.8]."); 711 } 712 722 723 setInstanceValue(ctx, value); 724 } 725 726 730 public Object getInstanceValue(EntityEnterpriseContext myCtx) 731 { 732 load(myCtx); 733 734 FieldState fieldState = getFieldState(myCtx); 735 if(isCollectionValued()) 736 { 737 return fieldState.getRelationSet(); 738 } 739 740 try 742 { 743 List value = fieldState.getValue(); 744 if(!value.isEmpty()) 745 { 746 Object fk = value.get(0); 747 return getRelatedEntityByFK(fk); 748 } 749 else if(foreignKeyFields != null) 750 { 751 Object relatedId = getRelatedIdFromContext(myCtx); 753 if(relatedId != null) 754 { 755 return getRelatedEntityByFK(relatedId); 756 } 757 } 758 return null; 759 } 760 catch(EJBException e) 761 { 762 throw e; 763 } 764 catch(Exception e) 765 { 766 throw new EJBException (e); 767 } 768 } 769 770 780 public EJBLocalObject getRelatedEntityByFK(Object fk) 781 { 782 EJBLocalObject relatedLocalObject = null; 783 final EntityContainer relatedContainer = getRelatedContainer(); 784 785 if(hasFKFieldsMappedToCMPFields 786 && relatedManager.getReadAheadCache().getPreloadDataMap(fk, false) == null ) 788 { 789 EJBLocalHome relatedHome = relatedContainer.getLocalProxyFactory().getEJBLocalHome(); 790 try 791 { 792 relatedLocalObject = (EJBLocalObject ) relatedFindByPrimaryKey.invoke(relatedHome, new Object []{fk}); 793 } 794 catch(Exception ignore) 795 { 796 } 798 } 799 else 800 { 801 relatedLocalObject = relatedContainer.getLocalProxyFactory().getEntityEJBLocalObject(fk); 802 } 803 804 return relatedLocalObject; 805 } 806 807 814 public boolean isForeignKeyValid(Object fk) 815 { 816 boolean valid; 817 if(relatedManager.getReadAheadCache().getPreloadDataMap(fk, false) != null) 818 { 819 valid = true; 820 } 821 else 822 { 823 EJBLocalHome relatedHome = getRelatedContainer().getLocalProxyFactory().getEJBLocalHome(); 824 try 825 { 826 relatedFindByPrimaryKey.invoke(relatedHome, new Object []{fk}); 827 valid = true; 828 } 829 catch(Exception ignore) 830 { 831 valid = false; 833 } 834 } 835 return valid; 836 } 837 838 842 public void setInstanceValue(EntityEnterpriseContext myCtx, Object newValue) 843 { 844 List newPks; 846 if(newValue instanceof Collection ) 847 { 848 Collection col = (Collection ) newValue; 849 if(!col.isEmpty()) 850 { 851 newPks = new ArrayList (col.size()); 852 for(Iterator iter = col.iterator(); iter.hasNext();) 853 { 854 Object localObject = iter.next(); 855 if(localObject != null) 856 { 857 Object relatedId = getRelatedPrimaryKey(localObject); 858 859 if(relatedPKFieldsByMyPKFields.size() > 0) 861 { 862 checkSetForeignKey(myCtx, relatedId); 863 } 864 865 newPks.add(relatedId); 866 } 867 } 868 } 869 else 870 { 871 newPks = Collections.EMPTY_LIST; 872 } 873 } 874 else 875 { 876 if(newValue != null) 877 { 878 newPks = Collections.singletonList(getRelatedPrimaryKey(newValue)); 879 } 880 else 881 { 882 newPks = Collections.EMPTY_LIST; 883 } 884 } 885 886 load(myCtx); 888 FieldState fieldState = getFieldState(myCtx); 889 890 if(newValue == fieldState.getRelationSet()) 892 |