KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > objectserver > persistence > sleepycat > DBEnvironment


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
3  * notice. All rights reserved.
4  */

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 JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.LinkedList JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Properties JavaDoc;
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 JavaDoc OBJECTID_SEQUENCE_NAME = "objectids";
40   private static final String JavaDoc ROOT_DB_NAME = "roots";
41   private static final String JavaDoc OBJECT_DB_NAME = "objects";
42
43   private static final String JavaDoc CLIENTID_SEQUENCE_NAME = "clientids";
44   private static final String JavaDoc CLIENT_STATE_DB_NAME = "clientstates";
45   private static final String JavaDoc TRANSACTION_DB_NAME = "transactions";
46   private static final String JavaDoc TRANSACTION_SEQUENCE_DB_NAME = "transactionsequence";
47   private static final String JavaDoc STRING_INDEX_DB_NAME = "stringindex";
48   private static final String JavaDoc CLASS_DB_NAME = "classdefinitions";
49   private static final String JavaDoc MAP_DB_NAME = "mapsdatabase";
50   private static final String JavaDoc CLUSTER_STATE_STORE = "clusterstatestore";
51
52   private static final Object JavaDoc CONTROL_LOCK = new Object JavaDoc();
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 JavaDoc createdDatabases;
66   private final Map JavaDoc databasesByName;
67   private final File JavaDoc 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 JavaDoc envHome) throws IOException JavaDoc {
80     this(paranoid, envHome, new Properties JavaDoc());
81   }
82
83   public DBEnvironment(boolean paranoid, File JavaDoc envHome, Properties JavaDoc jeProperties) throws IOException JavaDoc {
84     this(new HashMap JavaDoc(), new LinkedList JavaDoc(), 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     // Lock timeout in microseconds - 3 minutes
92
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   // For tests
101
DBEnvironment(boolean paranoid, File JavaDoc envHome, EnvironmentConfig ecfg, DatabaseConfig dbcfg) throws IOException JavaDoc {
102     this(new HashMap JavaDoc(), new LinkedList JavaDoc(), paranoid, envHome, ecfg, dbcfg);
103   }
104
105   // For tests
106
DBEnvironment(Map JavaDoc databasesByName, List JavaDoc createdDatabases, boolean paranoid, File JavaDoc envHome, EnvironmentConfig ecfg,
107                 DatabaseConfig dbcfg) throws IOException JavaDoc {
108     this(databasesByName, createdDatabases, paranoid, envHome);
109     this.ecfg = ecfg;
110     this.dbcfg = dbcfg;
111   }
112
113   /**
114    * Note: it is not currently safe to create more than one of these instances in the same process. Sleepycat is
115    * supposed to keep more than one process from opening a writable handle to the same database, but it allows you to
116    * create more than one writable handle within the same process. So, don't do that.
117    */

118   private DBEnvironment(Map JavaDoc databasesByName, List JavaDoc createdDatabases, boolean paranoid, File JavaDoc envHome) throws IOException JavaDoc {
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         // XXX: Note: this doesn't guard against multiple instances in different
133
// classloaders...
134
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 JavaDoc e) {
161       this.status = STATUS_ERROR;
162       forceClose();
163       throw e;
164     } catch (RuntimeException JavaDoc 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 JavaDoc 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 JavaDoc 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   // This is for testing and cleanup on error.
210
synchronized void forceClose() {
211     List JavaDoc toClose = new ArrayList JavaDoc(createdDatabases);
212     toClose.add(controlDB);
213     for (Iterator JavaDoc 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 JavaDoc 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 JavaDoc("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 JavaDoc("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 JavaDoc 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 JavaDoc description;
372
373     DBEnvironmentStatus(String JavaDoc desc) {
374       this.description = desc;
375     }
376
377     public String JavaDoc 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 JavaDoc("Already closed.");
397       this.catalog.close();
398       closed = true;
399     }
400   }
401
402 }
Popular Tags