1 package org.columba.mail.folder.headercache; 19 20 import java.io.File ; 21 import java.io.IOException ; 22 import java.util.ArrayList ; 23 import java.util.HashSet ; 24 import java.util.List ; 25 import java.util.Set ; 26 import java.util.logging.Logger ; 27 28 import org.columba.mail.config.MailConfig; 29 import org.columba.mail.folder.IHeaderListCorruptedListener; 30 import org.columba.mail.message.ICloseableIterator; 31 import org.columba.mail.message.IColumbaHeader; 32 import org.columba.mail.message.IPersistantHeaderList; 33 34 import com.sleepycat.bind.tuple.IntegerBinding; 35 import com.sleepycat.bind.tuple.StringBinding; 36 import com.sleepycat.bind.tuple.TupleBinding; 37 import com.sleepycat.collections.StoredIterator; 38 import com.sleepycat.collections.StoredKeySet; 39 import com.sleepycat.collections.StoredValueSet; 40 import com.sleepycat.je.BtreeStats; 41 import com.sleepycat.je.Database; 42 import com.sleepycat.je.DatabaseConfig; 43 import com.sleepycat.je.DatabaseEntry; 44 import com.sleepycat.je.DatabaseException; 45 import com.sleepycat.je.Environment; 46 import com.sleepycat.je.EnvironmentConfig; 47 import com.sleepycat.je.LockMode; 48 import com.sleepycat.je.OperationStatus; 49 50 public class BerkeleyDBHeaderList implements IPersistantHeaderList { 51 52 53 private static final Logger LOG = Logger 54 .getLogger("org.columba.mail.folder.headercache"); 55 56 57 private String name; 58 private File headercacheDirectory; 59 private Database db; 60 private static Environment environment; 61 private DatabaseConfig databaseConfig; 62 63 private TupleBinding headerBinding; 64 private TupleBinding integerBinding; 65 private TupleBinding stringBinding; 66 67 private Class keyType = Integer .class; 68 69 private List listeners; 70 71 private static int openDatabases = 0; 72 73 public BerkeleyDBHeaderList(File headerCacheDirectory, String name){ 74 this(headerCacheDirectory, name, new DefaultHeaderBinding()); 75 } 76 77 78 public BerkeleyDBHeaderList(File headerCacheDirectory, String name, TupleBinding headerBinding){ 79 super(); 80 this.name = name; 81 this.headerBinding = headerBinding; 82 this.headercacheDirectory = headerCacheDirectory; 83 84 integerBinding = new IntegerBinding(); 85 stringBinding = new StringBinding(); 86 87 listeners = new ArrayList (); 88 } 89 90 private synchronized void openEnvironment() throws DatabaseException { 91 if( environment != null) return; 92 if( !headercacheDirectory.exists()) headercacheDirectory.mkdir(); 93 94 EnvironmentConfig environmentConfig = new EnvironmentConfig(); 95 environmentConfig.setAllowCreate(true); 96 environmentConfig.setCachePercent(80); 97 try { 99 environment = new Environment(headercacheDirectory, environmentConfig); 100 } catch (DatabaseException e) { 101 LOG.severe(e.getMessage()); 102 fireHeaderListCorrupted(); 103 throw e; 104 } 105 106 107 } 108 109 private synchronized void openDatabase() throws DatabaseException{ 110 if( db != null ) return; 111 openEnvironment(); 112 try { 113 databaseConfig = new DatabaseConfig(); 114 databaseConfig.setAllowCreate(true); 115 db = environment.openDatabase(null, name , databaseConfig); 117 118 openDatabases++; 119 } catch (DatabaseException e) { 120 LOG.severe(e.getMessage()); 121 fireHeaderListCorrupted(); 122 throw e; 123 } 124 } 125 126 129 public void add(IColumbaHeader header, Object uid) { 130 try { 131 openDatabase(); 132 header.getAttributes().put("columba.uid", uid); 133 134 db.put(null, getDatabaseEntry(uid), getDatabaseEntry(header)); 135 } catch (DatabaseException e) { 136 LOG.severe(e.getMessage()); 137 fireHeaderListCorrupted(); 138 } 139 } 140 141 private DatabaseEntry getDatabaseEntry(Object in) { 142 DatabaseEntry result = new DatabaseEntry(); 143 144 if( in instanceof String ) { 145 stringBinding.objectToEntry( in, result); 146 } else if( in instanceof Integer ) { 147 integerBinding.objectToEntry(in, result); 148 } else if( in instanceof IColumbaHeader) { 149 headerBinding.objectToEntry(in, result); 150 } 151 152 return result; 153 } 154 155 156 159 public void clear() { 160 closeDatabase(); 161 162 try { 163 environment.truncateDatabase(null, name, true); 164 } catch (DatabaseException e) { 165 LOG.warning(e.getMessage()); 166 } 167 } 168 169 private void closeEnvironment() { 170 if( db != null) closeDatabase(); 171 172 try { 173 if( environment != null ) { 174 environment.close(); 175 environment = null; 176 } 177 } catch (DatabaseException e) { 178 LOG.warning(e.getMessage()); 179 } 180 181 } 182 183 private void closeDatabase() { 184 try { 185 if( db != null ) { 186 openDatabases--; 187 db.close(); 188 db = null; 189 } 190 191 } catch (DatabaseException e) { 192 LOG.warning(e.getMessage()); 193 } 194 195 if( openDatabases == 0 ) { 196 closeEnvironment(); 197 } 198 199 } 200 201 204 public boolean exists(Object uid) { 205 try { 206 openDatabase(); 207 return db.get(null, getDatabaseEntry(uid), new DatabaseEntry(), LockMode.DEFAULT).equals(OperationStatus.SUCCESS); 208 } catch (DatabaseException e) { 209 LOG.fine(e.getMessage()); 210 return false; 211 } 212 } 213 214 217 public int count() { 218 219 try { 220 openDatabase(); 221 return (int) ((BtreeStats)db.getStats(null)).getLeafNodeCount(); 222 } catch (DatabaseException e) { 223 LOG.severe(e.getMessage()); 224 fireHeaderListCorrupted(); 225 226 return 0; 227 } 228 } 229 230 233 public IColumbaHeader get(Object uid) { 234 235 DatabaseEntry result = new DatabaseEntry(); 236 try { 237 openDatabase(); 238 OperationStatus status = db.get(null, getDatabaseEntry(uid), result, LockMode.DEFAULT); 239 if( status.equals(OperationStatus.SUCCESS) ) { 240 return (IColumbaHeader)headerBinding.entryToObject(result); 241 } 242 } catch (DatabaseException e) { 243 LOG.severe(e.getMessage()); 244 } 245 return null; 246 } 247 248 249 252 public Object [] getUids() { 253 try { 254 openDatabase(); 255 return keySet().toArray(); 256 } catch (DatabaseException e) { 257 LOG.severe(e.getMessage()); 258 return new Object [0]; 259 } 260 } 261 262 265 public Set keySet() { 266 try { 267 openDatabase(); 268 } catch (DatabaseException e) { 269 LOG.severe(e.getMessage()); 270 return new HashSet (); 271 } 272 273 if( keyType == Integer .class ) { 274 return new StoredKeySet(db, integerBinding, false); 275 } else if( keyType == String .class) { 276 return new StoredKeySet(db, stringBinding, false); 277 } 278 279 throw new IllegalArgumentException ("keyType not implemented!"); 280 } 281 282 285 public IColumbaHeader remove(Object uid) { 286 try { 287 openDatabase(); 288 289 IColumbaHeader header = get(uid); 290 db.delete(null, getDatabaseEntry(uid)); 291 292 return header; 293 } catch (DatabaseException e) { 294 LOG.severe(e.getMessage()); 295 296 return null; 297 } 298 299 } 300 301 304 public void persist() throws IOException { 305 closeDatabase(); 306 } 307 308 311 public ICloseableIterator headerIterator() { 312 try { 313 openDatabase(); 314 } catch (DatabaseException e) { 315 LOG.severe(e.getMessage()); 316 throw new RuntimeException (e); 317 } 318 return new BerkeleyDBIterator( (StoredIterator)new StoredValueSet(db,headerBinding, false).iterator()); 319 } 320 321 324 public ICloseableIterator keyIterator() { 325 try { 326 openDatabase(); 327 } catch (DatabaseException e) { 328 LOG.severe(e.getMessage()); 329 throw new RuntimeException (e); 330 } 331 return new BerkeleyDBIterator( (StoredIterator)new StoredKeySet(db, integerBinding, false).iterator()); 332 } 333 334 337 public void update(Object uid, IColumbaHeader header) { 338 try { 339 openDatabase(); 340 } catch (DatabaseException e) { 341 LOG.severe(e.getMessage()); 342 throw new RuntimeException (e); 343 } 344 DatabaseEntry key = getDatabaseEntry(uid); 345 346 try { 347 db.delete(null, key); 348 db.put(null, key, getDatabaseEntry(header)); 349 } catch (DatabaseException e) { 350 LOG.severe(e.getMessage()); 351 fireHeaderListCorrupted(); 352 } 353 } 354 355 358 public void setHeaderBinding(TupleBinding headerBinding) { 359 this.headerBinding = headerBinding; 360 } 361 362 363 366 public Class getKeyType() { 367 return keyType; 368 } 369 370 371 374 public void setKeyType(Class keyType) { 375 this.keyType = keyType; 376 } 377 378 private void fireHeaderListCorrupted() { 379 for( int i=0; i<listeners.size(); i++) { 380 ((IHeaderListCorruptedListener)listeners.get(i)).headerListCorrupted(this); 381 } 382 } 383 384 385 public void addHeaderListCorruptedListener(IHeaderListCorruptedListener listener) { 386 listeners.add(listener); 387 } 388 389 public void removeHeaderListCorruptedListener(IHeaderListCorruptedListener listener) { 390 listeners.remove(listener); 391 } 392 } 393 | Popular Tags |