1 8 9 package com.sleepycat.bind.serial; 10 11 import java.io.ByteArrayInputStream ; 12 import java.io.ByteArrayOutputStream ; 13 import java.io.IOException ; 14 import java.io.ObjectInputStream ; 15 import java.io.ObjectOutputStream ; 16 import java.io.ObjectStreamClass ; 17 import java.io.Serializable ; 18 import java.math.BigInteger ; 19 import java.util.HashMap ; 20 21 import com.sleepycat.compat.DbCompat; 22 import com.sleepycat.je.Cursor; 23 import com.sleepycat.je.CursorConfig; 24 import com.sleepycat.je.Database; 25 import com.sleepycat.je.DatabaseConfig; 26 import com.sleepycat.je.DatabaseEntry; 27 import com.sleepycat.je.DatabaseException; 28 import com.sleepycat.je.EnvironmentConfig; 29 import com.sleepycat.je.LockMode; 30 import com.sleepycat.je.OperationStatus; 31 import com.sleepycat.je.Transaction; 32 import com.sleepycat.util.RuntimeExceptionWrapper; 33 import com.sleepycat.util.UtfOps; 34 35 43 public class StoredClassCatalog implements ClassCatalog { 44 45 52 private static final byte REC_LAST_CLASS_ID = (byte) 0; 53 private static final byte REC_CLASS_FORMAT = (byte) 1; 54 private static final byte REC_CLASS_INFO = (byte) 2; 55 56 private static final byte[] LAST_CLASS_ID_KEY = {REC_LAST_CLASS_ID}; 57 58 private Database db; 59 private HashMap classMap; 60 private HashMap formatMap; 61 private LockMode writeLockMode; 62 private boolean cdbMode; 63 private boolean txnMode; 64 65 77 public StoredClassCatalog(Database database) 78 throws DatabaseException, IllegalArgumentException { 79 80 db = database; 81 DatabaseConfig dbConfig = db.getConfig(); 82 EnvironmentConfig envConfig = db.getEnvironment().getConfig(); 83 84 writeLockMode = (DbCompat.getInitializeLocking(envConfig) || 85 envConfig.getTransactional()) ? LockMode.RMW 86 : LockMode.DEFAULT; 87 cdbMode = DbCompat.getInitializeCDB(envConfig); 88 txnMode = dbConfig.getTransactional(); 89 90 if (!DbCompat.isTypeBtree(dbConfig)) { 91 throw new IllegalArgumentException ( 92 "The class catalog must be a BTREE database."); 93 } 94 if (DbCompat.getSortedDuplicates(dbConfig) || 95 DbCompat.getUnsortedDuplicates(dbConfig)) { 96 throw new IllegalArgumentException ( 97 "The class catalog database must not allow duplicates."); 98 } 99 100 105 classMap = new HashMap (); 106 formatMap = new HashMap (); 107 108 DatabaseEntry key = new DatabaseEntry(LAST_CLASS_ID_KEY); 109 DatabaseEntry data = new DatabaseEntry(); 110 if (dbConfig.getReadOnly()) { 111 112 OperationStatus status = db.get(null, key, data, null); 113 if (status != OperationStatus.SUCCESS) { 114 throw new IllegalStateException 115 ("A read-only catalog database may not be empty"); 116 } 117 } else { 118 119 data.setData(new byte[1]); 121 db.putNoOverwrite(null, key, data); 122 } 123 } 124 125 public synchronized void close() 127 throws DatabaseException { 128 129 if (db != null) { 130 db.close(); 131 } 132 db = null; 133 formatMap = null; 134 classMap = null; 135 } 136 137 public synchronized byte[] getClassID(ObjectStreamClass classFormat) 139 throws DatabaseException, ClassNotFoundException { 140 141 ClassInfo classInfo = getClassInfo(classFormat); 142 return classInfo.getClassID(); 143 } 144 145 public synchronized ObjectStreamClass getClassFormat(byte[] classID) 147 throws DatabaseException, ClassNotFoundException { 148 149 return getClassFormat(classID, new DatabaseEntry()); 150 } 151 152 157 private ObjectStreamClass getClassFormat(byte[] classID, 158 DatabaseEntry data) 159 throws DatabaseException, ClassNotFoundException { 160 161 162 163 BigInteger classIDObj = new BigInteger (classID); 164 ObjectStreamClass classFormat = 165 (ObjectStreamClass ) formatMap.get(classIDObj); 166 if (classFormat == null) { 167 168 169 170 byte[] keyBytes = new byte[classID.length + 1]; 171 keyBytes[0] = REC_CLASS_FORMAT; 172 System.arraycopy(classID, 0, keyBytes, 1, classID.length); 173 DatabaseEntry key = new DatabaseEntry(keyBytes); 174 175 176 177 OperationStatus status = db.get(null, key, data, LockMode.DEFAULT); 178 if (status != OperationStatus.SUCCESS) { 179 throw new ClassNotFoundException ("Catalog class ID not found"); 180 } 181 try { 182 ObjectInputStream ois = 183 new ObjectInputStream ( 184 new ByteArrayInputStream (data.getData(), 185 data.getOffset(), 186 data.getSize())); 187 classFormat = (ObjectStreamClass ) ois.readObject(); 188 } catch (IOException e) { 189 throw new RuntimeExceptionWrapper(e); 190 } 191 192 193 194 formatMap.put(classIDObj, classFormat); 195 } 196 return classFormat; 197 } 198 199 208 private ClassInfo getClassInfo(ObjectStreamClass classFormat) 209 throws DatabaseException, ClassNotFoundException { 210 211 215 String className = classFormat.getName(); 216 ClassInfo classInfo = (ClassInfo) classMap.get(className); 217 if (classInfo != null) { 218 return classInfo; 219 } else { 220 221 char[] nameChars = className.toCharArray(); 222 byte[] keyBytes = new byte[1 + UtfOps.getByteLength(nameChars)]; 223 keyBytes[0] = REC_CLASS_INFO; 224 UtfOps.charsToBytes(nameChars, 0, keyBytes, 1, nameChars.length); 225 DatabaseEntry key = new DatabaseEntry(keyBytes); 226 227 228 DatabaseEntry data = new DatabaseEntry(); 229 OperationStatus status = db.get(null, key, data, LockMode.DEFAULT); 230 if (status != OperationStatus.SUCCESS) { 231 235 classInfo = putClassInfo(new ClassInfo(), className, key, 236 classFormat); 237 } else { 238 242 classInfo = new ClassInfo(data); 243 DatabaseEntry formatData = new DatabaseEntry(); 244 ObjectStreamClass storedClassFormat = 245 getClassFormat(classInfo.getClassID(), formatData); 246 247 251 if (!areClassFormatsEqual(storedClassFormat, 252 getBytes(formatData), 253 classFormat)) { 254 classInfo = putClassInfo(classInfo, className, key, 255 classFormat); 256 } 257 258 259 classInfo.setClassFormat(classFormat); 260 classMap.put(className, classInfo); 261 } 262 } 263 return classInfo; 264 } 265 266 272 private ClassInfo putClassInfo(ClassInfo classInfo, 273 String className, 274 DatabaseEntry classKey, 275 ObjectStreamClass classFormat) 276 throws DatabaseException, ClassNotFoundException { 277 278 279 CursorConfig cursorConfig = null; 280 if (cdbMode) { 281 cursorConfig = new CursorConfig(); 282 DbCompat.setWriteCursor(cursorConfig, true); 283 } 284 Cursor cursor = null; 285 Transaction txn = null; 286 try { 287 if (txnMode) { 288 txn = db.getEnvironment().beginTransaction(null, null); 289 } 290 cursor = db.openCursor(txn, cursorConfig); 291 292 293 DatabaseEntry key = new DatabaseEntry(LAST_CLASS_ID_KEY); 294 DatabaseEntry data = new DatabaseEntry(); 295 OperationStatus status = cursor.getSearchKey(key, data, 296 writeLockMode); 297 if (status != OperationStatus.SUCCESS) { 298 throw new IllegalStateException ("Class ID not initialized"); 299 } 300 byte[] idBytes = getBytes(data); 301 302 303 idBytes = incrementID(idBytes); 304 data.setData(idBytes); 305 cursor.put(key, data); 306 307 311 byte[] keyBytes = new byte[1 + idBytes.length]; 312 keyBytes[0] = REC_CLASS_FORMAT; 313 System.arraycopy(idBytes, 0, keyBytes, 1, idBytes.length); 314 key.setData(keyBytes); 315 316 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 317 ObjectOutputStream oos; 318 try { 319 oos = new ObjectOutputStream (baos); 320 oos.writeObject(classFormat); 321 } catch (IOException e) { 322 throw new RuntimeExceptionWrapper(e); 323 } 324 data.setData(baos.toByteArray()); 325 326 cursor.put(key, data); 327 328 333 classInfo.setClassID(idBytes); 334 classInfo.toDbt(data); 335 336 cursor.put(classKey, data); 337 338 342 classInfo.setClassFormat(classFormat); 343 classMap.put(className, classInfo); 344 formatMap.put(new BigInteger (idBytes), classFormat); 345 return classInfo; 346 } finally { 347 if (cursor != null) { 348 cursor.close(); 349 } 350 if (txn != null) { 351 txn.commit(); 352 } 353 } 354 } 355 356 private static byte[] incrementID(byte[] key) { 357 358 BigInteger id = new BigInteger (key); 359 id = id.add(BigInteger.valueOf(1)); 360 return id.toByteArray(); 361 } 362 363 368 private static class ClassInfo implements Serializable { 369 370 private byte[] classID; 371 private transient ObjectStreamClass classFormat; 372 373 ClassInfo() { 374 } 375 376 ClassInfo(DatabaseEntry dbt) { 377 378 byte[] data = dbt.getData(); 379 int len = data[0]; 380 classID = new byte[len]; 381 System.arraycopy(data, 1, classID, 0, len); 382 } 383 384 void toDbt(DatabaseEntry dbt) { 385 386 byte[] data = new byte[1 + classID.length]; 387 data[0] = (byte) classID.length; 388 System.arraycopy(classID, 0, data, 1, classID.length); 389 dbt.setData(data); 390 } 391 392 void setClassID(byte[] classID) { 393 394 this.classID = classID; 395 } 396 397 byte[] getClassID() { 398 399 return classID; 400 } 401 402 ObjectStreamClass getClassFormat() { 403 404 return classFormat; 405 } 406 407 void setClassFormat(ObjectStreamClass classFormat) { 408 409 this.classFormat = classFormat; 410 } 411 } 412 413 418 private static boolean areClassFormatsEqual(ObjectStreamClass format1, 419 byte[] format1Bytes, 420 ObjectStreamClass format2) { 421 try { 422 if (format1Bytes == null) { format1Bytes = getObjectBytes(format1); 424 } 425 byte[] format2Bytes = getObjectBytes(format2); 426 return java.util.Arrays.equals(format2Bytes, format1Bytes); 427 } catch (IOException e) { return false; } 428 } 429 430 433 private static byte[] ZERO_LENGTH_BYTE_ARRAY = new byte[0]; 434 435 private static byte[] getBytes(DatabaseEntry dbt) { 436 byte[] b = dbt.getData(); 437 if (b == null) { 438 return null; 439 } 440 if (dbt.getOffset() == 0 && b.length == dbt.getSize()) { 441 return b; 442 } 443 int len = dbt.getSize(); 444 if (len == 0) { 445 return ZERO_LENGTH_BYTE_ARRAY; 446 } else { 447 byte[] t = new byte[len]; 448 System.arraycopy(b, dbt.getOffset(), t, 0, t.length); 449 return t; 450 } 451 } 452 453 private static byte[] getObjectBytes(Object o) 454 throws IOException { 455 456 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 457 ObjectOutputStream oos = new ObjectOutputStream (baos); 458 oos.writeObject(o); 459 return baos.toByteArray(); 460 } 461 } 462 | Popular Tags |