001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018: package org.columba.mail.folder.headercache;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.util.ArrayList;
023: import java.util.HashSet;
024: import java.util.List;
025: import java.util.Set;
026: import java.util.logging.Logger;
027:
028: import org.columba.mail.config.MailConfig;
029: import org.columba.mail.folder.IHeaderListCorruptedListener;
030: import org.columba.mail.message.ICloseableIterator;
031: import org.columba.mail.message.IColumbaHeader;
032: import org.columba.mail.message.IPersistantHeaderList;
033:
034: import com.sleepycat.bind.tuple.IntegerBinding;
035: import com.sleepycat.bind.tuple.StringBinding;
036: import com.sleepycat.bind.tuple.TupleBinding;
037: import com.sleepycat.collections.StoredIterator;
038: import com.sleepycat.collections.StoredKeySet;
039: import com.sleepycat.collections.StoredValueSet;
040: import com.sleepycat.je.BtreeStats;
041: import com.sleepycat.je.Database;
042: import com.sleepycat.je.DatabaseConfig;
043: import com.sleepycat.je.DatabaseEntry;
044: import com.sleepycat.je.DatabaseException;
045: import com.sleepycat.je.Environment;
046: import com.sleepycat.je.EnvironmentConfig;
047: import com.sleepycat.je.LockMode;
048: import com.sleepycat.je.OperationStatus;
049:
050: public class BerkeleyDBHeaderList implements IPersistantHeaderList {
051:
052: /** JDK 1.4+ logging framework logger, used for logging. */
053: private static final Logger LOG = Logger
054: .getLogger("org.columba.mail.folder.headercache");
055:
056: private String name;
057: private File headercacheDirectory;
058: private Database db;
059: private static Environment environment;
060: private DatabaseConfig databaseConfig;
061:
062: private TupleBinding headerBinding;
063: private TupleBinding integerBinding;
064: private TupleBinding stringBinding;
065:
066: private Class keyType = Integer.class;
067:
068: private List listeners;
069:
070: private static int openDatabases = 0;
071:
072: public BerkeleyDBHeaderList(File headerCacheDirectory, String name) {
073: this (headerCacheDirectory, name, new DefaultHeaderBinding());
074: }
075:
076: public BerkeleyDBHeaderList(File headerCacheDirectory, String name,
077: TupleBinding headerBinding) {
078: super ();
079: this .name = name;
080: this .headerBinding = headerBinding;
081: this .headercacheDirectory = headerCacheDirectory;
082:
083: integerBinding = new IntegerBinding();
084: stringBinding = new StringBinding();
085:
086: listeners = new ArrayList();
087: }
088:
089: private synchronized void openEnvironment()
090: throws DatabaseException {
091: if (environment != null)
092: return;
093: if (!headercacheDirectory.exists())
094: headercacheDirectory.mkdir();
095:
096: EnvironmentConfig environmentConfig = new EnvironmentConfig();
097: environmentConfig.setAllowCreate(true);
098: environmentConfig.setCachePercent(80);
099: // perform other environment configurations
100: try {
101: environment = new Environment(headercacheDirectory,
102: environmentConfig);
103: } catch (DatabaseException e) {
104: LOG.severe(e.getMessage());
105: fireHeaderListCorrupted();
106: throw e;
107: }
108:
109: }
110:
111: private synchronized void openDatabase() throws DatabaseException {
112: if (db != null)
113: return;
114: openEnvironment();
115: try {
116: databaseConfig = new DatabaseConfig();
117: databaseConfig.setAllowCreate(true);
118: // perform other database configurations
119: db = environment.openDatabase(null, name, databaseConfig);
120:
121: openDatabases++;
122: } catch (DatabaseException e) {
123: LOG.severe(e.getMessage());
124: fireHeaderListCorrupted();
125: throw e;
126: }
127: }
128:
129: /* (non-Javadoc)
130: * @see org.columba.mail.folder.headercache.HeaderList#add(org.columba.mail.message.IColumbaHeader, java.lang.Object)
131: */
132: public void add(IColumbaHeader header, Object uid) {
133: try {
134: openDatabase();
135: header.getAttributes().put("columba.uid", uid);
136:
137: db.put(null, getDatabaseEntry(uid),
138: getDatabaseEntry(header));
139: } catch (DatabaseException e) {
140: LOG.severe(e.getMessage());
141: fireHeaderListCorrupted();
142: }
143: }
144:
145: private DatabaseEntry getDatabaseEntry(Object in) {
146: DatabaseEntry result = new DatabaseEntry();
147:
148: if (in instanceof String) {
149: stringBinding.objectToEntry(in, result);
150: } else if (in instanceof Integer) {
151: integerBinding.objectToEntry(in, result);
152: } else if (in instanceof IColumbaHeader) {
153: headerBinding.objectToEntry(in, result);
154: }
155:
156: return result;
157: }
158:
159: /* (non-Javadoc)
160: * @see org.columba.mail.folder.headercache.HeaderList#clear()
161: */
162: public void clear() {
163: closeDatabase();
164:
165: try {
166: environment.truncateDatabase(null, name, true);
167: } catch (DatabaseException e) {
168: LOG.warning(e.getMessage());
169: }
170: }
171:
172: private void closeEnvironment() {
173: if (db != null)
174: closeDatabase();
175:
176: try {
177: if (environment != null) {
178: environment.close();
179: environment = null;
180: }
181: } catch (DatabaseException e) {
182: LOG.warning(e.getMessage());
183: }
184:
185: }
186:
187: private void closeDatabase() {
188: try {
189: if (db != null) {
190: openDatabases--;
191: db.close();
192: db = null;
193: }
194:
195: } catch (DatabaseException e) {
196: LOG.warning(e.getMessage());
197: }
198:
199: if (openDatabases == 0) {
200: closeEnvironment();
201: }
202:
203: }
204:
205: /* (non-Javadoc)
206: * @see org.columba.mail.folder.headercache.HeaderList#containsValue(java.lang.Object)
207: */
208: public boolean exists(Object uid) {
209: try {
210: openDatabase();
211: return db.get(null, getDatabaseEntry(uid),
212: new DatabaseEntry(), LockMode.DEFAULT).equals(
213: OperationStatus.SUCCESS);
214: } catch (DatabaseException e) {
215: LOG.fine(e.getMessage());
216: return false;
217: }
218: }
219:
220: /* (non-Javadoc)
221: * @see org.columba.mail.folder.headercache.HeaderList#count()
222: */
223: public int count() {
224:
225: try {
226: openDatabase();
227: return (int) ((BtreeStats) db.getStats(null))
228: .getLeafNodeCount();
229: } catch (DatabaseException e) {
230: LOG.severe(e.getMessage());
231: fireHeaderListCorrupted();
232:
233: return 0;
234: }
235: }
236:
237: /* (non-Javadoc)
238: * @see org.columba.mail.folder.headercache.HeaderList#get(java.lang.Object)
239: */
240: public IColumbaHeader get(Object uid) {
241:
242: DatabaseEntry result = new DatabaseEntry();
243: try {
244: openDatabase();
245: OperationStatus status = db.get(null,
246: getDatabaseEntry(uid), result, LockMode.DEFAULT);
247: if (status.equals(OperationStatus.SUCCESS)) {
248: return (IColumbaHeader) headerBinding
249: .entryToObject(result);
250: }
251: } catch (DatabaseException e) {
252: LOG.severe(e.getMessage());
253: }
254: return null;
255: }
256:
257: /* (non-Javadoc)
258: * @see org.columba.mail.folder.headercache.HeaderList#getUids()
259: */
260: public Object[] getUids() {
261: try {
262: openDatabase();
263: return keySet().toArray();
264: } catch (DatabaseException e) {
265: LOG.severe(e.getMessage());
266: return new Object[0];
267: }
268: }
269:
270: /* (non-Javadoc)
271: * @see org.columba.mail.folder.headercache.HeaderList#keySet()
272: */
273: public Set keySet() {
274: try {
275: openDatabase();
276: } catch (DatabaseException e) {
277: LOG.severe(e.getMessage());
278: return new HashSet();
279: }
280:
281: if (keyType == Integer.class) {
282: return new StoredKeySet(db, integerBinding, false);
283: } else if (keyType == String.class) {
284: return new StoredKeySet(db, stringBinding, false);
285: }
286:
287: throw new IllegalArgumentException("keyType not implemented!");
288: }
289:
290: /* (non-Javadoc)
291: * @see org.columba.mail.folder.headercache.HeaderList#remove(java.lang.Object)
292: */
293: public IColumbaHeader remove(Object uid) {
294: try {
295: openDatabase();
296:
297: IColumbaHeader header = get(uid);
298: db.delete(null, getDatabaseEntry(uid));
299:
300: return header;
301: } catch (DatabaseException e) {
302: LOG.severe(e.getMessage());
303:
304: return null;
305: }
306:
307: }
308:
309: /* (non-Javadoc)
310: * @see org.columba.mail.folder.headercache.PersistantHeaderList#persist()
311: */
312: public void persist() throws IOException {
313: closeDatabase();
314: }
315:
316: /* (non-Javadoc)
317: * @see org.columba.mail.folder.headercache.HeaderList#headerIterator()
318: */
319: public ICloseableIterator headerIterator() {
320: try {
321: openDatabase();
322: } catch (DatabaseException e) {
323: LOG.severe(e.getMessage());
324: throw new RuntimeException(e);
325: }
326: return new BerkeleyDBIterator(
327: (StoredIterator) new StoredValueSet(db, headerBinding,
328: false).iterator());
329: }
330:
331: /* (non-Javadoc)
332: * @see org.columba.mail.folder.headercache.HeaderList#keyIterator()
333: */
334: public ICloseableIterator keyIterator() {
335: try {
336: openDatabase();
337: } catch (DatabaseException e) {
338: LOG.severe(e.getMessage());
339: throw new RuntimeException(e);
340: }
341: return new BerkeleyDBIterator(
342: (StoredIterator) new StoredKeySet(db, integerBinding,
343: false).iterator());
344: }
345:
346: /* (non-Javadoc)
347: * @see org.columba.mail.folder.headercache.HeaderList#update(java.lang.Object, org.columba.mail.message.IColumbaHeader)
348: */
349: public void update(Object uid, IColumbaHeader header) {
350: try {
351: openDatabase();
352: } catch (DatabaseException e) {
353: LOG.severe(e.getMessage());
354: throw new RuntimeException(e);
355: }
356: DatabaseEntry key = getDatabaseEntry(uid);
357:
358: try {
359: db.delete(null, key);
360: db.put(null, key, getDatabaseEntry(header));
361: } catch (DatabaseException e) {
362: LOG.severe(e.getMessage());
363: fireHeaderListCorrupted();
364: }
365: }
366:
367: /**
368: * @param headerBinding The headerBinding to set.
369: */
370: public void setHeaderBinding(TupleBinding headerBinding) {
371: this .headerBinding = headerBinding;
372: }
373:
374: /**
375: * @return Returns the keyType.
376: */
377: public Class getKeyType() {
378: return keyType;
379: }
380:
381: /**
382: * @param keyType The keyType to set.
383: */
384: public void setKeyType(Class keyType) {
385: this .keyType = keyType;
386: }
387:
388: private void fireHeaderListCorrupted() {
389: for (int i = 0; i < listeners.size(); i++) {
390: ((IHeaderListCorruptedListener) listeners.get(i))
391: .headerListCorrupted(this );
392: }
393: }
394:
395: public void addHeaderListCorruptedListener(
396: IHeaderListCorruptedListener listener) {
397: listeners.add(listener);
398: }
399:
400: public void removeHeaderListCorruptedListener(
401: IHeaderListCorruptedListener listener) {
402: listeners.remove(listener);
403: }
404: }
|