1 10 11 package com.triactive.jdo.store; 12 13 import com.triactive.jdo.ClassNotPersistenceCapableException; 14 import com.triactive.jdo.PersistenceManager; 15 import com.triactive.jdo.PersistenceManagerFactoryImpl; 16 import com.triactive.jdo.SchemaManager; 17 import com.triactive.jdo.StateManager; 18 import com.triactive.jdo.model.ClassMetaData; 19 import com.triactive.jdo.model.FieldMetaData; 20 import com.triactive.jdo.model.MetaData; 21 import com.triactive.jdo.util.MacroString; 22 import com.triactive.jdo.util.SoftValueMap; 23 import java.sql.Connection ; 24 import java.sql.DatabaseMetaData ; 25 import java.sql.ResultSet ; 26 import java.sql.SQLException ; 27 import java.sql.SQLWarning ; 28 import java.sql.Statement ; 29 import java.util.ArrayList ; 30 import java.util.Collections ; 31 import java.util.HashMap ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.ListIterator ; 36 import java.util.Map ; 37 import javax.jdo.Extent; 38 import javax.jdo.JDODataStoreException; 39 import javax.jdo.JDOException; 40 import javax.jdo.JDOFatalException; 41 import javax.jdo.JDOUserException; 42 import javax.sql.DataSource ; 43 import org.apache.log4j.Category; 44 45 46 71 72 public class StoreManager implements SchemaManager 73 { 74 private static final Category LOG = Category.getInstance(StoreManager.class); 75 76 80 private static final int COLUMN_INFO_EXPIRATION_MS = 5 * 60 * 1000; 81 82 private final DataSource ds; 83 private final String userName; 84 private final String password; 85 private final DatabaseAdapter dba; 86 private final int tableValidationFlags; 87 private final int constraintValidationFlags; 88 private final String schemaName; 89 90 95 private ArrayList allTables = new ArrayList (); 96 private ArrayList tablesByTableID = new ArrayList (); 97 private HashMap tablesByName = new HashMap (); 98 private HashMap tablesByJavaID = new HashMap (); 99 private SchemaTable schemaTable = null; 100 private Map columnInfoByTableName = new HashMap (); 101 private long columnInfoReadTimestamp = -1; 102 103 107 private Map requestsByID = Collections.synchronizedMap(new SoftValueMap()); 108 109 124 private ClassAdder classAdder = null; 125 126 127 154 155 StoreManager(PersistenceManagerFactoryImpl pmf, String userName, String password) 156 { 157 this.ds = pmf.getNontransactionalDataSource(); 158 this.userName = userName; 159 this.password = password; 160 161 int validateTables = pmf.getValidateTables() ? Table.VALIDATE : 0; 162 int validateConstraints = pmf.getValidateConstraints() ? Table.VALIDATE : 0; 163 int autoCreate = pmf.getAutoCreateTables() ? Table.AUTO_CREATE : 0; 164 165 tableValidationFlags = validateTables | autoCreate; 166 constraintValidationFlags = validateConstraints | autoCreate; 167 168 try 169 { 170 Connection conn; 171 172 if (userName == null) 173 conn = ds.getConnection(); 174 else 175 conn = ds.getConnection(userName, password); 176 177 try 178 { 179 dba = DatabaseAdapter.getInstance(conn); 180 String name; 181 182 try 183 { 184 name = dba.getSchemaName(conn); 185 } 186 catch (UnsupportedOperationException e) 187 { 188 194 ProbeTable pt = new ProbeTable(this); 195 pt.initialize(); 196 pt.create(conn); 197 198 try 199 { 200 name = pt.findSchemaName(conn); 201 } 202 finally 203 { 204 pt.drop(conn); 205 } 206 } 207 208 schemaName = name; 209 } 210 finally 211 { 212 conn.close(); 213 } 214 } 215 catch (SQLException e) 216 { 217 throw new JDODataStoreException("Failed initializing database", e); 218 } 219 } 220 221 222 226 227 public synchronized void reset() 228 { 229 allTables.clear(); 230 tablesByTableID.clear(); 231 tablesByName.clear(); 232 tablesByJavaID.clear(); 233 schemaTable = null; 234 235 columnInfoByTableName.clear(); 236 columnInfoReadTimestamp = -1; 237 238 requestsByID.clear(); 239 } 240 241 242 249 250 private void checkSchemaInitialized() 251 { 252 254 synchronized (this) 255 { 256 if (schemaTable != null) 257 return; 258 } 259 260 MgmtTransaction mtx = new MgmtTransaction(Connection.TRANSACTION_SERIALIZABLE) 261 { 262 public String toString() 263 { 264 return "Initialize schema table for " + schemaName; 265 } 266 267 protected void execute(Connection conn) throws SQLException 268 { 269 initializeSchemaTable(true, conn); 270 } 271 }; 272 273 mtx.execute(); 274 } 275 276 277 292 private synchronized boolean initializeSchemaTable(boolean validate, Connection conn) throws SQLException 293 { 294 if (schemaTable != null) 295 return true; 296 297 try 298 { 299 SchemaTable st = new SchemaTable(this); 300 st.initialize(); 301 302 if (validate || st.exists(conn)) 303 { 304 st.validate(tableValidationFlags, conn); 305 306 LOG.info("Schema initialized: " + schemaName); 307 308 schemaTable = st; 309 return true; 310 } 311 else 312 return false; 313 } 314 finally 315 { 316 if (schemaTable == null) 317 reset(); 318 } 319 } 320 321 322 327 328 public DatabaseAdapter getDatabaseAdapter() 329 { 330 return dba; 331 } 332 333 334 public String getSchemaName() 335 { 336 return schemaName; 337 } 338 339 340 354 355 public void logSQLWarnings(SQLWarning warning) 356 { 357 while (warning != null) 358 { 359 LOG.warn("SQL warning: " + warning); 360 361 warning = warning.getNextWarning(); 362 } 363 } 364 365 366 public void logSQLWarnings(Connection conn) 367 { 368 try 369 { 370 logSQLWarnings(conn.getWarnings()); 371 } 372 catch (SQLException e) 373 { 374 throw dba.newDataStoreException("Error obtaining warnings from connection " + conn, e); 375 } 376 } 377 378 379 public void logSQLWarnings(Statement stmt) 380 { 381 try 382 { 383 logSQLWarnings(stmt.getWarnings()); 384 } 385 catch (SQLException e) 386 { 387 throw dba.newDataStoreException("Error obtaining warnings from statement " + stmt, e); 388 } 389 } 390 391 392 public void logSQLWarnings(ResultSet rs) 393 { 394 try 395 { 396 logSQLWarnings(rs.getWarnings()); 397 } 398 catch (SQLException e) 399 { 400 throw dba.newDataStoreException("Error obtaining warnings from result set " + rs, e); 401 } 402 } 403 404 405 417 418 public void addClasses(Class [] classes) 419 { 420 checkSchemaInitialized(); 421 422 synchronized (this) 423 { 424 if (classAdder != null) 425 { 426 430 classAdder.addClasses(classes); 431 return; 432 } 433 } 434 435 new ClassAdder(classes).execute(); 436 } 437 438 439 446 447 synchronized SetTable newSetTable(ClassBaseTable cbt, FieldMetaData fmd) 448 { 449 if (classAdder == null) 450 throw new IllegalStateException ("SetTables can only be created as a side effect of adding a new class"); 451 452 return classAdder.newSetTable(cbt, fmd); 453 } 454 455 456 463 464 synchronized MapTable newMapTable(ClassBaseTable cbt, FieldMetaData fmd) 465 { 466 if (classAdder == null) 467 throw new IllegalStateException ("MapTables can only be created as a side effect of adding a new class"); 468 469 return classAdder.newMapTable(cbt, fmd); 470 } 471 472 473 public void dropTablesFor(final Class [] classes) 474 { 475 MgmtTransaction mtx = new MgmtTransaction(Connection.TRANSACTION_READ_COMMITTED) 476 { 477 public String toString() 478 { 479 return "Drop tables for selected classes from schema " + schemaName; 480 } 481 482 protected void execute(Connection conn) throws SQLException 483 { 484 synchronized (StoreManager.this) 485 { 486 try 487 { 488 if (initializeSchemaTable(false, conn)) 489 schemaTable.dropTablesFor(classes, conn); 490 } 491 finally 492 { 493 reset(); 494 } 495 } 496 } 497 }; 498 499 mtx.execute(); 500 } 501 502 503 public void dropAllTables() 504 { 505 MgmtTransaction mtx = new MgmtTransaction(Connection.TRANSACTION_READ_COMMITTED) 506 { 507 public String toString() 508 { 509 return "Drop all tables from schema " + schemaName; 510 } 511 512 protected void execute(Connection conn) throws SQLException 513 { 514 synchronized (StoreManager.this) 515 { 516 try 517 { 518 if (initializeSchemaTable(false, conn)) 519 { 520 schemaTable.dropAllTables(conn); 521 schemaTable.drop(conn); 522 } 523 } 524 finally 525 { 526 reset(); 527 } 528 } 529 } 530 }; 531 532 mtx.execute(); 533 } 534 535 536 545 546 public synchronized JDOTable getTable(SQLIdentifier name) 547 { 548 return (JDOTable)tablesByName.get(name); 549 } 550 551 552 562 563 public synchronized JDOTable getTable(int tableID) 564 { 565 if (tableID < 0 || tableID >= tablesByTableID.size()) 566 return null; 567 else 568 return (JDOTable)tablesByTableID.get(tableID); 569 } 570 571 572 581 582 synchronized JDOTable getTable(MetaData md) 583 { 584 return (JDOTable)tablesByJavaID.get(md.getJavaName()); 585 } 586 587 588 600 601 public ClassTable getTable(Class c) 602 { 603 ClassTable ct; 604 605 synchronized (this) 606 { 607 ct = (ClassTable)tablesByJavaID.get(c.getName()); 608 } 609 610 if (ct == null) 611 { 612 addClasses(new Class [] { c }); 613 614 615 synchronized (this) 616 { 617 ct = (ClassTable)tablesByJavaID.get(c.getName()); 618 } 619 620 if (ct == null) 621 throw new NoExtentException(c); 622 } 623 624 return ct; 625 } 626 627 628 645 646 public ClassBaseTable getClassBaseTable(Class c) 647 { 648 ClassTable t = getTable(c); 649 650 if (!(t instanceof ClassBaseTable)) 651 throw new ViewNotSupportedException(c); 653 return (ClassBaseTable)t; 654 } 655 656 657 669 670 public String getJavaName(final int tableID) 671 { 672 JDOTable table = getTable(tableID); 673 674 if (table != null) 675 return table.getJavaName(); 676 else 677 { 678 checkSchemaInitialized(); 679 680 final String s[] = new String [1]; 681 682 MgmtTransaction mtx = new MgmtTransaction(Connection.TRANSACTION_READ_COMMITTED) 683 { 684 public String toString() 685 { 686 return "Query schema table for schema " + schemaName; 687 } 688 689 protected void execute(Connection conn) throws SQLException 690 { 691 synchronized (StoreManager.this) 692 { 693 if (schemaTable != null) 694 s[0] = schemaTable.getJavaName(tableID, conn); 695 } 696 } 697 }; 698 699 mtx.execute(); 700 701 String javaName = s[0]; 702 703 if (javaName == null) 704 throw new JDOUserException("Unknown table ID = " + tableID); 705 706 return javaName; 707 } 708 } 709 710 public Extent getExtent(PersistenceManager pm, Class c, boolean subclasses) 711 { 712 ClassTable t = getTable(c); 713 714 return t.newExtent(pm, subclasses); 715 } 716 717 public Query getQuery(PersistenceManager pm, Object query) 718 { 719 return getQuery("javax.jdo.query.JDOQL", pm, query); 720 } 721 722 public Query getQuery(String language, PersistenceManager pm, Object query) 723 { 724 Query q; 725 726 if (language.equals("javax.jdo.query.JDOQL")) 727 { 728 if (query != null && !(query instanceof JDOQLQuery)) 729 throw new JDOUserException("Invalid query argument, " + query + ", should be an object of type " + JDOQLQuery.class.getName()); 730 731 q = new JDOQLQuery(pm, this, (JDOQLQuery)query); 732 } 733 else if (language.equals("javax.jdo.query.TJDOSQL")) 734 { 735 if (query == null || !(query instanceof String )) 736 throw new JDOUserException("Invalid query argument, " + query + ", should be a String containing a SQL SELECT statement, optionally using embedded macros"); 737 738 q = new TJDOSQLQuery(pm, this, (String )query); 739 } 740 else 741 throw new JDOUserException("Unknown query language: " + language); 742 743 return q; 744 } 745 746 747 754 755 public Object newObjectID(Class c) 756 { 757 ClassMetaData cmd = ClassMetaData.forClass(c); 758 759 if (cmd == null) 760 throw new ClassNotPersistenceCapableException(c); 761 762 if (cmd.requiresExtent()) 763 return getTable(c).newOID(); 764 else 765 return new SCOID(c); 766 } 767 768 769 779 780 int getNextOIDHiValue(final int classID) 781 { 782 final int[] nextHiValue = new int[] { -1 }; 783 MgmtTransaction mtx = new MgmtTransaction(Connection.TRANSACTION_SERIALIZABLE) 784 { 785 public String toString() 786 { 787 return "Obtain next ID value for class ID " + classID; 788 } 789 790 protected void execute(Connection conn) throws SQLException 791 { 792 nextHiValue[0] = schemaTable.getNextOIDHiValue(classID, conn); 793 } 794 }; 795 796 mtx.execute(); 797 798 return nextHiValue[0]; 799 } 800 801 802 807 808 public void insert(StateManager sm) 809 { 810 getClassBaseTable(sm.getObject().getClass()).insert(sm); 811 } 812 813 814 819 820 public void lookup(StateManager sm) 821 { 822 getClassBaseTable(sm.getObject().getClass()).lookup(sm); 823 } 824 825 826 832 833 public void fetch(StateManager sm, int fieldNumbers[]) 834 { 835 getClassBaseTable(sm.getObject().getClass()).fetch(sm, fieldNumbers); 836 } 837 838 839 845 846 public void update(StateManager sm, int fieldNumbers[]) 847 { 848 getClassBaseTable(sm.getObject().getClass()).update(sm, fieldNumbers); 849 } 850 851 852 857 858 public void delete(StateManager sm) 859 { 860 getClassBaseTable(sm.getObject().getClass()).delete(sm); 861 } 862 863 864 873 874 InsertRequest getInsertRequest(ClassBaseTable cbt) 875 { 876 RequestIdentifier reqID = new RequestIdentifier(cbt, null, RequestIdentifier.Type.INSERT); 877 InsertRequest req; 878 879 req = (InsertRequest)requestsByID.get(reqID); 880 881 if (req == null) 882 { 883 req = new InsertRequest(cbt); 884 requestsByID.put(reqID, req); 885 } 886 887 return req; 888 } 889 890 891 900 901 LookupRequest getLookupRequest(ClassBaseTable cbt) 902 { 903 RequestIdentifier reqID = new RequestIdentifier(cbt, null, RequestIdentifier.Type.LOOKUP); 904 LookupRequest req; 905 906 req = (LookupRequest)requestsByID.get(reqID); 907 908 if (req == null) 909 { 910 req = new LookupRequest(cbt); 911 requestsByID.put(reqID, req); 912 } 913 914 return req; 915 } 916 917 918 930 931 FetchRequest getFetchRequest(ClassBaseTable cbt, int[] fieldNumbers) 932 { 933 RequestIdentifier reqID = new RequestIdentifier(cbt, fieldNumbers, RequestIdentifier.Type.FETCH); 934 FetchRequest req; 935 936 req = (FetchRequest)requestsByID.get(reqID); 937 938 if (req == null) 939 { 940 req = new FetchRequest(cbt, fieldNumbers); 941 requestsByID.put(reqID, req); 942 } 943 944 return req; 945 } 946 947 948 960 961 UpdateRequest getUpdateRequest(ClassBaseTable cbt, int[] fieldNumbers) 962 { 963 RequestIdentifier reqID = new RequestIdentifier(cbt, fieldNumbers, RequestIdentifier.Type.UPDATE); 964 UpdateRequest req; 965 966 req = (UpdateRequest)requestsByID.get(reqID); 967 968 if (req == null) 969 { 970 req = new UpdateRequest(cbt, fieldNumbers); 971 requestsByID.put(reqID, req); 972 } 973 974 return req; 975 } 976 977 978 987 988 DeleteRequest getDeleteRequest(ClassBaseTable cbt) 989 { 990 RequestIdentifier reqID = new RequestIdentifier(cbt, null, RequestIdentifier.Type.DELETE); 991 DeleteRequest req; 992 993 req = (DeleteRequest)requestsByID.get(reqID); 994 995 if (req == null) 996 { 997 req = new DeleteRequest(cbt); 998 requestsByID.put(reqID, req); 999 } 1000 1001 return req; 1002 } 1003 1004 1005 1015 1016 public int getTableType(SQLIdentifier tableName, Connection conn) throws SQLException 1017 { 1018 String tableType = null; 1019 String tableNameSQL = tableName.getSQLIdentifier(); 1020 DatabaseMetaData dmd = conn.getMetaData(); 1021 1022 ResultSet rs = dmd.getTables(null, schemaName, tableNameSQL, null); 1023 1024 try 1025 { 1026 while (rs.next()) 1027 { 1028 if (tableNameSQL.equalsIgnoreCase(rs.getString(3))) 1029 { 1030 tableType = rs.getString(4).toUpperCase(); 1031 break; 1032 } 1033 } 1034 } 1035 finally 1036 { 1037 rs.close(); 1038 } 1039 1040 if (tableType == null) 1041 return Table.TABLE_TYPE_MISSING; 1042 else if (tableType.equals("TABLE")) 1043 return Table.TABLE_TYPE_BASE_TABLE; 1044 else if (tableType.equals("VIEW")) 1045 return Table.TABLE_TYPE_VIEW; 1046 else 1047 return Table.TABLE_TYPE_UNKNOWN; 1048 } 1049 1050 1051 1070 1071 List getColumnInfo(SQLIdentifier tableName, Connection conn) throws SQLException 1072 { 1073 List cols = null; 1074 1075 if (schemaTable == null) 1076 { 1077 1083 cols = new ArrayList (); 1084 DatabaseMetaData dmd = conn.getMetaData(); 1085 ResultSet rs = dmd.getColumns(null, schemaName, tableName.getSQLIdentifier(), null); 1086 1087 try 1088 { 1089 while (rs.next()) 1090 cols.add(dba.newColumnInfo(rs)); 1091 } 1092 finally 1093 { 1094 rs.close(); 1095 } 1096 } 1097 else 1098 { 1099 synchronized (this) 1100 { 1101 long now = System.currentTimeMillis(); 1102 1103 1107 if (now >= columnInfoReadTimestamp && now < columnInfoReadTimestamp + COLUMN_INFO_EXPIRATION_MS) 1108 cols = (List )columnInfoByTableName.get(tableName); 1109 1110 1114 if (cols == null) 1115 { 1116 1120 HashSet knownTableNames = new HashSet (); 1121 Iterator i = schemaTable.getAllTableMetadata(false, conn).iterator(); 1122 1123 while (i.hasNext()) 1124 knownTableNames.add(((TableMetadata)i.next()).tableName); 1125 1126 1131 HashMap cim = new HashMap (); 1132 DatabaseMetaData dmd = conn.getMetaData(); 1133 ResultSet rs = dmd.getColumns(null, schemaName, null, null); 1134 1135 try 1136 { 1137 while (rs.next()) 1138 { 1139 SQLIdentifier tblName = new SQLIdentifier(dba, rs.getString(3)); 1140 1141 if (knownTableNames.contains(tblName)) 1142 { 1143 Table tbl = (Table)tablesByName.get(tblName); 1144 1145 if (tbl == null || !tbl.isValidated()) 1146 { 1147 List l = (List )cim.get(tblName); 1148 1149 if (l == null) 1150 { 1151 l = new ArrayList (); 1152 cim.put(tblName, l); 1153 } 1154 1155 l.add(dba.newColumnInfo(rs)); 1156 } 1157 } 1158 } 1159 } 1160 finally 1161 { 1162 rs.close(); 1163 } 1164 1165 if (LOG.isDebugEnabled()) 1166 LOG.debug("Column info loaded for " + schemaName + ", " + cim.size() + " tables, time = " + (System.currentTimeMillis() - now) + " ms"); 1167 1168 1169 columnInfoByTableName = cim; 1170 columnInfoReadTimestamp = now; 1171 1172 1176 cols = (List )columnInfoByTableName.get(tableName); 1177 1178 if (cols == null) 1179 { 1180 cols = Collections.EMPTY_LIST; 1181 LOG.warn("No column info found for " + tableName); 1182 } 1183 } 1184 } 1185 } 1186 1187 return cols; 1188 } 1189 1190 1191 1208 1209 List getForeignKeyInfo(SQLIdentifier tableName, Connection conn) throws SQLException 1210 { 1211 List fkCols = new ArrayList (); 1212 DatabaseMetaData dmd = conn.getMetaData(); 1213 ResultSet rs = dmd.getImportedKeys(null, schemaName, tableName.getSQLIdentifier()); 1214 1215 try 1216 { 1217 while (rs.next()) 1218 { 1219 ForeignKeyInfo fki = dba.newForeignKeyInfo(rs); 1220 1221 1226 if (!fkCols.contains(fki)) 1227 fkCols.add(fki); 1228 } 1229 } 1230 finally 1231 { 1232 rs.close(); 1233 } 1234 1235 return fkCols; 1236 } 1237 1238 1239 1248 1249 public boolean tableExists(SQLIdentifier tableName, Connection conn) throws SQLException 1250 { 1251 return getTableType(tableName, conn) != Table.TABLE_TYPE_MISSING; 1252 } 1253 1254 1255 1263 1264 public void resolveIdentifierMacro(MacroString.IdentifierMacro im) 1265 { 1266 ClassTable ct = getTable(im.clazz); 1267 1268 if (im.fieldName == null) 1269 { 1270 im.value = ct.getName().toString(); 1271 return; 1272 } 1273 1274 ColumnMapping cm; 1275 1276 if (im.fieldName.equals("this")) 1277 { 1278 if (!(ct instanceof ClassBaseTable)) 1279 throw new JDOUserException("Table for class " + im.clazz.getName() + " has no ID column"); 1280 1281 if (im.subfieldName != null) 1282 throw new JDOUserException("Field " + im.clazz.getName() + ".this has no table of its own in which to look for " + im.subfieldName); 1283 1284 cm = ((ClassBaseTable)ct).getIDMapping(); 1285 } 1286 else 1287 { 1288 ClassMetaData cmd = ClassMetaData.forClass(im.clazz); 1289 FieldMetaData fmd = cmd.getFieldRelative(cmd.getRelativeFieldNumber(im.fieldName)); 1290 1291 if (im.subfieldName == null) 1292 { 1293 Mapping m = ct.getFieldMapping(im.fieldName); 1294 1295 if (m instanceof ColumnMapping) 1296 cm = (ColumnMapping)m; 1297 else 1298 { 1299 JDOTable t = getTable(fmd); 1300 1301 if (t == null) 1302 throw new JDOUserException("Invalid pseudo-field name " + im.subfieldName + " in macro " + im + ", has no backing table or column"); 1303 1304 im.value = t.getName().toString(); 1305 return; 1306 } 1307 } 1308 else 1309 { 1310 JDOTable t = getTable(fmd); 1311 1312 if (t instanceof SetTable) 1313 { 1314 SetTable st = (SetTable)t; 1315 1316 if (im.subfieldName.equals("owner")) 1317 cm = st.getOwnerMapping(); 1318 else if (im.subfieldName.equals("element")) 1319 cm = st.getElementMapping(); 1320 else 1321 throw new JDOUserException("Invalid pseudo-field name " + im.subfieldName + " in macro " + im + ", must be \"owner\" or \"element\""); 1322 } 1323 else if (t instanceof MapTable) 1324 { 1325 MapTable mt = (MapTable)t; 1326 1327 if (im.subfieldName.equals("owner")) 1328 cm = mt.getOwnerMapping(); 1329 else if (im.subfieldName.equals("key")) 1330 cm = mt.getKeyMapping(); 1331 else if (im.subfieldName.equals("value")) 1332 cm = mt.getValueMapping(); 1333 else 1334 throw new JDOUserException("Invalid pseudo-field name " + im.subfieldName + " in macro " + im + ", must be \"owner\", \"key\", or \"value\""); 1335 } 1336 else 1337 throw new JDOUserException("Field " + im.clazz.getName() + '.' + im.fieldName + " has no table of its own in which to look for " + im.subfieldName); 1338 } 1339 } 1340 1341 im.value = cm.getColumn().getName().toString(); 1342 } 1343 1344 1345 1346 1347 1348 1355 1356 private abstract class MgmtTransaction 1357 { 1358 public static final String MAX_RETRIES_PROPERTY = "com.triactive.jdo.store.maxRetries"; 1359 1360 protected final int isolationLevel; 1361 protected final int maxRetries; 1362 1363 1364 1371 1372 public MgmtTransaction(int isolationLevel) 1373 { 1374 this.isolationLevel = isolationLevel; 1375 1376 String s = System.getProperty(MAX_RETRIES_PROPERTY, "3"); 1377 int mr; 1378 1379 try 1380 { 1381 mr = Integer.parseInt(s); 1382 } 1383 catch (NumberFormatException e) 1384 { 1385 LOG.warn("Failed parsing " + MAX_RETRIES_PROPERTY + " property, value was " + s); 1386 mr = 3; 1387 } 1388 1389 maxRetries = mr; 1390 } 1391 1392 1393 1400 1401 public abstract String toString(); 1402 1403 1404 1416 1417 protected abstract void execute(Connection conn) throws SQLException ; 1418 1419 1420 1436 1437 public final void execute() 1438 { 1439 int attempts = 0; 1440 1441 for (;;) 1442 { 1443 try 1444 { 1445 Connection conn = dba.getConnection(ds, userName, password, isolationLevel); 1446 1447 try 1448 { 1449 boolean succeeded = false; 1450 1451 try 1452 { 1453 execute(conn); 1454 succeeded = true; 1455 } 1456 finally 1457 { 1458 if (isolationLevel != Connection.TRANSACTION_NONE) 1459 { 1460 if (succeeded) 1461 conn.commit(); 1462 else 1463 conn.rollback(); 1464 } 1465 } 1466 } 1467 finally 1468 { 1469 dba.closeConnection(conn); 1470 } 1471 1472 break; 1473 } 1474 catch (SQLException e) 1475 { 1476 SQLState state = dba.getSQLState(e); 1477 1478 if ((state != null && !state.isWorthRetrying()) || ++attempts >= maxRetries) 1479 throw new JDODataStoreException("SQL exception: " + this, e); 1480 } 1481 } 1482 } 1483 } 1484 1485 1486 1503 1504 private class ClassAdder extends MgmtTransaction 1505 { 1506 private final Class [] classes; 1507 private Connection schemaConnection = null; 1508 1509 1510 1516 1517 public ClassAdder(Class [] classes) 1518 { 1519 super(Connection.TRANSACTION_SERIALIZABLE); 1520 1521 this.classes = classes; 1522 } 1523 1524 1525 public String toString() 1526 { 1527 return "Add classes to schema " + schemaName; 1528 } 1529 1530 1531 protected void execute(Connection conn) throws SQLException 1532 { 1533 synchronized (StoreManager.this) 1534 { 1535 classAdder = this; 1536 schemaConnection = conn; 1537 1538 try 1539 { 1540 try 1541 { 1542 addClassTablesAndValidate(classes); 1543 } 1544 catch (NestedSQLException e) 1545 { 1546 throw e.getSQLException(); 1547 } 1548 } 1549 finally 1550 { 1551 schemaConnection = null; 1552 classAdder = null; 1553 } 1554 } 1555 } 1556 1557 1558 1571 1572 public void addClasses(Class [] classes) 1573 { 1574 if (schemaConnection == null) 1575 throw new IllegalStateException ("Add classes transaction is not active"); 1576 1577 try 1578 { 1579 addClassTables(classes); 1580 } 1581 catch (SQLException e) 1582 { 1583 throw new NestedSQLException(e); 1584 } 1585 } 1586 1587 1588 1598 1599 private void addClassTables(Class [] classes) throws SQLException 1600 { 1601 Iterator i = getReferencedClasses(classes).iterator(); 1602 1603 while (i.hasNext()) 1604 { 1605 ClassMetaData cmd = (ClassMetaData)i.next(); 1606 1607 if (getTable(cmd) == null && cmd.requiresExtent()) 1608 { 1609 TableMetadata tmd = schemaTable.getTableMetadata(cmd, schemaConnection); 1610 ClassTable t; 1611 1612 if (cmd.getViewDefinition(dba.getVendorID()) != null) 1613 t = new ClassView(tmd, cmd, StoreManager.this); 1614 else 1615 t = new ClassBaseTable(tmd, cmd, StoreManager.this); 1616 1617 addTable(t); 1618 } 1619 } 1620 1621 } 1622 1623 1624 1637 1638 private List getReferencedClasses(Class [] classes) 1639 { 1640 List cmds = new ArrayList (); 1641 1642 for (int i = 0; i < classes.length; ++i) 1643 { 1644 ClassMetaData cmd = ClassMetaData.forClass(classes[i]); 1645 1646 if (cmd == null) 1647 throw new ClassNotPersistenceCapableException(classes[i]); 1648 1649 cmds.addAll(cmd.getReferencedClasses(dba.getVendorID())); 1650 } 1651 1652 return cmds; 1653 } 1654 1655 1656 1661 1662 private void addTable(JDOTable t) 1663 { 1664 allTables.add(t); 1665 1666 int tableID = t.getTableID(); 1667 1668 while (tablesByTableID.size() <= tableID) 1669 tablesByTableID.add(null); 1670 1671 tablesByTableID.set(tableID, t); 1672 tablesByName.put(t.getName(), t); 1673 tablesByJavaID.put(t.getJavaName(), t); 1674 } 1675 1676 1677 1692 1693 private void addClassTablesAndValidate(Class [] classes) throws SQLException 1694 { 1695 ArrayList allTablesPrev = (ArrayList )allTables.clone(); 1696 ArrayList tablesByTableIDPrev = (ArrayList )tablesByTableID.clone(); 1697 HashMap tablesByNamePrev = (HashMap )tablesByName.clone(); 1698 HashMap tablesByJavaIDPrev = (HashMap )tablesByJavaID.clone(); 1699 1700 ArrayList baseTablesCreated = new ArrayList (); 1701 ArrayList baseTableConstraintsCreated = new ArrayList (); 1702 ArrayList viewsCreated = new ArrayList (); 1703 boolean completed = false; 1704 1705 try 1706 { 1707 1708 addClassTables(classes); 1709 1710 1717 ArrayList newBaseTables = new ArrayList (); 1718 ArrayList newViews = new ArrayList (); 1719 boolean someNeededInitialization; 1720 1721 do 1722 { 1723 someNeededInitialization = false; 1724 1725 Iterator i = ((ArrayList )allTables.clone()).iterator(); 1726 1727 while (i.hasNext()) 1728 { 1729 JDOTable t = (JDOTable)i.next(); 1730 1731 if (!t.isInitialized()) 1732 { 1733 t.initialize(); 1734 1735 if (t instanceof View) 1736 newViews.add(t); 1737 else 1738 newBaseTables.add(t); 1739 1740 someNeededInitialization = true; 1741 } 1742 } 1743 } while (someNeededInitialization); 1744 1745 1750 Iterator i = newBaseTables.iterator(); 1751 1752 while (i.hasNext()) 1753 { 1754 BaseTable t = (BaseTable)i.next(); 1755 1756 if (t.validate(tableValidationFlags, schemaConnection)) 1757 baseTablesCreated.add(t); 1758 1759 1760 columnInfoByTableName.remove(t.getName()); 1761 } 1762 1763 1773 i = newBaseTables.iterator(); 1774 1775 while (i.hasNext()) 1776 { 1777 BaseTable t = (BaseTable)i.next(); 1778 1779 if (t.validateConstraints(constraintValidationFlags, schemaConnection)) 1780 baseTableConstraintsCreated.add(t); 1781 } 1782 1783 1788 i = newViews.iterator(); 1789 1790 while (i.hasNext()) 1791 { 1792 View v = (View)i.next(); 1793 1794 if (v.validate(tableValidationFlags, schemaConnection)) 1795 viewsCreated.add(v); 1796 1797 1798 columnInfoByTableName.remove(v.getName()); 1799 } 1800 1801 completed = true; 1802 } 1803 finally 1804 { 1805 1812 if (!completed) 1813 { 1814 allTables = allTablesPrev; 1815 tablesByTableID = tablesByTableIDPrev; 1816 tablesByName = tablesByNamePrev; 1817 tablesByJavaID = tablesByJavaIDPrev; 1818 1819 1823 try 1824 { 1825 ListIterator li = viewsCreated.listIterator(viewsCreated.size()); 1826 1827 while (li.hasPrevious()) 1828 ((View)li.previous()).drop(schemaConnection); 1829 1830 li = baseTableConstraintsCreated.listIterator(baseTableConstraintsCreated.size()); 1831 1832 while (li.hasPrevious()) 1833 ((BaseTable)li.previous()).dropConstraints(schemaConnection); 1834 1835 li = baseTablesCreated.listIterator(baseTablesCreated.size()); 1836 1837 while (li.hasPrevious()) 1838 ((BaseTable)li.previous()).drop(schemaConnection); 1839 } 1840 catch (Exception e) 1841 { 1842 LOG.warn("An error occurred while auto-creating schema elements. The following exception occurred while attempting to rollback the partially-completed schema changes: " + e.toString()); 1843 } 1844 } 1845 } 1846 } 1847 1848 1849 1862 1863 public SetTable newSetTable(ClassBaseTable cbt, FieldMetaData fmd) 1864 { 1865 TableMetadata tmd; 1866 1867 try 1868 { 1869 tmd = schemaTable.getTableMetadata(fmd, schemaConnection); 1870 } 1871 catch (SQLException e) 1872 { 1873 throw new NestedSQLException(e); 1874 } 1875 1876 SetTable st = new SetTable(tmd, fmd, StoreManager.this); 1877 1878 addTable(st); 1879 1880 return st; 1881 } 1882 1883 1884 1897 1898 public MapTable newMapTable(ClassBaseTable cbt, FieldMetaData fmd) 1899 { 1900 TableMetadata tmd; 1901 1902 try 1903 { 1904 tmd = schemaTable.getTableMetadata(fmd, schemaConnection); 1905 } 1906 catch (SQLException e) 1907 { 1908 throw new NestedSQLException(e); 1909 } 1910 1911 MapTable mt = new MapTable(tmd, fmd, StoreManager.this); 1912 1913 addTable(mt); 1914 1915 return mt; 1916 } 1917 } 1918 1919 1920 1924 1925 private static class NestedSQLException extends RuntimeException 1926 { 1927 private final SQLException e; 1928 1929 public NestedSQLException(SQLException e) 1930 { 1931 super("Inner invocation of recursive procedure threw a SQL exception: " + e.getMessage()); 1932 this.e = e; 1933 } 1934 1935 public SQLException getSQLException() 1936 { 1937 return e; 1938 } 1939 } 1940} 1941 | Popular Tags |