1 21 22 package org.opensubsystems.core.persist.db; 23 24 import java.sql.Connection ; 25 import java.sql.PreparedStatement ; 26 import java.sql.ResultSet ; 27 import java.sql.SQLException ; 28 import java.sql.Statement ; 29 import java.util.Collection ; 30 import java.util.Iterator ; 31 import java.util.Map ; 32 import java.util.logging.Level ; 33 import java.util.logging.Logger ; 34 35 import org.apache.commons.collections.map.LinkedMap; 36 import org.opensubsystems.core.error.OSSDatabaseAccessException; 37 import org.opensubsystems.core.error.OSSException; 38 import org.opensubsystems.core.util.DatabaseUtils; 39 import org.opensubsystems.core.util.GlobalConstants; 40 import org.opensubsystems.core.util.Log; 41 42 53 public class VersionedDatabaseSchema extends DatabaseSchemaImpl 54 { 55 68 69 71 74 public static final String VERSIONED_SCHEMA_NAME = "SCHEMA"; 75 76 81 public static final int VERSIONED_SCHEMA_VERSION = 2; 82 83 86 public static final String SCHEMA_TABLE_NAME = getVersionedDatabaseSchemaTableName(); 87 88 90 94 private Map m_mpSchemas; 95 96 98 101 private static Logger s_logger = Log.getInstance(VersionedDatabaseSchema.class); 102 103 105 110 public VersionedDatabaseSchema( 111 ) throws OSSException 112 { 113 super(null, VERSIONED_SCHEMA_NAME, VERSIONED_SCHEMA_VERSION, true); 114 115 m_mpSchemas = new LinkedMap(); 117 add(this); 119 } 120 121 123 126 private static String getVersionedDatabaseSchemaTableName( 127 ) 128 { 129 return getSchemaPrefix() + "SCHEMA"; 130 } 131 132 134 140 public void add( 141 DatabaseSchema dsSchema 142 ) throws OSSException 143 { 144 if (GlobalConstants.ERROR_CHECKING) 145 { 146 assert dsSchema != null : "Cannot add null schema."; 147 } 154 155 if (m_mpSchemas.get(dsSchema.getName()) != null) 156 { 157 s_logger.log(Level.FINEST, "Database schema " + dsSchema.getName() 158 + " is already managed by versioned schema." 159 + " It is not added second time."); 160 } 161 else 162 { 163 DatabaseSchema[] arSchemas; 164 165 arSchemas = dsSchema.getDependentSchemas(); 166 if (arSchemas != null) 169 { 170 for (int iIndex = 0; iIndex < arSchemas.length; iIndex++) 171 { 172 add(arSchemas[iIndex]); 173 } 174 } 175 m_mpSchemas.put(dsSchema.getName(), dsSchema); 176 s_logger.log(Level.FINEST, "Database schema " + dsSchema.getName() 177 + " version " + dsSchema.getVersion() 178 + " is now managed by versioned schema."); 179 } 180 } 181 182 184 193 public void init( 194 Connection cntDBConnection, 195 String strUserName 196 ) throws OSSException, 197 SQLException 198 { 199 if (GlobalConstants.ERROR_CHECKING) 200 { 201 assert cntDBConnection != null : "Connection is not valid."; 202 } 203 204 Map mpSchemasToAdd = new LinkedMap(m_mpSchemas); 208 Map mpSchemasToUpgrade = new LinkedMap(); 210 211 loadExistingSchemas(cntDBConnection, mpSchemasToAdd, mpSchemasToUpgrade); 212 createOrUpgradeSchemas(cntDBConnection, strUserName, mpSchemasToAdd, 213 mpSchemasToUpgrade); 214 } 215 216 224 public void create( 225 Connection cntDBConnection, 226 String strUserName 227 ) throws SQLException , OSSException 228 { 229 s_logger.entering(this.getClass().getName(), "create"); 230 231 try 232 { 233 Statement stmQuery = null; 234 try 235 { 236 237 stmQuery = cntDBConnection.createStatement(); 238 if (stmQuery.execute( 239 "create table " + SCHEMA_TABLE_NAME + NL 240 + "(" + NL 241 + " SCHEMA_NAME VARCHAR(50) NOT NULL," + NL 242 + " SCHEMA_VERSION INTEGER NOT NULL," + NL 243 + " CREATION_TIMESTAMP TIMESTAMP NOT NULL," + NL 244 + " MODIFICATION_TIMESTAMP TIMESTAMP NOT NULL," + NL 245 + " CONSTRAINT " + getSchemaPrefix() + "SCH_PK PRIMARY KEY" + 246 " (SCHEMA_NAME)" + NL 247 + ")")) 248 { 249 stmQuery.getMoreResults(Statement.CLOSE_ALL_RESULTS); 251 } 252 s_logger.log(Level.FINEST, "Table " + SCHEMA_TABLE_NAME + " created."); 253 261 } 262 catch (SQLException sqleExc) 263 { 264 s_logger.log(Level.WARNING, "Failed to create version schema.", 266 sqleExc); 267 throw sqleExc; 268 } 269 finally 270 { 271 DatabaseUtils.closeStatement(stmQuery); 272 } 273 } 274 finally 275 { 276 s_logger.exiting(this.getClass().getName(), "create"); 277 } 278 } 279 280 282 295 private PreparedStatement createSchema( 296 Connection cntDBConnection, 297 String strUserName, 298 DatabaseSchema dsSchema, 299 PreparedStatement pstmUpdate 300 ) throws OSSException, 301 SQLException 302 { 303 int iUpdateCount; 304 305 try 306 { 307 308 s_logger.log(Level.FINEST, "Creating new database schema " 309 + dsSchema.getName() + " version " + dsSchema.getVersion() 310 + "."); 311 dsSchema.create(cntDBConnection, strUserName); 312 313 if (pstmUpdate == null) 316 { 317 pstmUpdate = cntDBConnection.prepareStatement( 319 "insert into " + SCHEMA_TABLE_NAME + " (SCHEMA_NAME, SCHEMA_VERSION," 320 + " CREATION_TIMESTAMP, MODIFICATION_TIMESTAMP)" 321 + " values (?, ?, " 322 + DatabaseImpl.getInstance().getCurrentTimestampFunctionCall() 323 + ", " 324 + DatabaseImpl.getInstance().getCurrentTimestampFunctionCall() 325 + ")"); 326 } 327 328 pstmUpdate.setString(1, dsSchema.getName()); 329 pstmUpdate.setInt(2, dsSchema.getVersion()); 330 iUpdateCount = pstmUpdate.executeUpdate(); 331 if (iUpdateCount == 0) 332 { 333 String strMessage; 334 335 strMessage = "Record for database schema " + dsSchema.getName() 336 + " version " + dsSchema.getVersion() 337 + " was not created."; 338 s_logger.log(Level.WARNING, strMessage); 339 throw new OSSDatabaseAccessException(strMessage); 340 341 } 342 s_logger.log(Level.FINER, "Database schema " + dsSchema.getName() 343 + " created."); 344 345 351 DatabaseTransactionFactoryImpl.getInstance().commitTransaction(cntDBConnection); 356 } 357 catch (OSSException ossExc) 358 { 359 DatabaseTransactionFactoryImpl.getInstance().rollbackTransaction(cntDBConnection); 364 throw ossExc; 366 } 367 catch (SQLException sqlExc) 368 { 369 DatabaseTransactionFactoryImpl.getInstance().rollbackTransaction(cntDBConnection); 374 throw sqlExc; 376 } 377 catch (Throwable thr) 378 { 379 DatabaseTransactionFactoryImpl.getInstance().rollbackTransaction(cntDBConnection); 384 throw new OSSDatabaseAccessException(thr); 385 } 386 387 return pstmUpdate; 388 } 389 390 404 private PreparedStatement upgradeSchema( 405 Connection cntDBConnection, 406 String strUserName, 407 DatabaseSchema dsSchema, 408 int iOriginalVersion, 409 PreparedStatement pstmUpdate 410 ) throws OSSException, 411 SQLException 412 { 413 int iUpdateCount; 414 415 s_logger.log(Level.FINEST, "Upgrading new database schema " 416 + dsSchema.getName() + " version " + dsSchema.getVersion() 417 + "."); 418 dsSchema.upgrade(cntDBConnection, strUserName, iOriginalVersion); 419 420 if (pstmUpdate == null) 422 { 423 pstmUpdate = cntDBConnection.prepareStatement( 425 "update " + SCHEMA_TABLE_NAME + " set SCHEMA_VERSION = ?," 426 + " MODIFICATION_TIMESTAMP = " 427 + DatabaseImpl.getInstance().getCurrentTimestampFunctionCall() 428 + " where SCHEMA_NAME = ? and SCHEMA_VERSION < ?"); 429 } 430 431 pstmUpdate.setInt(1, dsSchema.getVersion()); 432 pstmUpdate.setString(2, dsSchema.getName()); 433 pstmUpdate.setInt(3, dsSchema.getVersion()); 434 iUpdateCount = pstmUpdate.executeUpdate(); 435 if (iUpdateCount == 0) 436 { 437 String strMessage; 438 439 strMessage = "No record for database schema " + dsSchema.getName() 440 + " version " + dsSchema.getVersion() 441 + " was updated to new version."; 442 s_logger.log(Level.WARNING, strMessage); 443 throw new OSSDatabaseAccessException(strMessage); 444 445 } 446 else if (iUpdateCount > 1) 447 { 448 String strMessage; 449 450 strMessage = "Multiple (" + iUpdateCount + ") records for database schema " 451 + dsSchema.getName() + " version " + dsSchema.getVersion() 452 + " were updated to new version."; 453 s_logger.log(Level.WARNING, strMessage); 454 throw new OSSDatabaseAccessException(strMessage); 455 456 } 457 s_logger.log(Level.FINER, "Database schema " + dsSchema.getName() 458 + " upgraded."); 459 460 return pstmUpdate; 461 } 462 463 473 protected void loadExistingSchemas( 474 Connection cntDBConnection, 475 Map mpSchemasToAdd, 476 Map mpSchemasToUpgrade 477 ) throws OSSException 478 { 479 Statement stmQuery = null; 481 ResultSet rsQueryResults = null; 482 try 483 { 484 s_logger.fine("Verifying existing database schemas"); 485 486 stmQuery = cntDBConnection.createStatement(); 487 rsQueryResults = stmQuery.executeQuery( 488 "select SCHEMA_NAME, SCHEMA_VERSION from " + SCHEMA_TABLE_NAME); 489 490 String strSchemaName; 491 int iLastVersion; 492 DatabaseSchema dsSchema; 493 494 while (rsQueryResults.next()) 495 { 496 strSchemaName = rsQueryResults.getString(1); 497 dsSchema = (DatabaseSchema) mpSchemasToAdd.remove(strSchemaName); 500 if (dsSchema != null) 501 { 502 iLastVersion = rsQueryResults.getInt(2); 504 if (iLastVersion < dsSchema.getVersion()) 505 { 506 s_logger.finer("Database contains schema " + strSchemaName 507 + " version " + iLastVersion 508 + ", but the current schema has version " 509 + dsSchema.getVersion() 510 + ". It needs to be upgraded."); 511 mpSchemasToUpgrade.put(dsSchema.getName(), 513 new Integer (iLastVersion)); 514 } 515 else if (iLastVersion > dsSchema.getVersion()) 516 { 517 String strMessage; 518 519 strMessage = "Database contains schema " + strSchemaName 520 + " version " + iLastVersion 521 + ", which is newer that the current schema version " 522 + dsSchema.getVersion() 523 + ". You should obtain newer version of the product."; 524 525 s_logger.finer(strMessage); 526 throw new OSSDatabaseAccessException(strMessage); 527 } 528 else 529 { 530 s_logger.finer("Database schema " + strSchemaName 531 + " version " + iLastVersion + " is up to date."); 532 } 533 } 534 else 535 { 536 s_logger.finer("Database contains schema " + strSchemaName 540 + ", which doesn't exist anymore in current schema."); 541 } 542 } 543 544 s_logger.fine("Existing database schemas verified: " 545 + mpSchemasToAdd.size() + " schemas to add and " 546 + mpSchemasToUpgrade.size() + " schemas to upgrade."); 547 } 548 catch (SQLException sqleExc) 549 { 550 s_logger.log(Level.FINER, 551 "Failed to load version schema, trying to create one.", 552 sqleExc); 553 if (GlobalConstants.ERROR_CHECKING) 555 { 556 assert mpSchemasToUpgrade.isEmpty() 557 : "There should be no schemas to upgrade."; 558 assert mpSchemasToAdd.size() == m_mpSchemas.size() 559 : "All schemas should be set to be created."; 560 } 561 } 562 finally 563 { 564 DatabaseUtils.closeResultSetAndStatement(rsQueryResults, stmQuery); 565 } 566 } 567 568 578 protected void createOrUpgradeSchemas( 579 Connection cntDBConnection, 580 String strUserName, 581 Map mpSchemasToAdd, 582 Map mpSchemasToUpgrade 583 ) throws OSSException, 584 SQLException 585 { 586 if ((!mpSchemasToAdd.isEmpty()) || (!mpSchemasToUpgrade.isEmpty())) 592 { 593 s_logger.fine("Adding new and upgraging existing database schemas"); 594 595 Collection clSchemas; 596 Iterator itrSchemas; 597 DatabaseSchema dsSchema; 598 PreparedStatement pstmCreate = null; 599 PreparedStatement pstmUpdate = null; 600 Integer intLastVersion; 601 602 clSchemas = m_mpSchemas.values(); 603 try 604 { 605 if (mpSchemasToAdd.get(getName()) != null) 608 { 609 pstmCreate = createSchema(cntDBConnection, strUserName, 610 this, pstmCreate); 611 } 612 else 613 { 614 intLastVersion = (Integer )mpSchemasToUpgrade.get(getName()); 615 if (intLastVersion != null) 616 { 617 pstmUpdate = upgradeSchema(cntDBConnection, strUserName, 618 this, intLastVersion.intValue(), 619 pstmUpdate); 620 } 621 } 622 623 for (itrSchemas = clSchemas.iterator(); itrSchemas.hasNext();) 626 { 627 dsSchema = (DatabaseSchema) itrSchemas.next(); 628 if (dsSchema != this) 629 { 630 if (mpSchemasToAdd.get(dsSchema.getName()) != null) 631 { 632 pstmCreate = createSchema(cntDBConnection, strUserName, 633 dsSchema, pstmCreate); 634 } 635 else 636 { 637 intLastVersion = (Integer )mpSchemasToUpgrade.get(dsSchema.getName()); 638 if (intLastVersion != null) 639 { 640 pstmUpdate = upgradeSchema(cntDBConnection, strUserName, 641 dsSchema, intLastVersion.intValue(), 642 pstmUpdate); 643 } 644 } 645 } 646 } 647 648 s_logger.fine("Database schemas have been updated."); 649 } 650 finally 651 { 652 DatabaseUtils.closeStatement(pstmCreate); 653 DatabaseUtils.closeStatement(pstmUpdate); 654 } 655 } 656 } 657 } 658 | Popular Tags |