📄 storedclasscatalog.java
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2000-2006 * Oracle Corporation. All rights reserved. * * $Id: StoredClassCatalog.java,v 12.4 2006/08/31 18:14:05 bostic Exp $ */package com.sleepycat.bind.serial;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.ObjectStreamClass;import java.io.Serializable;import java.math.BigInteger;import java.util.HashMap;import com.sleepycat.compat.DbCompat;import com.sleepycat.db.Cursor;import com.sleepycat.db.CursorConfig;import com.sleepycat.db.Database;import com.sleepycat.db.DatabaseConfig;import com.sleepycat.db.DatabaseEntry;import com.sleepycat.db.DatabaseException;import com.sleepycat.db.EnvironmentConfig;import com.sleepycat.db.LockMode;import com.sleepycat.db.OperationStatus;import com.sleepycat.db.Transaction;import com.sleepycat.util.RuntimeExceptionWrapper;import com.sleepycat.util.UtfOps;/** * A <code>ClassCatalog</code> that is stored in a <code>Database</code>. * * <p>A single <code>StoredClassCatalog</code> object is normally used along * with a set of databases that stored serialized objects.</p> * * @author Mark Hayes */public class StoredClassCatalog implements ClassCatalog { /* * Record types ([key] [data]): * * [0] [next class ID] * [1 / class ID] [ObjectStreamClass (class format)] * [2 / class name] [ClassInfo (has 8 byte class ID)] */ private static final byte REC_LAST_CLASS_ID = (byte) 0; private static final byte REC_CLASS_FORMAT = (byte) 1; private static final byte REC_CLASS_INFO = (byte) 2; private static final byte[] LAST_CLASS_ID_KEY = {REC_LAST_CLASS_ID}; private Database db; private HashMap classMap; private HashMap formatMap; private LockMode writeLockMode; private boolean cdbMode; private boolean txnMode; /** * Creates a catalog based on a given database. To save resources, only a * single catalog object should be used for each unique catalog database. * * @param database an open database to use as the class catalog. It must * be a BTREE database and must not allow duplicates. * * @throws DatabaseException if an error occurs accessing the database. * * @throws IllegalArgumentException if the database is not a BTREE database * or if it configured to allow duplicates. */ public StoredClassCatalog(Database database) throws DatabaseException, IllegalArgumentException { db = database; DatabaseConfig dbConfig = db.getConfig(); EnvironmentConfig envConfig = db.getEnvironment().getConfig(); writeLockMode = (DbCompat.getInitializeLocking(envConfig) || envConfig.getTransactional()) ? LockMode.RMW : LockMode.DEFAULT; cdbMode = DbCompat.getInitializeCDB(envConfig); txnMode = dbConfig.getTransactional(); if (!DbCompat.isTypeBtree(dbConfig)) { throw new IllegalArgumentException( "The class catalog must be a BTREE database."); } if (DbCompat.getSortedDuplicates(dbConfig) || DbCompat.getUnsortedDuplicates(dbConfig)) { throw new IllegalArgumentException( "The class catalog database must not allow duplicates."); } /* * Create the class format and class info maps. Note that these are not * synchronized, and therefore the methods that use them are * synchronized. */ classMap = new HashMap(); formatMap = new HashMap(); DatabaseEntry key = new DatabaseEntry(LAST_CLASS_ID_KEY); DatabaseEntry data = new DatabaseEntry(); if (dbConfig.getReadOnly()) { /* Check that the class ID record exists. */ OperationStatus status = db.get(null, key, data, null); if (status != OperationStatus.SUCCESS) { throw new IllegalStateException ("A read-only catalog database may not be empty"); } } else { /* Add the initial class ID record if it doesn't exist. */ data.setData(new byte[1]); // zero ID /* Use putNoOverwrite to avoid phantoms. */ db.putNoOverwrite(null, key, data); } } // javadoc is inherited public synchronized void close() throws DatabaseException { if (db != null) { db.close(); } db = null; formatMap = null; classMap = null; } // javadoc is inherited public synchronized byte[] getClassID(ObjectStreamClass classFormat) throws DatabaseException, ClassNotFoundException { ClassInfo classInfo = getClassInfo(classFormat); return classInfo.getClassID(); } // javadoc is inherited public synchronized ObjectStreamClass getClassFormat(byte[] classID) throws DatabaseException, ClassNotFoundException { return getClassFormat(classID, new DatabaseEntry()); } /** * Internal function for getting the class format. Allows passing the * DatabaseEntry object for the data, so the bytes of the class format can * be examined afterwards. */ private ObjectStreamClass getClassFormat(byte[] classID, DatabaseEntry data) throws DatabaseException, ClassNotFoundException { /* First check the map and, if found, add class info to the map. */ BigInteger classIDObj = new BigInteger(classID); ObjectStreamClass classFormat = (ObjectStreamClass) formatMap.get(classIDObj); if (classFormat == null) { /* Make the class format key. */ byte[] keyBytes = new byte[classID.length + 1]; keyBytes[0] = REC_CLASS_FORMAT; System.arraycopy(classID, 0, keyBytes, 1, classID.length); DatabaseEntry key = new DatabaseEntry(keyBytes); /* Read the class format. */ OperationStatus status = db.get(null, key, data, LockMode.DEFAULT); if (status != OperationStatus.SUCCESS) { throw new ClassNotFoundException("Catalog class ID not found"); } try { ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(data.getData(), data.getOffset(), data.getSize())); classFormat = (ObjectStreamClass) ois.readObject(); } catch (IOException e) { throw new RuntimeExceptionWrapper(e); } /* Update the class format map. */ formatMap.put(classIDObj, classFormat); } return classFormat; } /** * Get the ClassInfo for a given class name, adding it and its * ObjectStreamClass to the database if they are not already present, and * caching both of them using the class info and class format maps. When a * class is first loaded from the database, the stored ObjectStreamClass is * compared to the current ObjectStreamClass loaded by the Java class * loader; if they are different, a new class ID is assigned for the * current format. */ private ClassInfo getClassInfo(ObjectStreamClass classFormat) throws DatabaseException, ClassNotFoundException { /* * First check for a cached copy of the class info, which if * present always contains the class format object */ String className = classFormat.getName(); ClassInfo classInfo = (ClassInfo) classMap.get(className); if (classInfo != null) { return classInfo; } else { /* Make class info key. */ char[] nameChars = className.toCharArray(); byte[] keyBytes = new byte[1 + UtfOps.getByteLength(nameChars)]; keyBytes[0] = REC_CLASS_INFO; UtfOps.charsToBytes(nameChars, 0, keyBytes, 1, nameChars.length); DatabaseEntry key = new DatabaseEntry(keyBytes); /* Read class info. */ DatabaseEntry data = new DatabaseEntry(); OperationStatus status = db.get(null, key, data, LockMode.DEFAULT); if (status != OperationStatus.SUCCESS) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -