1 5 package com.tc.objectserver.persistence.sleepycat; 6 7 import org.apache.commons.io.FileUtils; 8 9 import com.sleepycat.bind.serial.ClassCatalog; 10 import com.sleepycat.bind.serial.StoredClassCatalog; 11 import com.sleepycat.je.Database; 12 import com.sleepycat.je.DatabaseConfig; 13 import com.sleepycat.je.DatabaseEntry; 14 import com.sleepycat.je.DatabaseException; 15 import com.sleepycat.je.Environment; 16 import com.sleepycat.je.EnvironmentConfig; 17 import com.sleepycat.je.LockMode; 18 import com.sleepycat.je.OperationStatus; 19 import com.sleepycat.je.Transaction; 20 import com.tc.logging.CustomerLogging; 21 import com.tc.logging.TCLogger; 22 import com.tc.logging.TCLogging; 23 24 import java.io.File ; 25 import java.io.IOException ; 26 import java.util.ArrayList ; 27 import java.util.HashMap ; 28 import java.util.Iterator ; 29 import java.util.LinkedList ; 30 import java.util.List ; 31 import java.util.Map ; 32 import java.util.Properties ; 33 34 public class DBEnvironment { 35 36 private static final TCLogger clogger = CustomerLogging.getDSOGenericLogger(); 37 private static final TCLogger logger = TCLogging.getLogger(DBEnvironment.class); 38 39 private static final String OBJECTID_SEQUENCE_NAME = "objectids"; 40 private static final String ROOT_DB_NAME = "roots"; 41 private static final String OBJECT_DB_NAME = "objects"; 42 43 private static final String CLIENTID_SEQUENCE_NAME = "clientids"; 44 private static final String CLIENT_STATE_DB_NAME = "clientstates"; 45 private static final String TRANSACTION_DB_NAME = "transactions"; 46 private static final String TRANSACTION_SEQUENCE_DB_NAME = "transactionsequence"; 47 private static final String STRING_INDEX_DB_NAME = "stringindex"; 48 private static final String CLASS_DB_NAME = "classdefinitions"; 49 private static final String MAP_DB_NAME = "mapsdatabase"; 50 private static final String CLUSTER_STATE_STORE = "clusterstatestore"; 51 52 private static final Object CONTROL_LOCK = new Object (); 53 54 private static final DBEnvironmentStatus STATUS_INIT = new DBEnvironmentStatus("INIT"); 55 private static final DBEnvironmentStatus STATUS_ERROR = new DBEnvironmentStatus("ERROR"); 56 private static final DBEnvironmentStatus STATUS_OPENING = new DBEnvironmentStatus("OPENING"); 57 private static final DBEnvironmentStatus STATUS_OPEN = new DBEnvironmentStatus("OPEN"); 58 private static final DBEnvironmentStatus STATUS_CLOSING = new DBEnvironmentStatus("CLOSING"); 59 private static final DBEnvironmentStatus STATUS_CLOSED = new DBEnvironmentStatus("CLOSED"); 60 61 private static final DatabaseEntry CLEAN_FLAG_KEY = new DatabaseEntry(new byte[] { 1 }); 62 private static final byte IS_CLEAN = 1; 63 private static final byte IS_DIRTY = 2; 64 65 private final List createdDatabases; 66 private final Map databasesByName; 67 private final File envHome; 68 private EnvironmentConfig ecfg; 69 private DatabaseConfig dbcfg; 70 private ClassCatalogWrapper catalog; 71 72 private Environment env; 73 private Database controlDB; 74 private DBEnvironmentStatus status = STATUS_INIT; 75 private DatabaseOpenResult openResult = null; 76 77 private final boolean paranoid; 78 79 public DBEnvironment(boolean paranoid, File envHome) throws IOException { 80 this(paranoid, envHome, new Properties ()); 81 } 82 83 public DBEnvironment(boolean paranoid, File envHome, Properties jeProperties) throws IOException { 84 this(new HashMap (), new LinkedList (), paranoid, envHome); 85 this.ecfg = new EnvironmentConfig(jeProperties); 86 this.ecfg.setCachePercent(25); 87 this.ecfg.setTransactional(true); 88 this.ecfg.setAllowCreate(true); 89 this.ecfg.setReadOnly(false); 90 this.ecfg.setTxnWriteNoSync(!paranoid); 91 this.ecfg.setLockTimeout(180000000L); 93 this.dbcfg = new DatabaseConfig(); 94 this.dbcfg.setAllowCreate(true); 95 this.dbcfg.setTransactional(true); 96 97 logger.info("Env config = " + this.ecfg + " DB Config = " + this.dbcfg + " JE Properties = " + jeProperties); 98 } 99 100 DBEnvironment(boolean paranoid, File envHome, EnvironmentConfig ecfg, DatabaseConfig dbcfg) throws IOException { 102 this(new HashMap (), new LinkedList (), paranoid, envHome, ecfg, dbcfg); 103 } 104 105 DBEnvironment(Map databasesByName, List createdDatabases, boolean paranoid, File envHome, EnvironmentConfig ecfg, 107 DatabaseConfig dbcfg) throws IOException { 108 this(databasesByName, createdDatabases, paranoid, envHome); 109 this.ecfg = ecfg; 110 this.dbcfg = dbcfg; 111 } 112 113 118 private DBEnvironment(Map databasesByName, List createdDatabases, boolean paranoid, File envHome) throws IOException { 119 this.databasesByName = databasesByName; 120 this.createdDatabases = createdDatabases; 121 this.paranoid = paranoid; 122 this.envHome = envHome; 123 FileUtils.forceMkdir(this.envHome); 124 } 125 126 public synchronized DatabaseOpenResult open() throws DatabaseException { 127 assertInit(); 128 status = STATUS_OPENING; 129 try { 130 env = newEnvironment(); 131 synchronized (CONTROL_LOCK) { 132 controlDB = env.openDatabase(null, "control", this.dbcfg); 135 openResult = new DatabaseOpenResult(isClean()); 136 if (!openResult.isClean()) { 137 this.status = STATUS_INIT; 138 forceClose(); 139 return openResult; 140 } 141 } 142 if (!this.paranoid) setDirty(); 143 this.catalog = new ClassCatalogWrapper(env, dbcfg); 144 newDatabase(env, OBJECTID_SEQUENCE_NAME); 145 newDatabase(env, OBJECT_DB_NAME); 146 newDatabase(env, ROOT_DB_NAME); 147 148 newDatabase(env, CLIENTID_SEQUENCE_NAME); 149 newDatabase(env, CLIENT_STATE_DB_NAME); 150 newDatabase(env, TRANSACTION_DB_NAME); 151 newDatabase(env, TRANSACTION_SEQUENCE_DB_NAME); 152 newDatabase(env, STRING_INDEX_DB_NAME); 153 newDatabase(env, CLASS_DB_NAME); 154 newDatabase(env, MAP_DB_NAME); 155 newDatabase(env, CLUSTER_STATE_STORE); 156 } catch (DatabaseException e) { 157 this.status = STATUS_ERROR; 158 forceClose(); 159 throw e; 160 } catch (Error e) { 161 this.status = STATUS_ERROR; 162 forceClose(); 163 throw e; 164 } catch (RuntimeException e) { 165 this.status = STATUS_ERROR; 166 forceClose(); 167 throw e; 168 } 169 170 this.status = STATUS_OPEN; 171 return openResult; 172 } 173 174 private void cinfo(Object message) { 175 clogger.info("DB Environment: " + message); 176 } 177 178 public synchronized void close() throws DatabaseException { 179 assertOpen(); 180 status = STATUS_CLOSING; 181 cinfo("Closing..."); 182 for (Iterator i = createdDatabases.iterator(); i.hasNext();) { 183 Database db = (Database) i.next(); 184 cinfo("Closing database: " + db.getDatabaseName() + "..."); 185 db.close(); 186 } 187 cinfo("Closing class catalog..."); 188 this.catalog.close(); 189 setClean(); 190 if (this.controlDB != null) { 191 cinfo("Closing control database..."); 192 this.controlDB.close(); 193 } 194 if (this.env != null) { 195 cinfo("Closing environment..."); 196 this.env.close(); 197 } 198 this.controlDB = null; 199 this.env = null; 200 201 status = STATUS_CLOSED; 202 cinfo("Closed."); 203 } 204 205 public synchronized boolean isOpen() { 206 return STATUS_OPEN.equals(status); 207 } 208 209 synchronized void forceClose() { 211 List toClose = new ArrayList (createdDatabases); 212 toClose.add(controlDB); 213 for (Iterator i = toClose.iterator(); i.hasNext();) { 214 try { 215 Database db = (Database) i.next(); 216 if (db != null) db.close(); 217 } catch (DatabaseException e) { 218 e.printStackTrace(); 219 } 220 } 221 222 try { 223 if (this.catalog != null) this.catalog.close(); 224 } catch (DatabaseException e) { 225 e.printStackTrace(); 226 } 227 228 try { 229 if (env != null) env.close(); 230 } catch (DatabaseException e) { 231 e.printStackTrace(); 232 } 233 } 234 235 public File getEnvironmentHome() { 236 return envHome; 237 } 238 239 public synchronized Environment getEnvironment() throws DatabaseException { 240 assertOpen(); 241 return env; 242 } 243 244 public synchronized Database getObjectDatabase() throws DatabaseException { 245 assertOpen(); 246 return (Database) databasesByName.get(OBJECT_DB_NAME); 247 } 248 249 public synchronized ClassCatalogWrapper getClassCatalogWrapper() throws DatabaseException { 250 assertOpen(); 251 return catalog; 252 } 253 254 public synchronized Database getRootDatabase() throws DatabaseException { 255 assertOpen(); 256 return (Database) databasesByName.get(ROOT_DB_NAME); 257 } 258 259 public synchronized Database getObjectIDDB() throws DatabaseException { 260 assertOpen(); 261 return (Database) databasesByName.get(OBJECTID_SEQUENCE_NAME); 262 } 263 264 public Database getClientStateDatabase() throws DatabaseException { 265 assertOpen(); 266 return (Database) databasesByName.get(CLIENT_STATE_DB_NAME); 267 } 268 269 public Database getClientIDDatabase() throws DatabaseException { 270 assertOpen(); 271 return (Database) databasesByName.get(CLIENTID_SEQUENCE_NAME); 272 } 273 274 public Database getTransactionDatabase() throws DatabaseException { 275 assertOpen(); 276 return (Database) databasesByName.get(TRANSACTION_DB_NAME); 277 } 278 279 public Database getTransactionSequenceDatabase() throws DatabaseException { 280 assertOpen(); 281 return (Database) databasesByName.get(TRANSACTION_SEQUENCE_DB_NAME); 282 } 283 284 public Database getClassDatabase() throws DatabaseException { 285 assertOpen(); 286 return (Database) databasesByName.get(CLASS_DB_NAME); 287 } 288 289 public Database getMapsDatabase() throws DatabaseException { 290 assertOpen(); 291 return (Database) databasesByName.get(MAP_DB_NAME); 292 } 293 294 public Database getStringIndexDatabase() throws DatabaseException { 295 assertOpen(); 296 return (Database) databasesByName.get(STRING_INDEX_DB_NAME); 297 } 298 299 public Database getClusterStateStoreDatabase() throws DatabaseException { 300 assertOpen(); 301 return (Database) databasesByName.get(CLUSTER_STATE_STORE); 302 } 303 304 private void assertNotError() throws DatabaseException { 305 if (STATUS_ERROR == status) throw new TCDatabaseException("Attempt to operate on an environment in an error state."); 306 } 307 308 private void assertInit() throws DatabaseException { 309 if (STATUS_INIT != status) throw new DatabaseOpenException("Database environment isn't in INIT state."); 310 } 311 312 private void assertOpening() { 313 if (STATUS_OPENING != status) throw new AssertionError ("Database environment should be opening but isn't"); 314 } 315 316 private void assertOpen() throws DatabaseException { 317 assertNotError(); 318 if (STATUS_OPEN != status) throw new DatabaseNotOpenException("Database environment should be open but isn't."); 319 } 320 321 private void assertClosing() { 322 if (STATUS_CLOSING != status) throw new AssertionError ("Database environment should be closing but isn't"); 323 } 324 325 private boolean isClean() throws DatabaseException { 326 assertOpening(); 327 DatabaseEntry value = new DatabaseEntry(new byte[] { 0 }); 328 Transaction tx = newTransaction(); 329 OperationStatus stat = controlDB.get(tx, CLEAN_FLAG_KEY, value, LockMode.DEFAULT); 330 tx.commit(); 331 return OperationStatus.NOTFOUND.equals(stat) 332 || (OperationStatus.SUCCESS.equals(stat) && value.getData()[0] == IS_CLEAN); 333 } 334 335 private void setDirty() throws DatabaseException { 336 assertOpening(); 337 DatabaseEntry value = new DatabaseEntry(new byte[] { IS_DIRTY }); 338 Transaction tx = newTransaction(); 339 OperationStatus stat = controlDB.put(tx, CLEAN_FLAG_KEY, value); 340 if (!OperationStatus.SUCCESS.equals(stat)) throw new TCDatabaseException("Unexpected operation status " 341 + "trying to unset clean flag: " + stat); 342 tx.commitSync(); 343 } 344 345 private Transaction newTransaction() throws DatabaseException { 346 Transaction tx = env.beginTransaction(null, null); 347 return tx; 348 } 349 350 private void setClean() throws DatabaseException { 351 assertClosing(); 352 DatabaseEntry value = new DatabaseEntry(new byte[] { IS_CLEAN }); 353 Transaction tx = newTransaction(); 354 OperationStatus stat = controlDB.put(tx, CLEAN_FLAG_KEY, value); 355 if (!OperationStatus.SUCCESS.equals(stat)) throw new TCDatabaseException("Unexpected operation status " 356 + "trying to set clean flag: " + stat); 357 tx.commitSync(); 358 } 359 360 private void newDatabase(Environment e, String name) throws DatabaseException { 361 Database db = e.openDatabase(null, name, dbcfg); 362 createdDatabases.add(db); 363 databasesByName.put(name, db); 364 } 365 366 private Environment newEnvironment() throws DatabaseException { 367 return new Environment(envHome, ecfg); 368 } 369 370 private static final class DBEnvironmentStatus { 371 private final String description; 372 373 DBEnvironmentStatus(String desc) { 374 this.description = desc; 375 } 376 377 public String toString() { 378 return this.description; 379 } 380 } 381 382 public static final class ClassCatalogWrapper { 383 384 private final StoredClassCatalog catalog; 385 private boolean closed = false; 386 387 private ClassCatalogWrapper(Environment env, DatabaseConfig cfg) throws DatabaseException { 388 catalog = new StoredClassCatalog(env.openDatabase(null, "java_class_catalog", cfg)); 389 } 390 391 public final ClassCatalog getClassCatalog() { 392 return this.catalog; 393 } 394 395 synchronized void close() throws DatabaseException { 396 if (closed) throw new IllegalStateException ("Already closed."); 397 this.catalog.close(); 398 closed = true; 399 } 400 } 401 402 } | Popular Tags |