1 package org.apache.ojb.broker.accesslayer; 2 3 17 18 import java.lang.ref.WeakReference ; 19 import java.sql.ResultSet ; 20 import java.sql.SQLException ; 21 import java.util.Collection ; 22 import java.util.HashMap ; 23 import java.util.List ; 24 import java.util.Map ; 25 import java.util.NoSuchElementException ; 26 import java.util.Vector ; 27 28 import org.apache.ojb.broker.Identity; 29 import org.apache.ojb.broker.OJBRuntimeException; 30 import org.apache.ojb.broker.PBLifeCycleEvent; 31 import org.apache.ojb.broker.PBStateEvent; 32 import org.apache.ojb.broker.PBStateListener; 33 import org.apache.ojb.broker.PersistenceBrokerException; 34 import org.apache.ojb.broker.PersistenceBrokerInternal; 35 import org.apache.ojb.broker.PersistenceBrokerSQLException; 36 import org.apache.ojb.broker.cache.MaterializationCache; 37 import org.apache.ojb.broker.cache.ObjectCacheInternal; 38 import org.apache.ojb.broker.core.PersistenceBrokerImpl; 39 import org.apache.ojb.broker.metadata.ClassDescriptor; 40 import org.apache.ojb.broker.metadata.DescriptorRepository; 41 import org.apache.ojb.broker.metadata.FieldDescriptor; 42 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor; 43 import org.apache.ojb.broker.query.Query; 44 import org.apache.ojb.broker.query.QueryBySQL; 45 import org.apache.ojb.broker.util.logging.Logger; 46 import org.apache.ojb.broker.util.logging.LoggerFactory; 47 48 78 public class RsIterator implements OJBIterator 79 { 80 protected Logger logger = LoggerFactory.getLogger(this.getClass()); 81 private static final String INFO_MSG = "Resources already cleaned up, recommend to set" + 82 " this flag before first use of the iterator"; 83 87 private PBLifeCycleEvent afterLookupEvent; 88 private MaterializationCache m_cache; 89 90 93 private PersistenceBrokerImpl m_broker; 94 95 98 private ResultSetAndStatement m_rsAndStmt; 99 100 103 private RsQueryObject m_queryObject; 104 105 108 private Class m_itemProxyClass; 109 110 113 private Class m_itemTopLevelClass = null; 114 115 118 private Map m_row = null; 119 120 123 private boolean m_hasCalledCheck = false; 124 125 130 private boolean m_inBatchedMode = false; 131 132 135 private boolean hasNext = false; 136 137 private boolean advancedJDBCSupport = false; 138 private boolean JDBCSupportAssessed = false; 139 private int m_current_row = 0; 140 143 private boolean resourcesAreReleased = false; 144 145 149 private boolean autoRelease = true; 150 private ResourceWrapper resourceListener; 151 152 153 private boolean disableLifeCycleEvents = false; 154 155 161 public RsIterator(RsQueryObject queryObject, final PersistenceBrokerImpl broker) 162 { 163 setCache(broker.getInternalCache()); 164 setRow(new HashMap ()); 165 setBroker(broker); 166 setQueryObject(queryObject); 167 168 Class classToPrefetch = broker.getReferenceBroker().getClassToPrefetch(); 169 if ((classToPrefetch != null) && classToPrefetch.isAssignableFrom(queryObject.getClassDescriptor().getClassOfObject())) 170 { 171 setItemProxyClass(null); 172 } 173 else 174 { 175 setItemProxyClass(queryObject.getClassDescriptor().getProxyClass()); 176 } 177 178 183 setAfterLookupEvent(new PBLifeCycleEvent(getBroker(), PBLifeCycleEvent.Type.AFTER_LOOKUP)); 184 185 try 186 { 187 setRsAndStmt(queryObject.performQuery(broker.serviceJdbcAccess())); 188 192 prefetchRelationships(queryObject.getQuery()); 193 if (logger.isDebugEnabled()) 194 { 195 logger.debug("RsIterator[" + queryObject + "] initialized"); 196 } 197 } 198 catch (RuntimeException e) 199 { 200 autoReleaseDbResources(); 201 throw e; 202 } 203 204 209 resourceListener = new ResourceWrapper(this); 210 m_broker.addListener(resourceListener); 211 } 212 213 protected Class getTopLevelClass() 214 { 215 if (m_itemTopLevelClass == null) 216 { 217 m_itemTopLevelClass = getBroker().getTopLevelClass(getQueryObject().getClassDescriptor().getClassOfObject()); 218 } 219 return m_itemTopLevelClass; 220 } 221 222 226 public synchronized boolean hasNext() 227 { 228 try 229 { 230 if (!isHasCalledCheck()) 231 { 232 setHasCalledCheck(true); 233 setHasNext(getRsAndStmt().m_rs.next()); 234 if (!getHasNext()) 235 { 236 autoReleaseDbResources(); 237 } 238 } 239 } 240 catch (Exception ex) 241 { 242 setHasNext(false); 243 autoReleaseDbResources(); 244 if(ex instanceof ResourceClosedException) 245 { 246 throw (ResourceClosedException)ex; 247 } 248 if(ex instanceof SQLException ) 249 { 250 throw new PersistenceBrokerSQLException("Calling ResultSet.next() failed", (SQLException ) ex); 251 } 252 else 253 { 254 throw new PersistenceBrokerException("Can't get next row from ResultSet", ex); 255 } 256 } 257 if (logger.isDebugEnabled()) 258 logger.debug("hasNext() -> " + getHasNext()); 259 260 return getHasNext(); 261 } 262 263 267 public synchronized Object next() throws NoSuchElementException 268 { 269 try 270 { 271 if (!isHasCalledCheck()) 272 { 273 hasNext(); 274 } 275 setHasCalledCheck(false); 276 if (getHasNext()) 277 { 278 Object obj = getObjectFromResultSet(); 279 m_current_row++; 280 281 if (!disableLifeCycleEvents) 284 { 285 getAfterLookupEvent().setTarget(obj); 286 getBroker().fireBrokerEvent(getAfterLookupEvent()); 287 getAfterLookupEvent().setTarget(null); 288 } 289 return obj; 290 } 291 else 292 { 293 throw new NoSuchElementException ("inner hasNext was false"); 294 } 295 } 296 catch (ResourceClosedException ex) 297 { 298 autoReleaseDbResources(); 299 throw ex; 300 } 301 catch (NoSuchElementException ex) 302 { 303 autoReleaseDbResources(); 304 logger.error("Error while iterate ResultSet for query " + m_queryObject, ex); 305 throw new NoSuchElementException ("Could not obtain next object: " + ex.getMessage()); 306 } 307 } 308 309 312 public void remove() 313 { 314 throw new UnsupportedOperationException ("removing not supported by RsIterator"); 315 } 316 317 320 private Collection getOwnerObjects() 321 { 322 Collection owners = new Vector (); 323 while (hasNext()) 324 { 325 owners.add(next()); 326 } 327 return owners; 328 } 329 330 334 private void prefetchRelationships(Query query) 335 { 336 List prefetchedRel; 337 Collection owners; 338 String relName; 339 RelationshipPrefetcher[] prefetchers; 340 341 if (query == null || query.getPrefetchedRelationships() == null || query.getPrefetchedRelationships().isEmpty()) 342 { 343 return; 344 } 345 346 if (!supportsAdvancedJDBCCursorControl()) 347 { 348 logger.info("prefetching relationships requires JDBC level 2.0"); 349 return; 350 } 351 352 setInBatchedMode(true); 354 355 prefetchedRel = query.getPrefetchedRelationships(); 356 prefetchers = new RelationshipPrefetcher[prefetchedRel.size()]; 357 358 for (int i = 0; i < prefetchedRel.size(); i++) 360 { 361 relName = (String ) prefetchedRel.get(i); 362 prefetchers[i] = getBroker().getRelationshipPrefetcherFactory() 363 .createRelationshipPrefetcher(getQueryObject().getClassDescriptor(), relName); 364 prefetchers[i].prepareRelationshipSettings(); 365 } 366 367 owners = getOwnerObjects(); 369 370 for (int i = 0; i < prefetchedRel.size(); i++) 372 { 373 prefetchers[i].prefetchRelationship(owners); 374 } 375 376 for (int i = 0; i < prefetchedRel.size(); i++) 378 { 379 prefetchers[i].restoreRelationshipSettings(); 380 } 381 382 try 383 { 384 getRsAndStmt().m_rs.beforeFirst(); } 386 catch (SQLException e) 387 { 388 logger.error("beforeFirst failed !", e); 389 } 390 391 setInBatchedMode(false); 392 setHasCalledCheck(false); 393 } 394 395 398 protected Identity getIdentityFromResultSet() throws PersistenceBrokerException 399 { 400 FieldDescriptor fld; 402 FieldDescriptor[] pkFields = getQueryObject().getClassDescriptor().getPkFields(); 403 Object [] pkValues = new Object [pkFields.length]; 404 405 for (int i = 0; i < pkFields.length; i++) 406 { 407 fld = pkFields[i]; 408 pkValues[i] = getRow().get(fld.getColumnName()); 409 } 410 411 return getBroker().serviceIdentity().buildIdentity( 413 getQueryObject().getClassDescriptor().getClassOfObject(), getTopLevelClass(), pkValues); 414 } 415 416 422 protected Object getObjectFromResultSet() throws PersistenceBrokerException 423 { 424 getRow().clear(); 425 434 435 RowReader rowReader = getQueryObject().getClassDescriptor().getRowReader(); 436 rowReader.readPkValuesFrom(getRsAndStmt(), getRow()); 439 440 if (getItemProxyClass() != null) 441 { 442 return getProxyFromResultSet(); 444 } 445 else 446 { 447 Identity oid = getIdentityFromResultSet(); 449 Object result; 450 451 result = getCache().lookup(oid); 453 if (result == null) 454 { 455 456 rowReader.readObjectArrayFrom(getRsAndStmt(), getRow()); 458 result = rowReader.readObjectFrom(getRow()); 461 if (result != null) 463 { 464 469 synchronized (result) 470 { 471 getCache().enableMaterializationCache(); 472 try 473 { 474 getCache().doInternalCache(oid, result, ObjectCacheInternal.TYPE_NEW_MATERIALIZED); 475 482 ClassDescriptor cld = getBroker().getClassDescriptor(result.getClass()); 484 final boolean unforced = false; 486 getBroker().getReferenceBroker().retrieveReferences(result, cld, unforced); 488 getBroker().getReferenceBroker().retrieveCollections(result, cld, unforced); 489 getCache().disableMaterializationCache(); 490 } 491 catch(RuntimeException e) 492 { 493 getCache().doLocalClear(); 495 throw e; 496 } 497 } 498 } 499 } 500 else { 502 ClassDescriptor cld = getBroker().getClassDescriptor(result.getClass()); 503 if (cld.isAlwaysRefresh()) 506 { 507 rowReader.readObjectArrayFrom(getRsAndStmt(), getRow()); 509 rowReader.refreshObject(result, getRow()); 510 } 511 getBroker().checkRefreshRelationships(result, oid, cld); 512 } 513 514 return result; 515 } 516 } 517 518 526 protected Object getProxyFromResultSet() throws PersistenceBrokerException 527 { 528 Identity oid = getIdentityFromResultSet(); 530 531 return getBroker().createProxy(getItemProxyClass(), oid); 533 } 534 535 540 private boolean supportsAdvancedJDBCCursorControl() 541 { 542 if (!JDBCSupportAssessed) 543 { 544 if (getConnectionDescriptor().getJdbcLevel() >= 2.0) 545 advancedJDBCSupport = true; 546 JDBCSupportAssessed = true; 547 } 548 return advancedJDBCSupport; 549 } 550 551 556 protected int countedSize() throws PersistenceBrokerException 557 { 558 Query countQuery = getBroker().serviceBrokerHelper().getCountQuery(getQueryObject().getQuery()); 559 ResultSetAndStatement rsStmt; 560 ClassDescriptor cld = getQueryObject().getClassDescriptor(); 561 int count = 0; 562 563 if (countQuery instanceof QueryBySQL) 566 { 567 String countSql = ((QueryBySQL) countQuery).getSql(); 568 rsStmt = getBroker().serviceJdbcAccess().executeSQL(countSql, cld, Query.NOT_SCROLLABLE); 569 } 570 else 571 { 572 rsStmt = getBroker().serviceJdbcAccess().executeQuery(countQuery, cld); 573 } 574 575 try 576 { 577 if (rsStmt.m_rs.next()) 578 { 579 count = rsStmt.m_rs.getInt(1); 580 } 581 } 582 catch (SQLException e) 583 { 584 throw new PersistenceBrokerException(e); 585 } 586 finally 587 { 588 rsStmt.close(); 589 } 590 591 return count; 592 } 593 594 598 public int size() throws PersistenceBrokerException 599 { 600 int retval = 0; boolean forwardOnly = true; 602 try 603 { 604 forwardOnly = getRsAndStmt().m_stmt.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY; 605 } 606 catch (SQLException e) 607 { 608 } 610 if (!supportsAdvancedJDBCCursorControl() 611 || getBroker().serviceConnectionManager().getSupportedPlatform().useCountForResultsetSize() 612 || forwardOnly) 613 { 614 619 if (logger.isDebugEnabled()) 620 logger.debug("Executing count(*) to get size()"); 621 retval = countedSize(); 622 } 623 else 624 { 625 631 int whereIAm; try 633 { 634 if (getRsAndStmt().m_rs != null) 635 { 636 whereIAm = getRsAndStmt().m_rs.getRow(); 637 if (getRsAndStmt().m_rs.last()) 638 { 639 retval = getRsAndStmt().m_rs.getRow(); 640 } 641 else 642 { 643 retval = 0; 644 } 645 if (whereIAm > 0) 647 { 648 getRsAndStmt().m_rs.absolute(whereIAm); 649 } 650 else 651 { 652 getRsAndStmt().m_rs.beforeFirst(); 653 } 654 } 655 } 656 catch (SQLException se) 657 { 658 advancedJDBCSupport = false; 659 } 660 } 661 return retval; 662 } 663 664 667 public int fullSize() throws PersistenceBrokerException 668 { 669 return size(); 670 } 671 672 680 public boolean absolute(int row) throws PersistenceBrokerException 681 { 682 boolean retval; 683 if (supportsAdvancedJDBCCursorControl()) 684 { 685 retval = absoluteAdvanced(row); 686 } 687 else 688 { 689 retval = absoluteBasic(row); 690 } 691 return retval; 692 } 693 694 698 private boolean absoluteBasic(int row) 699 { 700 boolean retval = false; 701 702 if (row > m_current_row) 703 { 704 try 705 { 706 while (m_current_row < row && getRsAndStmt().m_rs.next()) 707 { 708 m_current_row++; 709 } 710 if (m_current_row == row) 711 { 712 retval = true; 713 } 714 else 715 { 716 setHasCalledCheck(true); 717 setHasNext(false); 718 retval = false; 719 autoReleaseDbResources(); 720 } 721 } 722 catch (Exception ex) 723 { 724 setHasCalledCheck(true); 725 setHasNext(false); 726 retval = false; 727 } 728 } 729 else 730 { 731 logger.info("Your driver does not support advanced JDBC Functionality, " + 732 "you cannot call absolute() with a position < current"); 733 } 734 return retval; 735 } 736 737 741 private boolean absoluteAdvanced(int row) 742 { 743 boolean retval = false; 744 745 try 746 { 747 if (getRsAndStmt().m_rs != null) 748 { 749 if (row == 0) 750 { 751 getRsAndStmt().m_rs.beforeFirst(); 752 } 753 else 754 { 755 retval = getRsAndStmt().m_rs.absolute(row); 756 } 757 m_current_row = row; 758 setHasCalledCheck(false); 759 } 760 } 761 catch (SQLException e) 762 { 763 advancedJDBCSupport = false; 764 } 765 return retval; 766 } 767 768 777 public boolean relative(int row) throws PersistenceBrokerException 778 { 779 boolean retval = false; 780 if (supportsAdvancedJDBCCursorControl()) 781 { 782 try 783 { 784 if (getRsAndStmt().m_rs != null) 785 { 786 retval = getRsAndStmt().m_rs.relative(row); 787 m_current_row += row; 788 } 789 } 790 catch (SQLException e) 791 { 792 advancedJDBCSupport = false; 793 } 794 } 795 else 796 { 797 if (row >= 0) 798 { 799 return absolute(m_current_row + row); 800 } 801 else 802 { 803 logger.info("Your driver does not support advanced JDBC Functionality, you cannot call relative() with a negative value"); 804 } 805 } 806 return retval; 807 } 808 809 815 public void releaseDbResources() 816 { 817 release(true); 818 } 819 820 void release(boolean removeResourceListener) 821 { 822 if (!isInBatchedMode()) { 824 if (!this.resourcesAreReleased) 826 { 827 if(removeResourceListener && resourceListener != null) 829 { 830 try 831 { 832 836 m_broker.removeListener(resourceListener); 837 this.resourceListener = null; 838 } 839 catch(Exception e) 840 { 841 logger.error("Error when try to remove RsIterator resource listener", e); 842 } 843 } 844 this.resourcesAreReleased = true; 845 if (m_rsAndStmt != null) 846 { 847 m_rsAndStmt.close(); 848 m_rsAndStmt = null; 849 } 850 } 851 } 852 } 853 854 858 protected void autoReleaseDbResources() 859 { 860 if(autoRelease) 861 { 862 releaseDbResources(); 863 } 864 } 865 866 874 public void setAutoRelease(boolean autoRelease) 875 { 876 882 if(resourcesAreReleased && !autoRelease) 883 { 884 logger.info(INFO_MSG); 885 } 886 this.autoRelease = autoRelease; 887 } 888 889 892 protected DescriptorRepository getDescriptorRepository() 893 { 894 return getBroker().getDescriptorRepository(); 895 } 896 897 protected JdbcConnectionDescriptor getConnectionDescriptor() 898 { 899 return getBroker().serviceConnectionManager().getConnectionDescriptor(); 900 } 901 902 905 protected void finalize() 906 { 907 if (m_rsAndStmt != null) 908 { 909 logger.info("Found unclosed resources while finalize (causer class: " + this.getClass().getName() + ")" + 910 " Do automatic cleanup"); 911 releaseDbResources(); 912 } 913 try 914 { 915 super.finalize(); 916 } 917 catch(Throwable throwable) 918 { 919 throwable.printStackTrace(); 920 } 921 } 922 923 public String toString() 924 { 925 return super.toString(); 926 } 927 928 931 public ClassDescriptor getClassDescriptor() 932 { 933 return getQueryObject().getClassDescriptor(); 934 } 935 936 protected void setBroker(PersistenceBrokerImpl broker) 937 { 938 m_broker = broker; 939 } 940 941 protected PersistenceBrokerInternal getBroker() 942 { 943 return m_broker; 944 } 945 946 protected void setRsAndStmt(ResultSetAndStatement rsAndStmt) 947 { 948 if(m_rsAndStmt != null) 949 { 950 throw new ResourceNotClosedException("Unclosed resources found, please release resources" + 951 " before set new ones"); 952 } 953 resourcesAreReleased = false; 954 m_rsAndStmt = rsAndStmt; 955 } 956 957 protected ResultSetAndStatement getRsAndStmt() 958 { 959 if(resourcesAreReleased) 960 { 961 throw new ResourceClosedException("Resources no longer reachable, RsIterator will be automatic" + 962 " cleaned up on PB.close/.commitTransaction/.abortTransaction"); 963 } 964 return m_rsAndStmt; 965 } 966 967 protected void setQueryObject(RsQueryObject queryObject) 968 { 969 this.m_queryObject = queryObject; 970 } 971 972 protected RsQueryObject getQueryObject() 973 { 974 return m_queryObject; 975 } 976 977 protected void setItemProxyClass(Class itemProxyClass) 978 { 979 this.m_itemProxyClass = itemProxyClass; 980 } 981 982 protected Class getItemProxyClass() 983 { 984 return m_itemProxyClass; 985 } 986 987 protected void setRow(Map row) 988 { 989 m_row = row; 990 } 991 992 protected Map getRow() 993 { 994 return m_row; 995 } 996 997 protected void setCache(MaterializationCache cache) 998 { 999 this.m_cache = cache; 1000 } 1001 1002 protected MaterializationCache getCache() 1003 { 1004 return m_cache; 1005 } 1006 1007 protected void setAfterLookupEvent(PBLifeCycleEvent afterLookupEvent) 1008 { 1009 this.afterLookupEvent = afterLookupEvent; 1010 } 1011 1012 protected PBLifeCycleEvent getAfterLookupEvent() 1013 { 1014 return afterLookupEvent; 1015 } 1016 1017 protected void setHasCalledCheck(boolean hasCalledCheck) 1018 { 1019 this.m_hasCalledCheck = hasCalledCheck; 1020 } 1021 1022 protected boolean isHasCalledCheck() 1023 { 1024 return m_hasCalledCheck; 1025 } 1026 1027 protected void setHasNext(boolean hasNext) 1028 { 1029 this.hasNext = hasNext; 1030 } 1031 1032 protected boolean getHasNext() 1033 { 1034 return hasNext; 1035 } 1036 1037 protected void setInBatchedMode(boolean inBatchedMode) 1038 { 1039 this.m_inBatchedMode = inBatchedMode; 1040 } 1041 1042 protected boolean isInBatchedMode() 1043 { 1044 return m_inBatchedMode; 1045 } 1046 1047 1053 public static class ResourceWrapper implements PBStateListener 1054 { 1055 1063 WeakReference ref; 1064 1065 public ResourceWrapper(RsIterator rs) 1066 { 1067 ref = new WeakReference (rs); 1068 } 1069 1070 public void beforeClose(PBStateEvent event) 1071 { 1072 if(ref != null) 1073 { 1074 RsIterator rs = (RsIterator) ref.get(); 1075 if(rs != null) rs.release(false); 1076 ref = null; 1077 } 1078 } 1079 1080 public void beforeRollback(PBStateEvent event) 1081 { 1082 if(ref != null) 1083 { 1084 RsIterator rs = (RsIterator) ref.get(); 1085 if(rs != null) rs.release(false); 1086 ref = null; 1087 } 1088 } 1089 1090 public void beforeCommit(PBStateEvent event) 1091 { 1092 if(ref != null) 1093 { 1094 RsIterator rs = (RsIterator) ref.get(); 1095 if(rs != null) rs.release(false); 1096 ref = null; 1097 } 1098 } 1099 1100 public void afterCommit(PBStateEvent event) 1101 { 1102 } 1104 public void afterRollback(PBStateEvent event) 1105 { 1106 } 1108 public void afterBegin(PBStateEvent event) 1109 { 1110 } 1112 public void beforeBegin(PBStateEvent event) 1113 { 1114 } 1116 public void afterOpen(PBStateEvent event) 1117 { 1118 } 1120 } 1121 1122 public static class ResourceClosedException extends OJBRuntimeException 1123 { 1124 public ResourceClosedException(String msg) 1125 { 1126 super(msg); 1127 } 1128 1129 public ResourceClosedException(String msg, Throwable cause) 1130 { 1131 super(msg, cause); 1132 } 1133 } 1134 1135 public static class ResourceNotClosedException extends OJBRuntimeException 1136 { 1137 public ResourceNotClosedException(String msg) 1138 { 1139 super(msg); 1140 } 1141 1142 public ResourceNotClosedException(String msg, Throwable cause) 1143 { 1144 super(msg, cause); 1145 } 1146 } 1147 1148 1151 public void disableLifeCycleEvents() 1152 { 1153 disableLifeCycleEvents = true; 1154 } 1155} 1156 | Popular Tags |