summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/amazon/carbonado/repo/sleepycat/BDBPanicHandler.java35
-rw-r--r--src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java2251
-rw-r--r--src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepositoryBuilder.java2305
3 files changed, 2326 insertions, 2265 deletions
diff --git a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBPanicHandler.java b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBPanicHandler.java
new file mode 100644
index 0000000..3e5aabe
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBPanicHandler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.sleepycat;
+
+/**
+ * Interface for a generic panic handler for any BDB products.
+ *
+ * @author Jesse Morgan
+ *
+ */
+public interface BDBPanicHandler {
+ /**
+ * Called when an Environment panics or an EnvironmentFailureException is thrown.
+ *
+ * @param environment The affected environment or null if the environment could not be opened.
+ * @param exception The related exception.
+ */
+ void onPanic(Object environment, Exception exception);
+}
diff --git a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java
index 07d0192..04cc87d 100644
--- a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java
+++ b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java
@@ -1,1123 +1,1128 @@
-/*
- * Copyright 2006-2012 Amazon Technologies, Inc. or its affiliates.
- * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
- * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.amazon.carbonado.repo.sleepycat;
-
-import java.io.File;
-import java.io.PrintStream;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import com.amazon.carbonado.ConfigurationException;
-import com.amazon.carbonado.Cursor;
-import com.amazon.carbonado.FetchException;
-import com.amazon.carbonado.IsolationLevel;
-import com.amazon.carbonado.MalformedArgumentException;
-import com.amazon.carbonado.PersistException;
-import com.amazon.carbonado.Repository;
-import com.amazon.carbonado.RepositoryException;
-import com.amazon.carbonado.Storable;
-import com.amazon.carbonado.Storage;
-import com.amazon.carbonado.Transaction;
-import com.amazon.carbonado.TriggerFactory;
-
-import com.amazon.carbonado.capability.Capability;
-import com.amazon.carbonado.capability.IndexInfo;
-import com.amazon.carbonado.capability.IndexInfoCapability;
-import com.amazon.carbonado.capability.ShutdownCapability;
-import com.amazon.carbonado.capability.StorableInfoCapability;
-
-import com.amazon.carbonado.info.StorableIntrospector;
-
-import com.amazon.carbonado.layout.Layout;
-import com.amazon.carbonado.layout.LayoutCapability;
-import com.amazon.carbonado.layout.LayoutFactory;
-
-import com.amazon.carbonado.qe.RepositoryAccess;
-import com.amazon.carbonado.qe.StorageAccess;
-
-import com.amazon.carbonado.raw.StorableCodecFactory;
-
-import com.amazon.carbonado.sequence.SequenceCapability;
-import com.amazon.carbonado.sequence.SequenceValueGenerator;
-import com.amazon.carbonado.sequence.SequenceValueProducer;
-
-import com.amazon.carbonado.spi.AbstractRepository;
-import com.amazon.carbonado.spi.ExceptionTransformer;
-import com.amazon.carbonado.spi.LobEngine;
-
-import com.amazon.carbonado.txn.TransactionManager;
-import com.amazon.carbonado.txn.TransactionScope;
-
-/**
- * Repository implementation backed by a Berkeley DB. Data is encoded in the
- * BDB in a specialized format, and so this repository should not be used to
- * open arbitrary Berkeley databases. BDBRepository has total schema ownership,
- * and so it updates type definitions in the storage layer automatically.
- *
- * @author Brian S O'Neill
- * @author Vidya Iyer
- * @author Nicole Deflaux
- * @author bcastill
- */
-abstract class BDBRepository<Txn> extends AbstractRepository<Txn>
- implements Repository,
- RepositoryAccess,
- IndexInfoCapability,
- HotBackupCapability,
- CheckpointCapability,
- EnvironmentCapability,
- ShutdownCapability,
- StorableInfoCapability,
- SequenceCapability,
- LayoutCapability
-{
- private final Log mLog = LogFactory.getLog(getClass());
-
- private final boolean mIsMaster;
- final Iterable<TriggerFactory> mTriggerFactories;
- private final AtomicReference<Repository> mRootRef;
- private final StorableCodecFactory mStorableCodecFactory;
- private final ExceptionTransformer mExTransformer;
- private final BDBTransactionManager<Txn> mTxnMgr;
-
- Checkpointer mCheckpointer;
- DeadlockDetector mDeadlockDetector;
-
- private final Runnable mPreShutdownHook;
- private final Runnable mPostShutdownHook;
-
- private final Object mInitialDBConfig;
- private final BDBRepositoryBuilder.DatabaseHook mDatabaseHook;
- private final Map<Class<?>, Integer> mDatabasePageSizes;
-
- final boolean mRunCheckpointer;
- final boolean mKeepOldLogFiles;
- final boolean mLogInMemory;
- final boolean mRunDeadlockDetector;
-
- final File mDataHome;
- final File mEnvHome;
- final String mSingleFileName;
- final Map<String, String> mFileNameMap;
-
- final Object mBackupLock = new Object();
- int mBackupCount = 0;
- int mIncrementalBackupCount = 0;
-
- private LayoutFactory mLayoutFactory;
-
- private LobEngine mLobEngine;
-
- /**
- * Subclass must call protected start method to fully initialize
- * BDBRepository.
- *
- * @param builder repository configuration
- * @param exTransformer transformer for exceptions
- * @throws IllegalArgumentException if name or environment home is null
- */
- @SuppressWarnings("unchecked")
- BDBRepository(AtomicReference<Repository> rootRef,
- BDBRepositoryBuilder builder,
- ExceptionTransformer exTransformer)
- throws ConfigurationException
- {
- super(builder.getName());
-
- builder.assertReady();
-
- if (exTransformer == null) {
- throw new IllegalArgumentException("Exception transformer must not be null");
- }
-
- mIsMaster = builder.isMaster();
- mTriggerFactories = builder.getTriggerFactories();
- mRootRef = rootRef;
- mExTransformer = exTransformer;
- mTxnMgr = new BDBTransactionManager<Txn>(mExTransformer, this);
-
- mRunCheckpointer = !builder.getReadOnly() && builder.getRunCheckpointer();
- mKeepOldLogFiles = builder.getKeepOldLogFiles();
- mLogInMemory = builder.getLogInMemory();
- mRunDeadlockDetector = builder.getRunDeadlockDetector();
- mStorableCodecFactory = builder.getStorableCodecFactory();
- mPreShutdownHook = builder.getPreShutdownHook();
- mPostShutdownHook = builder.getShutdownHook();
- mInitialDBConfig = builder.getInitialDatabaseConfig();
- mDatabaseHook = builder.getDatabaseHook();
- mDatabasePageSizes = builder.getDatabasePagesMap();
- mDataHome = builder.getDataHomeFile();
- mEnvHome = builder.getEnvironmentHomeFile();
- mSingleFileName = builder.getSingleFileName();
- mFileNameMap = builder.getFileNameMap();
-
- getLog().info("Opening repository \"" + getName() + '"');
- }
-
- public <S extends Storable> IndexInfo[] getIndexInfo(Class<S> storableType)
- throws RepositoryException
- {
- return ((BDBStorage) storageFor(storableType)).getIndexInfo();
- }
-
- public String[] getUserStorableTypeNames() throws RepositoryException {
- Repository metaRepo = getRootRepository();
-
- Cursor<StoredDatabaseInfo> cursor =
- metaRepo.storageFor(StoredDatabaseInfo.class)
- .query().orderBy("databaseName").fetch();
-
- try {
- ArrayList<String> names = new ArrayList<String>();
- while (cursor.hasNext()) {
- StoredDatabaseInfo info = cursor.next();
- // Ordinary user types support evolution.
- if (info.getEvolutionStrategy() != StoredDatabaseInfo.EVOLUTION_NONE) {
- names.add(info.getDatabaseName());
- }
- }
-
- return names.toArray(new String[names.size()]);
- } finally {
- cursor.close();
- }
- }
-
- public boolean isSupported(Class<Storable> type) {
- if (type == null) {
- return false;
- }
- StorableIntrospector.examine(type);
- return true;
- }
-
- public boolean isPropertySupported(Class<Storable> type, String name) {
- if (type == null || name == null) {
- return false;
- }
- return StorableIntrospector.examine(type).getAllProperties().get(name) != null;
- }
-
- @Override
- public Backup startBackup() throws RepositoryException {
- return startBackup(false);
- }
-
- @Override
- public Backup startBackup(boolean deleteOldLogFiles) throws RepositoryException {
- if (mLogInMemory) {
- throw new IllegalStateException
- ("Log files are only kept in memory and backups cannot be performed");
- }
-
- synchronized (mBackupLock) {
- int count = mBackupCount;
- if (count == 0) {
- try {
- if (deleteOldLogFiles) {
- // TODO: If backup rejects log deletion, queue up for later.
- enterBackupMode(true);
- } else {
- // Call old API for backwards compatibility.
- enterBackupMode();
- }
- } catch (Exception e) {
- throw mExTransformer.toRepositoryException(e);
- }
- }
- mBackupCount = count + 1;
-
- return new FullBackup();
- }
- }
-
- @Override
- public Backup startIncrementalBackup(long lastLogNumber)
- throws RepositoryException
- {
- return startIncrementalBackup(lastLogNumber, false);
- }
-
- @Override
- public Backup startIncrementalBackup(long lastLogNumber, boolean deleteOldLogFiles)
- throws RepositoryException
- {
- if (mLogInMemory) {
- throw new IllegalStateException
- ("Log files are only kept in memory and incremental backup cannot be performed");
- }
-
- if (lastLogNumber < 0) {
- throw new IllegalArgumentException
- ("The number of the last backup cannot be negative: " + lastLogNumber);
- }
- synchronized (mBackupLock) {
- try {
- enterIncrementalBackupMode(lastLogNumber, deleteOldLogFiles);
- ++mIncrementalBackupCount;
- } catch (Exception e) {
- throw mExTransformer.toRepositoryException(e);
- }
- }
- return new IncrementalBackup(lastLogNumber);
- }
-
- /**
- * Suspend the checkpointer until the suspension time has expired or until
- * manually resumed. If a checkpoint is in progress, this method will block
- * until it is finished. If checkpointing is disabled, calling this method
- * has no effect.
- *
- * <p>Calling this method repeatedly resets the suspension time. This
- * technique should be used by hot backup processes to ensure that its
- * failure does not leave the checkpointer permanently suspended. Each
- * invocation of suspendCheckpointer is like a lease renewal or heartbeat.
- *
- * @param suspensionTime minimum length of suspension, in milliseconds,
- * unless checkpointer is manually resumed
- */
- public void suspendCheckpointer(long suspensionTime) {
- if (mCheckpointer != null) {
- mCheckpointer.suspendCheckpointer(suspensionTime);
- }
- }
-
- /**
- * Resumes the checkpointer if it was suspended. If checkpointing is
- * disabled or if not suspended, calling this method has no effect.
- */
- public void resumeCheckpointer() {
- if (mCheckpointer != null) {
- mCheckpointer.resumeCheckpointer();
- }
- }
-
- /**
- * Forces a checkpoint to run now, even if checkpointer is suspended or
- * disabled. If a checkpoint is in progress, then this method will block
- * until it is finished, and then run another checkpoint. This method does
- * not return until the requested checkpoint has finished.
- */
- public void forceCheckpoint() throws PersistException {
- if (mCheckpointer != null) {
- mCheckpointer.forceCheckpoint();
- } else {
- try {
- env_checkpoint();
- } catch (Exception e) {
- throw toPersistException(e);
- }
- }
- }
-
- public void sync() throws PersistException {
- try {
- env_sync();
- } catch (Exception e) {
- throw toPersistException(e);
- }
- }
-
- public Repository getRootRepository() {
- return mRootRef.get();
- }
-
- public <S extends Storable> StorageAccess<S> storageAccessFor(Class<S> type)
- throws RepositoryException
- {
- return (BDBStorage<Txn, S>) storageFor(type);
- }
-
- @Override
- public Layout layoutFor(Class<? extends Storable> type)
- throws FetchException, PersistException
- {
- try {
- return ((BDBStorage) storageFor(type)).getLayout(true, mStorableCodecFactory);
- } catch (PersistException e) {
- throw e;
- } catch (RepositoryException e) {
- throw e.toFetchException();
- }
- }
-
- @Override
- public Layout layoutFor(Class<? extends Storable> type, int generation)
- throws FetchException
- {
- return mLayoutFactory.layoutFor(type, generation);
- }
-
- @Override
- protected void finalize() {
- close();
- }
-
- @Override
- protected void shutdownHook() {
- // Run any external shutdown logic that needs to happen before the
- // databases and the environment are actually closed
- if (mPreShutdownHook != null) {
- mPreShutdownHook.run();
- }
-
- // Close database handles.
- for (Storage storage : allStorage()) {
- try {
- if (storage instanceof BDBStorage) {
- ((BDBStorage) storage).close();
- }
- } catch (Throwable e) {
- getLog().error(null, e);
- }
- }
-
- // Wait for checkpointer to finish.
- if (mCheckpointer != null) {
- mCheckpointer.interrupt();
- try {
- mCheckpointer.join();
- } catch (InterruptedException e) {
- }
- }
-
- // Wait for deadlock detector to finish.
- if (mDeadlockDetector != null) {
- mDeadlockDetector.interrupt();
- try {
- mDeadlockDetector.join();
- } catch (InterruptedException e) {
- }
- }
-
- // Close environment.
- try {
- env_close();
- } catch (Throwable e) {
- getLog().error(null, e);
- }
-
- if (mPostShutdownHook != null) {
- mPostShutdownHook.run();
- }
- }
-
- @Override
- protected Log getLog() {
- return mLog;
- }
-
- @Override
- protected <S extends Storable> Storage createStorage(Class<S> type)
- throws RepositoryException
- {
- try {
- return createBDBStorage(type);
- } catch (MalformedArgumentException e) {
- throw e;
- } catch (Exception e) {
- throw toRepositoryException(e);
- }
- }
-
- @Override
- protected SequenceValueProducer createSequenceValueProducer(String name)
- throws RepositoryException
- {
- return new SequenceValueGenerator(BDBRepository.this, name);
- }
-
- /**
- * @see com.amazon.carbonado.spi.RepositoryBuilder#isMaster
- */
- boolean isMaster() {
- return mIsMaster;
- }
-
- String[] getAllDatabaseNames() throws RepositoryException {
- Repository metaRepo = getRootRepository();
-
- Cursor<StoredDatabaseInfo> cursor =
- metaRepo.storageFor(StoredDatabaseInfo.class)
- .query().orderBy("databaseName").fetch();
-
- ArrayList<String> names = new ArrayList<String>();
- // This one needs to manually added since it is the metadata db itself.
- names.add(StoredDatabaseInfo.class.getName());
-
- try {
- while (cursor.hasNext()) {
- names.add(cursor.next().getDatabaseName());
- }
- } finally {
- cursor.close();
- }
-
- return names.toArray(new String[names.size()]);
- }
-
- String getDatabaseFileName(final String dbName) {
- String singleFileName = mSingleFileName;
- if (singleFileName == null && mFileNameMap != null) {
- singleFileName = mFileNameMap.get(dbName);
- if (singleFileName == null && dbName != null) {
- singleFileName = mFileNameMap.get(null);
- }
- }
-
- String dbFileName = dbName;
-
- if (singleFileName == null) {
- if (mDatabaseHook != null) {
- dbFileName = mDatabaseHook.databaseName(dbName);
- }
- } else {
- dbFileName = singleFileName;
- }
-
- if (mDataHome != null && !mDataHome.equals(mEnvHome)) {
- dbFileName = new File(mDataHome, dbFileName).getPath();
- }
-
- return dbFileName;
- }
-
- /**
- * Returns null if name should not be used.
- */
- String getDatabaseName(String dbName) {
- if (mFileNameMap == null) {
- return null;
- }
- String name = mFileNameMap.get(dbName);
- if (name == null && dbName != null) {
- name = mFileNameMap.get(null);
- }
- if (name == null) {
- return null;
- }
- if (mDatabaseHook != null) {
- try {
- dbName = mDatabaseHook.databaseName(dbName);
- } catch (IncompatibleClassChangeError e) {
- // Method not implemented.
- }
- }
- return dbName;
- }
-
- StorableCodecFactory getStorableCodecFactory() {
- return mStorableCodecFactory;
- }
-
- LayoutFactory getLayoutFactory() throws RepositoryException {
- if (mLayoutFactory == null) {
- mLayoutFactory = new LayoutFactory(getRootRepository());
- }
- return mLayoutFactory;
- }
-
- LobEngine getLobEngine() throws RepositoryException {
- if (mLobEngine == null) {
- mLobEngine = new LobEngine(this, getRootRepository());
- }
- return mLobEngine;
- }
-
- /**
- * Returns the optional BDB specific database configuration to use
- * for all databases created.
- */
- public Object getInitialDatabaseConfig() {
- return mInitialDBConfig;
- }
-
- /**
- * Returns the desired page size for the given type, or null for default.
- */
- Integer getDatabasePageSize(Class<? extends Storable> type) {
- if (mDatabasePageSizes == null) {
- return null;
- }
- Integer size = mDatabasePageSizes.get(type);
- if (size == null && type != null) {
- size = mDatabasePageSizes.get(null);
- }
- return size;
- }
-
- void runDatabasePrepareForOpeningHook(Object database) throws RepositoryException {
- if (mDatabaseHook != null) {
- mDatabaseHook.prepareForOpening(database);
- }
- }
-
- /**
- * Start background tasks and enable auto shutdown.
- *
- * @param checkpointInterval how often to run checkpoints, in milliseconds,
- * or zero if never. Ignored if repository is read only or builder has
- * checkpoints disabled.
- * @param deadlockDetectorInterval how often to run deadlock detector, in
- * milliseconds, or zero if never. Ignored if builder has deadlock detector
- * disabled.
- *
- * @deprecated Overloaded for backwards compatiblity with older
- * CarbonadoSleepycat packages
- */
- void start(long checkpointInterval, long deadlockDetectorInterval) {
- getLog().info("Opened repository \"" + getName() + '"');
-
- if (mRunCheckpointer && checkpointInterval > 0) {
- mCheckpointer = new Checkpointer(this, checkpointInterval, 1024, 5);
- mCheckpointer.start();
- } else {
- mCheckpointer = null;
- }
-
- if (mRunDeadlockDetector && deadlockDetectorInterval > 0) {
- mDeadlockDetector = new DeadlockDetector(this, deadlockDetectorInterval);
- mDeadlockDetector.start();
- } else {
- mDeadlockDetector = null;
- }
-
- setAutoShutdownEnabled(true);
- }
-
- /**
- * Start background tasks and enable auto shutdown.
- *
- * @param checkpointInterval how often to run checkpoints, in milliseconds,
- * or zero if never. Ignored if repository is read only or builder has
- * checkpoints disabled.
- * @param deadlockDetectorInterval how often to run deadlock detector, in
- * milliseconds, or zero if never. Ignored if builder has deadlock detector
- * disabled.
- * @param builder containing additonal background task properties.
- */
- void start(long checkpointInterval, long deadlockDetectorInterval,
- BDBRepositoryBuilder builder) {
- getLog().info("Opened repository \"" + getName() + '"');
-
- if (mRunCheckpointer && checkpointInterval > 0) {
- mCheckpointer = new Checkpointer(this, checkpointInterval,
- builder.getCheckpointThresholdKB(),
- builder.getCheckpointThresholdMinutes());
- mCheckpointer.start();
- } else {
- mCheckpointer = null;
- }
-
- if (mRunDeadlockDetector && deadlockDetectorInterval > 0) {
- mDeadlockDetector = new DeadlockDetector(this, deadlockDetectorInterval);
- mDeadlockDetector.start();
- } else {
- mDeadlockDetector = null;
- }
-
- setAutoShutdownEnabled(true);
- }
-
- abstract boolean verify(PrintStream out) throws Exception;
-
- abstract IsolationLevel selectIsolationLevel(Transaction parent, IsolationLevel level);
-
- abstract Txn txn_begin(Txn parent, IsolationLevel level) throws Exception;
-
- // Subclass should override this method to actually apply the timeout
- Txn txn_begin(Txn parent, IsolationLevel level, int timeout, TimeUnit unit) throws Exception {
- return txn_begin(parent, level);
- }
-
- abstract Txn txn_begin_nowait(Txn parent, IsolationLevel level) throws Exception;
-
- abstract void txn_commit(Txn txn) throws Exception;
-
- abstract void txn_abort(Txn txn) throws Exception;
-
- /**
- * Force a checkpoint to run.
- */
- abstract void env_checkpoint() throws Exception;
-
- /**
- * Synchronously flush changes to stable storage.
- */
- abstract void env_sync() throws Exception;
-
- /**
- * @param kBytes run checkpoint if at least this many kilobytes in log
- * @param minutes run checkpoint if at least this many minutes passed since
- * last checkpoint
- */
- abstract void env_checkpoint(int kBytes, int minutes) throws Exception;
-
- /**
- * Run the deadlock detector.
- */
- abstract void env_detectDeadlocks() throws Exception;
-
- /**
- * Close the environment.
- */
- abstract void env_close() throws Exception;
-
- abstract <S extends Storable> BDBStorage<Txn, S> createBDBStorage(Class<S> type)
- throws Exception;
-
- /**
- * Called only the first time a backup is started. Old API is kept for
- * backwards compatibility.
- */
- void enterBackupMode() throws Exception {
- enterBackupMode(false);
- }
-
- /**
- * Called only the first time a backup is started.
- */
- abstract void enterBackupMode(boolean deleteOldLogFiles) throws Exception;
-
- /**
- * Called only after the last backup ends.
- */
- abstract void exitBackupMode() throws Exception;
-
- /**
- * Called only when an incremental backup is started.
- */
- abstract void enterIncrementalBackupMode(long lastLogNumber, boolean deleteOldLogFiles)
- throws Exception;
-
- /**
- * Called only after incremental backup ends.
- */
- abstract void exitIncrementalBackupMode() throws Exception;
-
- /**
- * Called only if in backup mode. Old API is kept for backwards
- * compatibility.
- */
- @Deprecated
- File[] backupFiles() throws Exception {
- return backupFiles(new long[1]);
- }
-
- @Deprecated
- File[] backupFiles(long[] newLastLogNum) throws Exception {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Called only if in backup mode.
- */
- abstract File[] backupDataFiles() throws Exception;
-
- /**
- * Called only if in backup mode.
- *
- * @param newLastLogNum reference to last log number at [0]
- */
- abstract File[] backupLogFiles(long[] newLastLogNum) throws Exception;
-
- /**
- * Called only if in incremental backup mode.
- *
- * @param newLastLogNum reference to last log number at [0]
- */
- abstract File[] incrementalBackup(long lastLogNumber, long[] newLastLogNum) throws Exception;
-
- FetchException toFetchException(Throwable e) {
- return mExTransformer.toFetchException(e);
- }
-
- PersistException toPersistException(Throwable e) {
- return mExTransformer.toPersistException(e);
- }
-
- RepositoryException toRepositoryException(Throwable e) {
- return mExTransformer.toRepositoryException(e);
- }
-
- @Override
- protected final TransactionManager<Txn> transactionManager() {
- return mTxnMgr;
- }
-
- @Override
- protected final TransactionScope<Txn> localTransactionScope() {
- return mTxnMgr.localScope();
- }
-
- /**
- * Periodically runs checkpoints on the environment.
- */
- private static class Checkpointer extends Thread {
- private final WeakReference<BDBRepository> mRepository;
- private final long mSleepInterval;
- private final int mKBytes;
- private final int mMinutes;
-
- private boolean mInProgress;
- private long mSuspendUntil = Long.MIN_VALUE;
-
- /**
- *
- * @param repository outer class
- * @param sleepInterval milliseconds to sleep before running checkpoint
- * @param kBytes run checkpoint if at least this many kilobytes in log
- * @param minutes run checkpoint if at least this many minutes passed
- * since last checkpoint
- */
- Checkpointer(BDBRepository repository, long sleepInterval, int kBytes, int minutes) {
- super(repository.getClass().getSimpleName() + " checkpointer (" +
- repository.getName() + ')');
- setDaemon(true);
- mRepository = new WeakReference<BDBRepository>(repository);
- mSleepInterval = sleepInterval;
- mKBytes = kBytes;
- mMinutes = minutes;
- }
-
- @Override
- public void run() {
- try {
- while (true) {
- synchronized (this) {
- if (!mInProgress) {
- try {
- wait(mSleepInterval);
- } catch (InterruptedException e) {
- break;
- }
- }
- }
-
- BDBRepository repository = mRepository.get();
- if (repository == null) {
- break;
- }
-
- long suspendUntil;
- synchronized (this) {
- suspendUntil = mSuspendUntil;
- }
- if (suspendUntil != Long.MIN_VALUE) {
- if (System.currentTimeMillis() < suspendUntil) {
- continue;
- }
- }
-
- Log log = repository.getLog();
-
- if (log.isDebugEnabled()) {
- log.debug("Running checkpoint on repository \"" +
- repository.getName() + '"');
- }
-
- try {
- synchronized (this) {
- mInProgress = true;
- }
- repository.env_checkpoint(mKBytes, mMinutes);
- if (log.isDebugEnabled()) {
- log.debug("Finished running checkpoint on repository \"" +
- repository.getName() + '"');
- }
- } catch (ThreadDeath e) {
- break;
- } catch (Throwable e) {
- log.error("Checkpoint failed", e);
- } finally {
- synchronized (this) {
- mInProgress = false;
- // Only wait condition is mInProgress, so okay to not call notifyAll.
- notify();
- }
- repository = null;
- }
- }
- } finally {
- synchronized (this) {
- mInProgress = false;
- // Only wait condition is mInProgress, so okay to not call notifyAll.
- notify();
- }
- }
- }
-
- /**
- * Blocks until checkpoint has finished.
- */
- synchronized void suspendCheckpointer(long suspensionTime) {
- while (mInProgress) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
-
- if (suspensionTime <= 0) {
- return;
- }
-
- long now = System.currentTimeMillis();
- long suspendUntil = now + suspensionTime;
- if (now >= 0 && suspendUntil < 0) {
- // Overflow.
- suspendUntil = Long.MAX_VALUE;
- }
- mSuspendUntil = suspendUntil;
- }
-
- synchronized void resumeCheckpointer() {
- mSuspendUntil = Long.MIN_VALUE;
- }
-
- /**
- * Blocks until checkpoint has finished.
- */
- synchronized void forceCheckpoint() throws PersistException {
- while (mInProgress) {
- try {
- wait();
- } catch (InterruptedException e) {
- return;
- }
- }
-
- BDBRepository repository = mRepository.get();
- if (repository != null) {
- try {
- repository.env_checkpoint();
- } catch (Exception e) {
- throw repository.toPersistException(e);
- }
- }
- }
- }
-
- /**
- * Periodically runs deadlock detection on the environment.
- */
- private static class DeadlockDetector extends Thread {
- private final WeakReference<BDBRepository> mRepository;
- private final long mSleepInterval;
-
- /**
- * @param repository outer class
- * @param sleepInterval milliseconds to sleep before running deadlock detection
- */
- DeadlockDetector(BDBRepository repository, long sleepInterval) {
- super(repository.getClass().getSimpleName() + " deadlock detector (" +
- repository.getName() + ')');
- setDaemon(true);
- mRepository = new WeakReference<BDBRepository>(repository);
- mSleepInterval = sleepInterval;
- }
-
- @Override
- public void run() {
- while (true) {
- try {
- Thread.sleep(mSleepInterval);
- } catch (InterruptedException e) {
- break;
- }
-
- BDBRepository repository = mRepository.get();
- if (repository == null) {
- break;
- }
-
- try {
- repository.env_detectDeadlocks();
- } catch (ThreadDeath e) {
- break;
- } catch (Throwable e) {
- repository.getLog().error("Deadlock detection failed", e);
- } finally {
- repository = null;
- }
- }
- }
- }
-
- abstract class AbstractBackup implements Backup {
- boolean mDone;
- long mFinalLogNumber;
-
- AbstractBackup() {
- mFinalLogNumber = -1;
- }
-
- @Override
- public void endBackup() throws RepositoryException {
- synchronized (mBackupLock) {
- if (mDone) {
- return;
- }
- mDone = true;
- finishBackup();
- }
- }
-
- @Override
- @Deprecated
- public File[] getFiles() throws RepositoryException {
- synchronized (mBackupLock) {
- File[] data = getDataFiles();
- File[] logs = getLogFiles();
- File[] all = new File[data.length + logs.length];
- System.arraycopy(data, 0, all, 0, data.length);
- System.arraycopy(logs, 0, all, data.length, logs.length);
- return all;
- }
- }
-
- @Override
- public File[] getDataFiles() throws RepositoryException {
- synchronized (mBackupLock) {
- if (mDone) {
- throw new IllegalStateException("Backup has ended");
- }
-
- try {
- return getDataBackupFiles();
- } catch (Exception e) {
- throw mExTransformer.toRepositoryException(e);
- }
- }
- }
-
- @Override
- public File[] getLogFiles() throws RepositoryException {
- synchronized (mBackupLock) {
- if (mDone) {
- throw new IllegalStateException("Backup has ended");
- }
-
- try {
- long[] newLastLogNum = {-1};
- File[] toReturn = getLogBackupFiles(newLastLogNum);
- mFinalLogNumber = newLastLogNum[0];
- return toReturn;
- } catch (Exception e) {
- throw mExTransformer.toRepositoryException(e);
- }
- }
- }
-
- @Override
- public long getLastLogNumber() throws RepositoryException {
- if (mFinalLogNumber < 0) {
- throw new IllegalStateException
- ("Must get files prior to retrieving the last log number");
- }
- return mFinalLogNumber;
- }
-
- abstract void finishBackup() throws RepositoryException;
-
- abstract File[] getDataBackupFiles() throws Exception;
-
- abstract File[] getLogBackupFiles(long[] newLastLogNum) throws Exception;
- }
-
- class IncrementalBackup extends AbstractBackup {
- private final long mLastLogNumber;
-
- IncrementalBackup(long lastLogNumber) {
- super();
- mLastLogNumber = lastLogNumber;
- }
-
- @Override
- void finishBackup() throws RepositoryException {
- --mIncrementalBackupCount;
-
- try {
- exitIncrementalBackupMode();
- } catch (Exception e) {
- throw mExTransformer.toRepositoryException(e);
- }
- }
-
- @Override
- File[] getDataBackupFiles() throws Exception {
- return new File[0];
- }
-
- @Override
- File[] getLogBackupFiles(long[] newLastLogNum) throws Exception {
- return incrementalBackup(mLastLogNumber, newLastLogNum);
- }
- }
-
- class FullBackup extends AbstractBackup {
- @Override
- void finishBackup() throws RepositoryException {
- int count = mBackupCount - 1;
- try {
- if (count == 0) {
- try {
- exitBackupMode();
- } catch (Exception e) {
- throw mExTransformer.toRepositoryException(e);
- }
- }
- } finally {
- mBackupCount = count;
- }
- }
-
- @Override
- File[] getDataBackupFiles() throws Exception {
- try {
- return backupDataFiles();
- } catch (AbstractMethodError e) {
- // Old API will be called for backwards compatibility in the
- // getLogBackupFiles method.
- return new File[0];
- }
- }
-
- @Override
- File[] getLogBackupFiles(long[] newLastLogNum) throws Exception {
- try {
- return backupLogFiles(newLastLogNum);
- } catch (AbstractMethodError e) {
- // Call old API for backwards compatibility.
- try {
- return backupFiles(newLastLogNum);
- } catch (AbstractMethodError e2) {
- // Call even older API for backwards compatibility.
- return backupFiles();
- }
- }
- }
- }
-}
+/*
+ * Copyright 2006-2012 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.sleepycat;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.amazon.carbonado.ConfigurationException;
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.MalformedArgumentException;
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.Transaction;
+import com.amazon.carbonado.TriggerFactory;
+
+import com.amazon.carbonado.capability.Capability;
+import com.amazon.carbonado.capability.IndexInfo;
+import com.amazon.carbonado.capability.IndexInfoCapability;
+import com.amazon.carbonado.capability.ShutdownCapability;
+import com.amazon.carbonado.capability.StorableInfoCapability;
+
+import com.amazon.carbonado.info.StorableIntrospector;
+
+import com.amazon.carbonado.layout.Layout;
+import com.amazon.carbonado.layout.LayoutCapability;
+import com.amazon.carbonado.layout.LayoutFactory;
+
+import com.amazon.carbonado.qe.RepositoryAccess;
+import com.amazon.carbonado.qe.StorageAccess;
+
+import com.amazon.carbonado.raw.StorableCodecFactory;
+
+import com.amazon.carbonado.sequence.SequenceCapability;
+import com.amazon.carbonado.sequence.SequenceValueGenerator;
+import com.amazon.carbonado.sequence.SequenceValueProducer;
+
+import com.amazon.carbonado.spi.AbstractRepository;
+import com.amazon.carbonado.spi.ExceptionTransformer;
+import com.amazon.carbonado.spi.LobEngine;
+
+import com.amazon.carbonado.txn.TransactionManager;
+import com.amazon.carbonado.txn.TransactionScope;
+
+/**
+ * Repository implementation backed by a Berkeley DB. Data is encoded in the
+ * BDB in a specialized format, and so this repository should not be used to
+ * open arbitrary Berkeley databases. BDBRepository has total schema ownership,
+ * and so it updates type definitions in the storage layer automatically.
+ *
+ * @author Brian S O'Neill
+ * @author Vidya Iyer
+ * @author Nicole Deflaux
+ * @author bcastill
+ */
+abstract class BDBRepository<Txn> extends AbstractRepository<Txn>
+ implements Repository,
+ RepositoryAccess,
+ IndexInfoCapability,
+ HotBackupCapability,
+ CheckpointCapability,
+ EnvironmentCapability,
+ ShutdownCapability,
+ StorableInfoCapability,
+ SequenceCapability,
+ LayoutCapability
+{
+ private final Log mLog = LogFactory.getLog(getClass());
+
+ private final boolean mIsMaster;
+ final Iterable<TriggerFactory> mTriggerFactories;
+ private final AtomicReference<Repository> mRootRef;
+ private final StorableCodecFactory mStorableCodecFactory;
+ private final ExceptionTransformer mExTransformer;
+ private final BDBTransactionManager<Txn> mTxnMgr;
+
+ Checkpointer mCheckpointer;
+ DeadlockDetector mDeadlockDetector;
+
+ private final Runnable mPreShutdownHook;
+ private final Runnable mPostShutdownHook;
+
+ private final Object mInitialDBConfig;
+ private final BDBRepositoryBuilder.DatabaseHook mDatabaseHook;
+ private final Map<Class<?>, Integer> mDatabasePageSizes;
+
+ final boolean mRunCheckpointer;
+ final boolean mKeepOldLogFiles;
+ final boolean mLogInMemory;
+ final boolean mRunDeadlockDetector;
+
+ final File mDataHome;
+ final File mEnvHome;
+ final String mSingleFileName;
+ final Map<String, String> mFileNameMap;
+
+ final Object mBackupLock = new Object();
+ int mBackupCount = 0;
+ int mIncrementalBackupCount = 0;
+
+ private LayoutFactory mLayoutFactory;
+
+ private LobEngine mLobEngine;
+
+ /**
+ * Subclass must call protected start method to fully initialize
+ * BDBRepository.
+ *
+ * @param builder repository configuration
+ * @param exTransformer transformer for exceptions
+ * @throws IllegalArgumentException if name or environment home is null
+ */
+ @SuppressWarnings("unchecked")
+ BDBRepository(AtomicReference<Repository> rootRef,
+ BDBRepositoryBuilder builder,
+ ExceptionTransformer exTransformer)
+ throws ConfigurationException
+ {
+ super(builder.getName());
+
+ builder.assertReady();
+
+ if (exTransformer == null) {
+ throw new IllegalArgumentException("Exception transformer must not be null");
+ }
+
+ mIsMaster = builder.isMaster();
+ mTriggerFactories = builder.getTriggerFactories();
+ mRootRef = rootRef;
+ mExTransformer = exTransformer;
+ mTxnMgr = new BDBTransactionManager<Txn>(mExTransformer, this);
+
+ mRunCheckpointer = !builder.getReadOnly() && builder.getRunCheckpointer();
+ mKeepOldLogFiles = builder.getKeepOldLogFiles();
+ mLogInMemory = builder.getLogInMemory();
+ mRunDeadlockDetector = builder.getRunDeadlockDetector();
+ mStorableCodecFactory = builder.getStorableCodecFactory();
+ mPreShutdownHook = builder.getPreShutdownHook();
+ mPostShutdownHook = builder.getShutdownHook();
+ mInitialDBConfig = builder.getInitialDatabaseConfig();
+ mDatabaseHook = builder.getDatabaseHook();
+ mDatabasePageSizes = builder.getDatabasePagesMap();
+ mDataHome = builder.getDataHomeFile();
+ mEnvHome = builder.getEnvironmentHomeFile();
+ mSingleFileName = builder.getSingleFileName();
+ mFileNameMap = builder.getFileNameMap();
+
+ getLog().info("Opening repository \"" + getName() + '"');
+ }
+
+ public ExceptionTransformer getExceptionTransformer() {
+ return mExTransformer;
+ }
+
+ public <S extends Storable> IndexInfo[] getIndexInfo(Class<S> storableType)
+ throws RepositoryException
+ {
+ return ((BDBStorage) storageFor(storableType)).getIndexInfo();
+ }
+
+ public String[] getUserStorableTypeNames() throws RepositoryException {
+ Repository metaRepo = getRootRepository();
+
+ Cursor<StoredDatabaseInfo> cursor =
+ metaRepo.storageFor(StoredDatabaseInfo.class)
+ .query().orderBy("databaseName").fetch();
+
+ try {
+ ArrayList<String> names = new ArrayList<String>();
+ while (cursor.hasNext()) {
+ StoredDatabaseInfo info = cursor.next();
+ // Ordinary user types support evolution.
+ if (info.getEvolutionStrategy() != StoredDatabaseInfo.EVOLUTION_NONE) {
+ names.add(info.getDatabaseName());
+ }
+ }
+
+ return names.toArray(new String[names.size()]);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public boolean isSupported(Class<Storable> type) {
+ if (type == null) {
+ return false;
+ }
+ StorableIntrospector.examine(type);
+ return true;
+ }
+
+ public boolean isPropertySupported(Class<Storable> type, String name) {
+ if (type == null || name == null) {
+ return false;
+ }
+ return StorableIntrospector.examine(type).getAllProperties().get(name) != null;
+ }
+
+ @Override
+ public Backup startBackup() throws RepositoryException {
+ return startBackup(false);
+ }
+
+ @Override
+ public Backup startBackup(boolean deleteOldLogFiles) throws RepositoryException {
+ if (mLogInMemory) {
+ throw new IllegalStateException
+ ("Log files are only kept in memory and backups cannot be performed");
+ }
+
+ synchronized (mBackupLock) {
+ int count = mBackupCount;
+ if (count == 0) {
+ try {
+ if (deleteOldLogFiles) {
+ // TODO: If backup rejects log deletion, queue up for later.
+ enterBackupMode(true);
+ } else {
+ // Call old API for backwards compatibility.
+ enterBackupMode();
+ }
+ } catch (Exception e) {
+ throw mExTransformer.toRepositoryException(e);
+ }
+ }
+ mBackupCount = count + 1;
+
+ return new FullBackup();
+ }
+ }
+
+ @Override
+ public Backup startIncrementalBackup(long lastLogNumber)
+ throws RepositoryException
+ {
+ return startIncrementalBackup(lastLogNumber, false);
+ }
+
+ @Override
+ public Backup startIncrementalBackup(long lastLogNumber, boolean deleteOldLogFiles)
+ throws RepositoryException
+ {
+ if (mLogInMemory) {
+ throw new IllegalStateException
+ ("Log files are only kept in memory and incremental backup cannot be performed");
+ }
+
+ if (lastLogNumber < 0) {
+ throw new IllegalArgumentException
+ ("The number of the last backup cannot be negative: " + lastLogNumber);
+ }
+ synchronized (mBackupLock) {
+ try {
+ enterIncrementalBackupMode(lastLogNumber, deleteOldLogFiles);
+ ++mIncrementalBackupCount;
+ } catch (Exception e) {
+ throw mExTransformer.toRepositoryException(e);
+ }
+ }
+ return new IncrementalBackup(lastLogNumber);
+ }
+
+ /**
+ * Suspend the checkpointer until the suspension time has expired or until
+ * manually resumed. If a checkpoint is in progress, this method will block
+ * until it is finished. If checkpointing is disabled, calling this method
+ * has no effect.
+ *
+ * <p>Calling this method repeatedly resets the suspension time. This
+ * technique should be used by hot backup processes to ensure that its
+ * failure does not leave the checkpointer permanently suspended. Each
+ * invocation of suspendCheckpointer is like a lease renewal or heartbeat.
+ *
+ * @param suspensionTime minimum length of suspension, in milliseconds,
+ * unless checkpointer is manually resumed
+ */
+ public void suspendCheckpointer(long suspensionTime) {
+ if (mCheckpointer != null) {
+ mCheckpointer.suspendCheckpointer(suspensionTime);
+ }
+ }
+
+ /**
+ * Resumes the checkpointer if it was suspended. If checkpointing is
+ * disabled or if not suspended, calling this method has no effect.
+ */
+ public void resumeCheckpointer() {
+ if (mCheckpointer != null) {
+ mCheckpointer.resumeCheckpointer();
+ }
+ }
+
+ /**
+ * Forces a checkpoint to run now, even if checkpointer is suspended or
+ * disabled. If a checkpoint is in progress, then this method will block
+ * until it is finished, and then run another checkpoint. This method does
+ * not return until the requested checkpoint has finished.
+ */
+ public void forceCheckpoint() throws PersistException {
+ if (mCheckpointer != null) {
+ mCheckpointer.forceCheckpoint();
+ } else {
+ try {
+ env_checkpoint();
+ } catch (Exception e) {
+ throw toPersistException(e);
+ }
+ }
+ }
+
+ public void sync() throws PersistException {
+ try {
+ env_sync();
+ } catch (Exception e) {
+ throw toPersistException(e);
+ }
+ }
+
+ public Repository getRootRepository() {
+ return mRootRef.get();
+ }
+
+ public <S extends Storable> StorageAccess<S> storageAccessFor(Class<S> type)
+ throws RepositoryException
+ {
+ return (BDBStorage<Txn, S>) storageFor(type);
+ }
+
+ @Override
+ public Layout layoutFor(Class<? extends Storable> type)
+ throws FetchException, PersistException
+ {
+ try {
+ return ((BDBStorage) storageFor(type)).getLayout(true, mStorableCodecFactory);
+ } catch (PersistException e) {
+ throw e;
+ } catch (RepositoryException e) {
+ throw e.toFetchException();
+ }
+ }
+
+ @Override
+ public Layout layoutFor(Class<? extends Storable> type, int generation)
+ throws FetchException
+ {
+ return mLayoutFactory.layoutFor(type, generation);
+ }
+
+ @Override
+ protected void finalize() {
+ close();
+ }
+
+ @Override
+ protected void shutdownHook() {
+ // Run any external shutdown logic that needs to happen before the
+ // databases and the environment are actually closed
+ if (mPreShutdownHook != null) {
+ mPreShutdownHook.run();
+ }
+
+ // Close database handles.
+ for (Storage storage : allStorage()) {
+ try {
+ if (storage instanceof BDBStorage) {
+ ((BDBStorage) storage).close();
+ }
+ } catch (Throwable e) {
+ getLog().error(null, e);
+ }
+ }
+
+ // Wait for checkpointer to finish.
+ if (mCheckpointer != null) {
+ mCheckpointer.interrupt();
+ try {
+ mCheckpointer.join();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ // Wait for deadlock detector to finish.
+ if (mDeadlockDetector != null) {
+ mDeadlockDetector.interrupt();
+ try {
+ mDeadlockDetector.join();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ // Close environment.
+ try {
+ env_close();
+ } catch (Throwable e) {
+ getLog().error(null, e);
+ }
+
+ if (mPostShutdownHook != null) {
+ mPostShutdownHook.run();
+ }
+ }
+
+ @Override
+ protected Log getLog() {
+ return mLog;
+ }
+
+ @Override
+ protected <S extends Storable> Storage createStorage(Class<S> type)
+ throws RepositoryException
+ {
+ try {
+ return createBDBStorage(type);
+ } catch (MalformedArgumentException e) {
+ throw e;
+ } catch (Exception e) {
+ throw toRepositoryException(e);
+ }
+ }
+
+ @Override
+ protected SequenceValueProducer createSequenceValueProducer(String name)
+ throws RepositoryException
+ {
+ return new SequenceValueGenerator(BDBRepository.this, name);
+ }
+
+ /**
+ * @see com.amazon.carbonado.spi.RepositoryBuilder#isMaster
+ */
+ boolean isMaster() {
+ return mIsMaster;
+ }
+
+ String[] getAllDatabaseNames() throws RepositoryException {
+ Repository metaRepo = getRootRepository();
+
+ Cursor<StoredDatabaseInfo> cursor =
+ metaRepo.storageFor(StoredDatabaseInfo.class)
+ .query().orderBy("databaseName").fetch();
+
+ ArrayList<String> names = new ArrayList<String>();
+ // This one needs to manually added since it is the metadata db itself.
+ names.add(StoredDatabaseInfo.class.getName());
+
+ try {
+ while (cursor.hasNext()) {
+ names.add(cursor.next().getDatabaseName());
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return names.toArray(new String[names.size()]);
+ }
+
+ String getDatabaseFileName(final String dbName) {
+ String singleFileName = mSingleFileName;
+ if (singleFileName == null && mFileNameMap != null) {
+ singleFileName = mFileNameMap.get(dbName);
+ if (singleFileName == null && dbName != null) {
+ singleFileName = mFileNameMap.get(null);
+ }
+ }
+
+ String dbFileName = dbName;
+
+ if (singleFileName == null) {
+ if (mDatabaseHook != null) {
+ dbFileName = mDatabaseHook.databaseName(dbName);
+ }
+ } else {
+ dbFileName = singleFileName;
+ }
+
+ if (mDataHome != null && !mDataHome.equals(mEnvHome)) {
+ dbFileName = new File(mDataHome, dbFileName).getPath();
+ }
+
+ return dbFileName;
+ }
+
+ /**
+ * Returns null if name should not be used.
+ */
+ String getDatabaseName(String dbName) {
+ if (mFileNameMap == null) {
+ return null;
+ }
+ String name = mFileNameMap.get(dbName);
+ if (name == null && dbName != null) {
+ name = mFileNameMap.get(null);
+ }
+ if (name == null) {
+ return null;
+ }
+ if (mDatabaseHook != null) {
+ try {
+ dbName = mDatabaseHook.databaseName(dbName);
+ } catch (IncompatibleClassChangeError e) {
+ // Method not implemented.
+ }
+ }
+ return dbName;
+ }
+
+ StorableCodecFactory getStorableCodecFactory() {
+ return mStorableCodecFactory;
+ }
+
+ LayoutFactory getLayoutFactory() throws RepositoryException {
+ if (mLayoutFactory == null) {
+ mLayoutFactory = new LayoutFactory(getRootRepository());
+ }
+ return mLayoutFactory;
+ }
+
+ LobEngine getLobEngine() throws RepositoryException {
+ if (mLobEngine == null) {
+ mLobEngine = new LobEngine(this, getRootRepository());
+ }
+ return mLobEngine;
+ }
+
+ /**
+ * Returns the optional BDB specific database configuration to use
+ * for all databases created.
+ */
+ public Object getInitialDatabaseConfig() {
+ return mInitialDBConfig;
+ }
+
+ /**
+ * Returns the desired page size for the given type, or null for default.
+ */
+ Integer getDatabasePageSize(Class<? extends Storable> type) {
+ if (mDatabasePageSizes == null) {
+ return null;
+ }
+ Integer size = mDatabasePageSizes.get(type);
+ if (size == null && type != null) {
+ size = mDatabasePageSizes.get(null);
+ }
+ return size;
+ }
+
+ void runDatabasePrepareForOpeningHook(Object database) throws RepositoryException {
+ if (mDatabaseHook != null) {
+ mDatabaseHook.prepareForOpening(database);
+ }
+ }
+
+ /**
+ * Start background tasks and enable auto shutdown.
+ *
+ * @param checkpointInterval how often to run checkpoints, in milliseconds,
+ * or zero if never. Ignored if repository is read only or builder has
+ * checkpoints disabled.
+ * @param deadlockDetectorInterval how often to run deadlock detector, in
+ * milliseconds, or zero if never. Ignored if builder has deadlock detector
+ * disabled.
+ *
+ * @deprecated Overloaded for backwards compatiblity with older
+ * CarbonadoSleepycat packages
+ */
+ void start(long checkpointInterval, long deadlockDetectorInterval) {
+ getLog().info("Opened repository \"" + getName() + '"');
+
+ if (mRunCheckpointer && checkpointInterval > 0) {
+ mCheckpointer = new Checkpointer(this, checkpointInterval, 1024, 5);
+ mCheckpointer.start();
+ } else {
+ mCheckpointer = null;
+ }
+
+ if (mRunDeadlockDetector && deadlockDetectorInterval > 0) {
+ mDeadlockDetector = new DeadlockDetector(this, deadlockDetectorInterval);
+ mDeadlockDetector.start();
+ } else {
+ mDeadlockDetector = null;
+ }
+
+ setAutoShutdownEnabled(true);
+ }
+
+ /**
+ * Start background tasks and enable auto shutdown.
+ *
+ * @param checkpointInterval how often to run checkpoints, in milliseconds,
+ * or zero if never. Ignored if repository is read only or builder has
+ * checkpoints disabled.
+ * @param deadlockDetectorInterval how often to run deadlock detector, in
+ * milliseconds, or zero if never. Ignored if builder has deadlock detector
+ * disabled.
+ * @param builder containing additonal background task properties.
+ */
+ void start(long checkpointInterval, long deadlockDetectorInterval,
+ BDBRepositoryBuilder builder) {
+ getLog().info("Opened repository \"" + getName() + '"');
+
+ if (mRunCheckpointer && checkpointInterval > 0) {
+ mCheckpointer = new Checkpointer(this, checkpointInterval,
+ builder.getCheckpointThresholdKB(),
+ builder.getCheckpointThresholdMinutes());
+ mCheckpointer.start();
+ } else {
+ mCheckpointer = null;
+ }
+
+ if (mRunDeadlockDetector && deadlockDetectorInterval > 0) {
+ mDeadlockDetector = new DeadlockDetector(this, deadlockDetectorInterval);
+ mDeadlockDetector.start();
+ } else {
+ mDeadlockDetector = null;
+ }
+
+ setAutoShutdownEnabled(true);
+ }
+
+ abstract boolean verify(PrintStream out) throws Exception;
+
+ abstract IsolationLevel selectIsolationLevel(Transaction parent, IsolationLevel level);
+
+ abstract Txn txn_begin(Txn parent, IsolationLevel level) throws Exception;
+
+ // Subclass should override this method to actually apply the timeout
+ Txn txn_begin(Txn parent, IsolationLevel level, int timeout, TimeUnit unit) throws Exception {
+ return txn_begin(parent, level);
+ }
+
+ abstract Txn txn_begin_nowait(Txn parent, IsolationLevel level) throws Exception;
+
+ abstract void txn_commit(Txn txn) throws Exception;
+
+ abstract void txn_abort(Txn txn) throws Exception;
+
+ /**
+ * Force a checkpoint to run.
+ */
+ abstract void env_checkpoint() throws Exception;
+
+ /**
+ * Synchronously flush changes to stable storage.
+ */
+ abstract void env_sync() throws Exception;
+
+ /**
+ * @param kBytes run checkpoint if at least this many kilobytes in log
+ * @param minutes run checkpoint if at least this many minutes passed since
+ * last checkpoint
+ */
+ abstract void env_checkpoint(int kBytes, int minutes) throws Exception;
+
+ /**
+ * Run the deadlock detector.
+ */
+ abstract void env_detectDeadlocks() throws Exception;
+
+ /**
+ * Close the environment.
+ */
+ abstract void env_close() throws Exception;
+
+ abstract <S extends Storable> BDBStorage<Txn, S> createBDBStorage(Class<S> type)
+ throws Exception;
+
+ /**
+ * Called only the first time a backup is started. Old API is kept for
+ * backwards compatibility.
+ */
+ void enterBackupMode() throws Exception {
+ enterBackupMode(false);
+ }
+
+ /**
+ * Called only the first time a backup is started.
+ */
+ abstract void enterBackupMode(boolean deleteOldLogFiles) throws Exception;
+
+ /**
+ * Called only after the last backup ends.
+ */
+ abstract void exitBackupMode() throws Exception;
+
+ /**
+ * Called only when an incremental backup is started.
+ */
+ abstract void enterIncrementalBackupMode(long lastLogNumber, boolean deleteOldLogFiles)
+ throws Exception;
+
+ /**
+ * Called only after incremental backup ends.
+ */
+ abstract void exitIncrementalBackupMode() throws Exception;
+
+ /**
+ * Called only if in backup mode. Old API is kept for backwards
+ * compatibility.
+ */
+ @Deprecated
+ File[] backupFiles() throws Exception {
+ return backupFiles(new long[1]);
+ }
+
+ @Deprecated
+ File[] backupFiles(long[] newLastLogNum) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Called only if in backup mode.
+ */
+ abstract File[] backupDataFiles() throws Exception;
+
+ /**
+ * Called only if in backup mode.
+ *
+ * @param newLastLogNum reference to last log number at [0]
+ */
+ abstract File[] backupLogFiles(long[] newLastLogNum) throws Exception;
+
+ /**
+ * Called only if in incremental backup mode.
+ *
+ * @param newLastLogNum reference to last log number at [0]
+ */
+ abstract File[] incrementalBackup(long lastLogNumber, long[] newLastLogNum) throws Exception;
+
+ FetchException toFetchException(Throwable e) {
+ return mExTransformer.toFetchException(e);
+ }
+
+ PersistException toPersistException(Throwable e) {
+ return mExTransformer.toPersistException(e);
+ }
+
+ RepositoryException toRepositoryException(Throwable e) {
+ return mExTransformer.toRepositoryException(e);
+ }
+
+ @Override
+ protected final TransactionManager<Txn> transactionManager() {
+ return mTxnMgr;
+ }
+
+ @Override
+ protected final TransactionScope<Txn> localTransactionScope() {
+ return mTxnMgr.localScope();
+ }
+
+ /**
+ * Periodically runs checkpoints on the environment.
+ */
+ private static class Checkpointer extends Thread {
+ private final WeakReference<BDBRepository> mRepository;
+ private final long mSleepInterval;
+ private final int mKBytes;
+ private final int mMinutes;
+
+ private boolean mInProgress;
+ private long mSuspendUntil = Long.MIN_VALUE;
+
+ /**
+ *
+ * @param repository outer class
+ * @param sleepInterval milliseconds to sleep before running checkpoint
+ * @param kBytes run checkpoint if at least this many kilobytes in log
+ * @param minutes run checkpoint if at least this many minutes passed
+ * since last checkpoint
+ */
+ Checkpointer(BDBRepository repository, long sleepInterval, int kBytes, int minutes) {
+ super(repository.getClass().getSimpleName() + " checkpointer (" +
+ repository.getName() + ')');
+ setDaemon(true);
+ mRepository = new WeakReference<BDBRepository>(repository);
+ mSleepInterval = sleepInterval;
+ mKBytes = kBytes;
+ mMinutes = minutes;
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ synchronized (this) {
+ if (!mInProgress) {
+ try {
+ wait(mSleepInterval);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ BDBRepository repository = mRepository.get();
+ if (repository == null) {
+ break;
+ }
+
+ long suspendUntil;
+ synchronized (this) {
+ suspendUntil = mSuspendUntil;
+ }
+ if (suspendUntil != Long.MIN_VALUE) {
+ if (System.currentTimeMillis() < suspendUntil) {
+ continue;
+ }
+ }
+
+ Log log = repository.getLog();
+
+ if (log.isDebugEnabled()) {
+ log.debug("Running checkpoint on repository \"" +
+ repository.getName() + '"');
+ }
+
+ try {
+ synchronized (this) {
+ mInProgress = true;
+ }
+ repository.env_checkpoint(mKBytes, mMinutes);
+ if (log.isDebugEnabled()) {
+ log.debug("Finished running checkpoint on repository \"" +
+ repository.getName() + '"');
+ }
+ } catch (ThreadDeath e) {
+ break;
+ } catch (Throwable e) {
+ log.error("Checkpoint failed", e);
+ } finally {
+ synchronized (this) {
+ mInProgress = false;
+ // Only wait condition is mInProgress, so okay to not call notifyAll.
+ notify();
+ }
+ repository = null;
+ }
+ }
+ } finally {
+ synchronized (this) {
+ mInProgress = false;
+ // Only wait condition is mInProgress, so okay to not call notifyAll.
+ notify();
+ }
+ }
+ }
+
+ /**
+ * Blocks until checkpoint has finished.
+ */
+ synchronized void suspendCheckpointer(long suspensionTime) {
+ while (mInProgress) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ if (suspensionTime <= 0) {
+ return;
+ }
+
+ long now = System.currentTimeMillis();
+ long suspendUntil = now + suspensionTime;
+ if (now >= 0 && suspendUntil < 0) {
+ // Overflow.
+ suspendUntil = Long.MAX_VALUE;
+ }
+ mSuspendUntil = suspendUntil;
+ }
+
+ synchronized void resumeCheckpointer() {
+ mSuspendUntil = Long.MIN_VALUE;
+ }
+
+ /**
+ * Blocks until checkpoint has finished.
+ */
+ synchronized void forceCheckpoint() throws PersistException {
+ while (mInProgress) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+
+ BDBRepository repository = mRepository.get();
+ if (repository != null) {
+ try {
+ repository.env_checkpoint();
+ } catch (Exception e) {
+ throw repository.toPersistException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Periodically runs deadlock detection on the environment.
+ */
+ private static class DeadlockDetector extends Thread {
+ private final WeakReference<BDBRepository> mRepository;
+ private final long mSleepInterval;
+
+ /**
+ * @param repository outer class
+ * @param sleepInterval milliseconds to sleep before running deadlock detection
+ */
+ DeadlockDetector(BDBRepository repository, long sleepInterval) {
+ super(repository.getClass().getSimpleName() + " deadlock detector (" +
+ repository.getName() + ')');
+ setDaemon(true);
+ mRepository = new WeakReference<BDBRepository>(repository);
+ mSleepInterval = sleepInterval;
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ Thread.sleep(mSleepInterval);
+ } catch (InterruptedException e) {
+ break;
+ }
+
+ BDBRepository repository = mRepository.get();
+ if (repository == null) {
+ break;
+ }
+
+ try {
+ repository.env_detectDeadlocks();
+ } catch (ThreadDeath e) {
+ break;
+ } catch (Throwable e) {
+ repository.getLog().error("Deadlock detection failed", e);
+ } finally {
+ repository = null;
+ }
+ }
+ }
+ }
+
+ abstract class AbstractBackup implements Backup {
+ boolean mDone;
+ long mFinalLogNumber;
+
+ AbstractBackup() {
+ mFinalLogNumber = -1;
+ }
+
+ @Override
+ public void endBackup() throws RepositoryException {
+ synchronized (mBackupLock) {
+ if (mDone) {
+ return;
+ }
+ mDone = true;
+ finishBackup();
+ }
+ }
+
+ @Override
+ @Deprecated
+ public File[] getFiles() throws RepositoryException {
+ synchronized (mBackupLock) {
+ File[] data = getDataFiles();
+ File[] logs = getLogFiles();
+ File[] all = new File[data.length + logs.length];
+ System.arraycopy(data, 0, all, 0, data.length);
+ System.arraycopy(logs, 0, all, data.length, logs.length);
+ return all;
+ }
+ }
+
+ @Override
+ public File[] getDataFiles() throws RepositoryException {
+ synchronized (mBackupLock) {
+ if (mDone) {
+ throw new IllegalStateException("Backup has ended");
+ }
+
+ try {
+ return getDataBackupFiles();
+ } catch (Exception e) {
+ throw mExTransformer.toRepositoryException(e);
+ }
+ }
+ }
+
+ @Override
+ public File[] getLogFiles() throws RepositoryException {
+ synchronized (mBackupLock) {
+ if (mDone) {
+ throw new IllegalStateException("Backup has ended");
+ }
+
+ try {
+ long[] newLastLogNum = {-1};
+ File[] toReturn = getLogBackupFiles(newLastLogNum);
+ mFinalLogNumber = newLastLogNum[0];
+ return toReturn;
+ } catch (Exception e) {
+ throw mExTransformer.toRepositoryException(e);
+ }
+ }
+ }
+
+ @Override
+ public long getLastLogNumber() throws RepositoryException {
+ if (mFinalLogNumber < 0) {
+ throw new IllegalStateException
+ ("Must get files prior to retrieving the last log number");
+ }
+ return mFinalLogNumber;
+ }
+
+ abstract void finishBackup() throws RepositoryException;
+
+ abstract File[] getDataBackupFiles() throws Exception;
+
+ abstract File[] getLogBackupFiles(long[] newLastLogNum) throws Exception;
+ }
+
+ class IncrementalBackup extends AbstractBackup {
+ private final long mLastLogNumber;
+
+ IncrementalBackup(long lastLogNumber) {
+ super();
+ mLastLogNumber = lastLogNumber;
+ }
+
+ @Override
+ void finishBackup() throws RepositoryException {
+ --mIncrementalBackupCount;
+
+ try {
+ exitIncrementalBackupMode();
+ } catch (Exception e) {
+ throw mExTransformer.toRepositoryException(e);
+ }
+ }
+
+ @Override
+ File[] getDataBackupFiles() throws Exception {
+ return new File[0];
+ }
+
+ @Override
+ File[] getLogBackupFiles(long[] newLastLogNum) throws Exception {
+ return incrementalBackup(mLastLogNumber, newLastLogNum);
+ }
+ }
+
+ class FullBackup extends AbstractBackup {
+ @Override
+ void finishBackup() throws RepositoryException {
+ int count = mBackupCount - 1;
+ try {
+ if (count == 0) {
+ try {
+ exitBackupMode();
+ } catch (Exception e) {
+ throw mExTransformer.toRepositoryException(e);
+ }
+ }
+ } finally {
+ mBackupCount = count;
+ }
+ }
+
+ @Override
+ File[] getDataBackupFiles() throws Exception {
+ try {
+ return backupDataFiles();
+ } catch (AbstractMethodError e) {
+ // Old API will be called for backwards compatibility in the
+ // getLogBackupFiles method.
+ return new File[0];
+ }
+ }
+
+ @Override
+ File[] getLogBackupFiles(long[] newLastLogNum) throws Exception {
+ try {
+ return backupLogFiles(newLastLogNum);
+ } catch (AbstractMethodError e) {
+ // Call old API for backwards compatibility.
+ try {
+ return backupFiles(newLastLogNum);
+ } catch (AbstractMethodError e2) {
+ // Call even older API for backwards compatibility.
+ return backupFiles();
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepositoryBuilder.java b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepositoryBuilder.java
index 82f51d9..e0cd87a 100644
--- a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepositoryBuilder.java
+++ b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepositoryBuilder.java
@@ -1,1142 +1,1163 @@
-/*
- * Copyright 2006-2012 Amazon Technologies, Inc. or its affiliates.
- * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
- * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.amazon.carbonado.repo.sleepycat;
-
-import java.lang.reflect.Constructor;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.cojen.util.ThrowUnchecked;
-
-import com.amazon.carbonado.Repository;
-import com.amazon.carbonado.RepositoryException;
-import com.amazon.carbonado.Storable;
-import com.amazon.carbonado.repo.indexed.IndexedRepositoryBuilder;
-
-import com.amazon.carbonado.raw.CompressionType;
-import com.amazon.carbonado.raw.CompressedStorableCodecFactory;
-import com.amazon.carbonado.raw.StorableCodecFactory;
-
-import com.amazon.carbonado.spi.AbstractRepositoryBuilder;
-
-import com.amazon.carbonado.ConfigurationException;
-
-/**
- * Builder and configuration options for BDBRepository.
- *
- * <pre>
- * BDBRepositoryBuilder builder = new BDBRepositoryBuilder();
- *
- * builder.setProduct("JE");
- * builder.setName("test");
- * builder.setEnvironmentHome("/tmp/testRepo");
- * builder.setTransactionWriteNoSync(true);
- *
- * Repository repo = builder.build();
- * </pre>
- *
- * <p>
- * The following extra capabilities are supported:
- * <ul>
- * <li>{@link com.amazon.carbonado.capability.IndexInfoCapability IndexInfoCapability}
- * <li>{@link com.amazon.carbonado.capability.StorableInfoCapability StorableInfoCapability}
- * <li>{@link com.amazon.carbonado.capability.ShutdownCapability ShutdownCapability}
- * <li>{@link com.amazon.carbonado.layout.LayoutCapability LayoutCapability}
- * <li>{@link com.amazon.carbonado.sequence.SequenceCapability SequenceCapability}
- * <li>{@link CheckpointCapability CheckpointCapability}
- * <li>{@link EnvironmentCapability EnvironmentCapability}
- * </ul>
- *
- * @author Brian S O'Neill
- * @author Vidya Iyer
- * @author Nicole Deflaux
- */
-public final class BDBRepositoryBuilder extends AbstractRepositoryBuilder {
-
- private static final BDBProduct DEFAULT_PRODUCT = BDBProduct.JE;
-
- private static final int DEFAULT_CHECKPOINT_INTERVAL = 10000;
-
- private String mName;
- private boolean mIsMaster = true;
- private BDBProduct mProduct = DEFAULT_PRODUCT;
- private File mEnvHome;
- private File mDataHome;
- private String mSingleFileName;
- private Map<String, String> mFileNames;
- private boolean mIndexSupport = true;
- private boolean mIndexRepairEnabled = true;
- private double mIndexThrottle = 1.0;
- private boolean mReadOnly;
- private Long mCacheSize;
- private Integer mCachePercent;
- private Integer mLogRegionSize;
- private double mLockTimeout = 0.5;
- private Integer mMaxLocks;
- private double mTxnTimeout = 300.0;
- private boolean mTxnNoSync;
- private boolean mTxnWriteNoSync;
- private Integer mTxnMaxActive = 1000;
- private Boolean mDatabasesTransactional = null;
- private boolean mReverseSplitOff;
- private Map<Class<?>, Integer> mDatabasePageSizes;
- private boolean mPrivate;
- private boolean mMultiversion;
- private boolean mLogInMemory;
- private Integer mLogFileMaxSize;
- private boolean mInitializeLogging;
- private boolean mRunFullRecovery;
- private boolean mRunCheckpointer = true;
- private int mCheckpointInterval = DEFAULT_CHECKPOINT_INTERVAL;
- private int mCheckpointThresholdKB = 1024;
- private int mCheckpointThresholdMinutes = 1;
- private boolean mKeepOldLogFiles;
- private boolean mRunDeadlockDetector = true;
- private Boolean mChecksumEnabled;
- private Object mInitialEnvConfig = null;
- private Object mInitialDBConfig = null;
- private StorableCodecFactory mStorableCodecFactory;
- private Runnable mPreShutdownHook;
- private Runnable mPostShutdownHook;
- private DatabaseHook mDatabaseHook;
- private Map<String, CompressionType> mCompressionMap;
-
- public BDBRepositoryBuilder() {
- }
-
- public Repository build(AtomicReference<Repository> rootRef) throws RepositoryException {
- if (mIndexSupport) {
- // Wrap BDBRepository with IndexedRepository.
-
- // Temporarily set to false to avoid infinite recursion.
- mIndexSupport = false;
- try {
- IndexedRepositoryBuilder ixBuilder = new IndexedRepositoryBuilder();
- ixBuilder.setWrappedRepository(this);
- ixBuilder.setMaster(isMaster());
- ixBuilder.setIndexRepairEnabled(mIndexRepairEnabled);
- ixBuilder.setIndexRepairThrottle(mIndexThrottle);
- return ixBuilder.build(rootRef);
- } finally {
- mIndexSupport = true;
- }
- }
-
- if (mStorableCodecFactory == null) {
- mStorableCodecFactory = new CompressedStorableCodecFactory(mCompressionMap);
- }
-
- assertReady();
-
- // Make environment directory if it doesn't exist.
- File homeFile = getEnvironmentHomeFile();
- if (!homeFile.exists()) {
- if (!homeFile.mkdirs()) {
- throw new RepositoryException
- ("Unable to make environment home directory: " + homeFile);
- }
- }
-
- BDBRepository repo;
-
- try {
- repo = getRepositoryConstructor().newInstance(rootRef, this);
- } catch (Exception e) {
- ThrowUnchecked.fireFirstDeclaredCause(e, RepositoryException.class);
- // Not reached.
- return null;
- }
-
- rootRef.set(repo);
- return repo;
- }
-
- /**
- * Opens the BDB environment, checks if it is corrupt, and then closes it.
- * Only one process should open the environment for verification. Expect it
- * to take a long time.
- *
- * @param out optional stream to capture any verfication errors
- * @return true if environment passes verification
- */
- public boolean verify(PrintStream out) throws RepositoryException {
- final StorableCodecFactory codecFactory = mStorableCodecFactory;
- final String name = mName;
- final boolean readOnly = mReadOnly;
- final boolean runCheckpointer = mRunCheckpointer;
- final boolean runDeadlockDetector = mRunDeadlockDetector;
- final boolean isPrivate = mPrivate;
-
- if (mName == null) {
- // Allow a dummy name for verification.
- mName = "BDB verification";
- }
-
- if (mStorableCodecFactory == null) {
- mStorableCodecFactory = new CompressedStorableCodecFactory(mCompressionMap);
- }
-
- mReadOnly = true;
- mRunCheckpointer = false;
- mRunDeadlockDetector = false;
-
- try {
- assertReady();
-
- File homeFile = getEnvironmentHomeFile();
- if (!homeFile.exists()) {
- throw new RepositoryException
- ("Environment home directory does not exist: " + homeFile);
- }
-
- AtomicReference<Repository> rootRef = new AtomicReference<Repository>();
- BDBRepository repo;
-
- try {
- repo = getRepositoryConstructor().newInstance(rootRef, this);
- } catch (Exception e) {
- ThrowUnchecked.fireFirstDeclaredCause(e, RepositoryException.class);
- // Not reached.
- return false;
- }
-
- rootRef.set(repo);
-
- try {
- return repo.verify(out);
- } catch (Exception e) {
- throw repo.toRepositoryException(e);
- } finally {
- repo.close();
- }
- } finally {
- mName = name;
- mStorableCodecFactory = codecFactory;
- mReadOnly = readOnly;
- mRunCheckpointer = runCheckpointer;
- mRunDeadlockDetector = runDeadlockDetector;
- }
- }
-
- public String getName() {
- return mName;
- }
-
- public void setName(String name) {
- mName = name;
- }
-
- public boolean isMaster() {
- return mIsMaster;
- }
-
- public void setMaster(boolean b) {
- mIsMaster = b;
- }
-
- /**
- * Sets the BDB product to use, which defaults to JE. Also supported is DB
- * and DB_HA. If not supported, an IllegalArgumentException is thrown.
- */
- public void setProduct(String product) {
- mProduct = product == null ? DEFAULT_PRODUCT : BDBProduct.forString(product);
- }
-
- /**
- * Returns the BDB product to use, which is JE by default.
- */
- public String getProduct() {
- return mProduct.toString();
- }
-
- /**
- * Sets the BDB product to use, which defaults to JE.
- */
- public void setBDBProduct(BDBProduct product) {
- mProduct = product == null ? DEFAULT_PRODUCT : product;
- }
-
- /**
- * Returns the BDB product to use, which is JE by default.
- */
- public BDBProduct getBDBProduct() {
- return mProduct;
- }
-
- /**
- * Sets the repository environment home directory, which is required.
- */
- public void setEnvironmentHomeFile(File envHome) {
- try {
- // Switch to canonical for more detailed error messages.
- envHome = envHome.getCanonicalFile();
- } catch (IOException e) {
- }
- mEnvHome = envHome;
- }
-
- /**
- * Returns the repository environment home directory.
- */
- public File getEnvironmentHomeFile() {
- return mEnvHome;
- }
-
- /**
- * Sets the repository environment home directory, which is required.
- *
- * @throws RepositoryException if environment home is not valid
- */
- public void setEnvironmentHome(String envHome) {
- setEnvironmentHomeFile(new File(envHome));
- }
-
- /**
- * Returns the repository environment home directory.
- */
- public String getEnvironmentHome() {
- return mEnvHome.getPath();
- }
-
- /**
- * By default, data files are stored relative to the environment home. Call
- * this method to override. For BDBRepositories that are log files only,
- * this configuration is ignored.
- */
- public void setDataHomeFile(File dir) {
- if (dir != null) {
- try {
- // Switch to canonical for more detailed error messages.
- dir = dir.getCanonicalFile();
- } catch (IOException e) {
- }
- }
- mDataHome = dir;
- }
-
- /**
- * Returns the optional directory to store data files. Returns null if data
- * files are expected to be relative to the environment home.
- */
- public File getDataHomeFile() {
- if (mDataHome == null) {
- return getEnvironmentHomeFile();
- }
- return mDataHome;
- }
-
- /**
- * By default, data files are stored relative to the environment home. Call
- * this method to override. For BDBRepositories that are log files only,
- * this configuration is ignored.
- */
- public void setDataHome(String dir) {
- if (dir == null) {
- mDataHome = null;
- } else {
- setDataHomeFile(new File(dir));
- }
- }
-
- /**
- * Returns the directory to store data files.
- */
- public String getDataHome() {
- return getDataHomeFile().getPath();
- }
-
- /**
- * Specify that all BDB databases should reside in one file, except for log
- * files and caches. The filename is relative to the environment home,
- * unless data directories have been specified. For BDBRepositories that
- * are log files only, this configuration is ignored.
- *
- * <p>Note: When setting this option, the storable codec factory must also
- * be changed, since the default storable codec factory is unable to
- * distinguish storable types that reside in a single database file. Call
- * setFileName instead to use built-in BDB feature for supporting multiple
- * databases in one file.
- */
- public void setSingleFileName(String filename) {
- mSingleFileName = filename;
- mFileNames = null;
- }
-
- /**
- * Returns the single file that all BDB databases should reside in.
- */
- public String getSingleFileName() {
- return mSingleFileName;
- }
-
- /**
- * Specify the file that a BDB database should reside in, except for log
- * files and caches. The filename is relative to the environment home,
- * unless data directories have been specified. For BDBRepositories that
- * are log files only, this configuration is ignored.
- *
- * @param filename BDB database filename
- * @param typeName type to store in file; if null, the file is used by default
- * for all types
- */
- public void setFileName(String filename, String typeName) {
- mSingleFileName = null;
- if (mFileNames == null) {
- mFileNames = new HashMap<String, String>();
- }
- mFileNames.put(typeName, filename);
- }
-
- Map<String, String> getFileNameMap() {
- if (mFileNames == null) {
- return null;
- }
- return new HashMap<String, String>(mFileNames);
- }
-
- /**
- * By default, user specified indexes are supported. Pass false to disable
- * this, and no indexes will be built. Another consequence of this option
- * is that no unique constraint checks will be applied to alternate keys.
- */
- public void setIndexSupport(boolean indexSupport) {
- mIndexSupport = indexSupport;
- }
-
- /**
- * Returns true if indexes are supported, which is true by default.
- */
- public boolean getIndexSupport() {
- return mIndexSupport;
- }
-
- /**
- * @see #setIndexRepairEnabled(boolean)
- *
- * @return true by default
- */
- public boolean isIndexRepairEnabled() {
- return mIndexRepairEnabled;
- }
-
- /**
- * By default, index repair is enabled. In this mode, the first time a
- * Storable type is used, new indexes are populated and old indexes are
- * removed. Until finished, access to the Storable is blocked.
- *
- * <p>When index repair is disabled, the Storable is immediately
- * available. This does have consequences, however. The set of indexes
- * available for queries is defined by the <i>intersection</i> of the old
- * and new index sets. The set of indexes that are kept up-to-date is
- * defined by the <i>union</i> of the old and new index sets.
- *
- * <p>While index repair is disabled, another process can safely repair the
- * indexes in the background. When it is complete, index repair can be
- * enabled for this repository too.
- */
- public void setIndexRepairEnabled(boolean enabled) {
- mIndexRepairEnabled = enabled;
- }
-
- /**
- * Returns the throttle parameter used when indexes are added, dropped or
- * bulk repaired. By default this value is 1.0, or maximum speed.
- */
- public double getIndexRepairThrottle() {
- return mIndexThrottle;
- }
-
- /**
- * Sets the throttle parameter used when indexes are added, dropped or bulk
- * repaired. By default this value is 1.0, or maximum speed.
- *
- * @param desiredSpeed 1.0 = perform work at full speed,
- * 0.5 = perform work at half speed, 0.0 = fully suspend work
- */
- public void setIndexRepairThrottle(double desiredSpeed) {
- mIndexThrottle = desiredSpeed;
- }
-
- /**
- * Sets the repository to read-only mode. By default, repository is opened
- * for reads and writes.
- */
- public void setReadOnly(boolean readOnly) {
- mReadOnly = readOnly;
- }
-
- /**
- * Returns true if repository should be opened read-only.
- */
- public boolean getReadOnly() {
- return mReadOnly;
- }
-
- /**
- * Set the repository cache size, in bytes. Actual BDB implementation will
- * select a suitable default if this is not set.
- */
- public void setCacheSize(long cacheSize) {
- mCacheSize = cacheSize;
- }
-
- /**
- * Set the repository cache size, in bytes. Actual BDB implementation will
- * select a suitable default if this is not set.
- *
- * @param cacheSize cache size to use, or null for default
- */
- public void setCacheSize(Long cacheSize) {
- mCacheSize = cacheSize;
- }
-
- /**
- * Returns the repository cache size, or null if default should be
- * selected.
- */
- public Long getCacheSize() {
- return mCacheSize;
- }
-
- /**
- * Set the repository log region size, in bytes.
- */
- public void setLogRegionSize(int logRegionSize) {
- mLogRegionSize = logRegionSize;
- }
-
- /**
- * Set the repository log region size, in bytes.
- */
- public void setLogRegionSize(Integer logRegionSize) {
- mLogRegionSize = logRegionSize;
- }
-
- /**
- * Returns the repository log region size, or null if the default
- * should be selected.
- */
- public Integer getLogRegionSize() {
- return mLogRegionSize;
- }
-
- /**
- * Set the percent of JVM heap used by the repository cache. Actual
- * BDB implementation will select a suitable default if this is not
- * set. This is overridden by setting an explicit cacheSize.
- */
- public void setCachePercent(int cachePercent) {
- mCachePercent = cachePercent;
- }
-
- /**
- * Set the percent of JVM heap used by the repository cache. Actual
- * BDB implementation will select a suitable default if this is not
- * set. This is overridden by setting an explicit cacheSize.
- *
- * @param cachePercent percent of JVM heap to use, or null for default
- */
- public void setCachePercent(Integer cachePercent) {
- mCachePercent = cachePercent;
- }
-
- /**
- * Returns the percent of JVM heap used by the repository cache, or
- * null if default should be selected.
- */
- public Integer getCachePercent() {
- return mCachePercent;
- }
-
- /**
- * Set the lock timeout, in seconds. Default value is 0.5 seconds.
- */
- public void setLockTimeout(double lockTimeout) {
- mLockTimeout = lockTimeout;
- }
-
- /**
- * Returns the lock timeout, in seconds.
- */
- public double getLockTimeout() {
- return mLockTimeout;
- }
-
- /**
- * Returns the lock timeout, in microseconds, limited to max long value.
- */
- public long getLockTimeoutInMicroseconds() {
- return inMicros(mLockTimeout);
- }
-
- public void setMaxLocks(Integer max) {
- mMaxLocks = max;
- }
-
- public Integer getMaxLocks() {
- return mMaxLocks;
- }
-
- /**
- * Set the transaction timeout, in seconds. Default value is 300 seconds.
- */
- public void setTransactionTimeout(double txnTimeout) {
- mTxnTimeout = txnTimeout;
- }
-
- /**
- * Returns the repository transaction timeout, in seconds.
- */
- public double getTransactionTimeout() {
- return mTxnTimeout;
- }
-
- /**
- * Returns the repository transaction timeout, in microseconds, limited to
- * max long value.
- */
- public long getTransactionTimeoutInMicroseconds() {
- return inMicros(mTxnTimeout);
- }
-
- /**
- * When true, commits are not immediately written or flushed to disk. This
- * improves performance, but there is a chance of losing the most recent
- * commits if the process is killed or if the machine crashes.
- */
- public void setTransactionNoSync(boolean noSync) {
- mTxnNoSync = noSync;
- }
-
- /**
- * Returns true if transactions are not written or flushed to disk.
- */
- public boolean getTransactionNoSync() {
- return mTxnNoSync;
- }
-
- /**
- * When true, commits are written, but they are not flushed to disk. This
- * improves performance, but there is a chance of losing the most recent
- * commits if the machine crashes.
- */
- public void setTransactionWriteNoSync(boolean noSync) {
- mTxnWriteNoSync = noSync;
- }
-
- /**
- * Returns true if transactions are not flushed to disk.
- */
- public boolean getTransactionWriteNoSync() {
- return mTxnWriteNoSync;
- }
-
- /**
- * Set the maximum number of concurrent transactions, or pass null to use
- * the default. This setting has no effect for BDB-JE.
- */
- public void setTransactionMaxActive(Integer max) {
- mTxnMaxActive = max;
- }
-
- /**
- * Returns the maximum number of concurrent transactions, or null if the
- * default is used.
- */
- public Integer getTransactionMaxActive() {
- return mTxnMaxActive;
- }
-
- /**
- * When true, allows databases to be transactional. This setting affects
- * the databases, not the environment. If this is not explicitly set, the
- * environment getTransactional is used.
- */
- public void setDatabasesTransactional(Boolean transactional) {
- mDatabasesTransactional = transactional;
- }
-
- /**
- * Returns true if the databases are configured to be transactional,
- * false if configured to not be transactional, null if this override was never set
- */
- public Boolean getDatabasesTransactional() {
- return mDatabasesTransactional;
- }
-
- /**
- * Pass true to disable reverse split of B-tree nodes to reduce deadlocks.
- * This setting has no effect for BDB-JE.
- */
- public void setReverseSplitOff(boolean off) {
- mReverseSplitOff = off;
- }
-
- public boolean isReverseSplitOff() {
- return mReverseSplitOff;
- }
-
- /**
- * Sets the desired page size for a given type. If not specified, the page
- * size applies to all types.
- */
- public void setDatabasePageSize(Integer bytes, Class<? extends Storable> type) {
- if (mDatabasePageSizes == null) {
- mDatabasePageSizes = new HashMap<Class<?>, Integer>();
- }
- mDatabasePageSizes.put(type, bytes);
- }
-
- Map<Class<?>, Integer> getDatabasePagesMap() {
- if (mDatabasePageSizes == null) {
- return null;
- }
- return new HashMap<Class<?>, Integer>(mDatabasePageSizes);
- }
-
- /**
- * When true, BDB environment cannot be shared by other processes, and
- * region files are not created. By default, environment is shared, if
- * supported.
- */
- public void setPrivate(boolean b) {
- mPrivate = b;
- }
-
- /**
- * Returns true if BDB environment is private. By default, environment is
- * shared, if supported.
- */
- public boolean isPrivate() {
- return mPrivate;
- }
-
- /**
- * Set true to enable multiversion concurrency control (MVCC) on BDB
- * environment. This enables snapshot isolation, and is it is not supported
- * by all BDB products and versions.
- */
- public void setMultiversion(boolean multiversion) {
- mMultiversion = multiversion;
- }
-
- /**
- * Returns false by default because multiversion concurrency control (MVCC)
- * is not enabled.
- */
- public boolean isMultiversion() {
- return mMultiversion;
- }
-
- /**
- * Set true to store transaction logs in memory only instead of persistent
- * storage. For BDB products which are entirely log based, no records are
- * ever persisted.
- */
- public void setLogInMemory(boolean logInMemory) {
- mLogInMemory = logInMemory;
- }
-
- /**
- * Returns false by default, indicating that transaction logs are persisted.
- */
- public boolean getLogInMemory() {
- return mLogInMemory;
- }
-
- /**
- * Set the maximum transaction log file size for the BDB environment.
- */
- public void setLogFileMaxSize(Integer sizeInBytes) {
- mLogFileMaxSize = sizeInBytes;
- }
-
- /**
- * Returns null if default size will be used.
- */
- public Integer getLogFileMaxSize() {
- return mLogFileMaxSize;
- }
-
- /**
- * Ensure the transaction logging sub-system is initialized, which is
- * usually implied.
- */
- public void setInitializeLogging(boolean b) {
- mInitializeLogging = b;
- }
-
- public boolean getInitializeLogging() {
- return mInitializeLogging;
- }
-
- /**
- * Pass true to override the default and run a full (catastrophic) recovery
- * when environment is opened. This setting has no effect for BDB-JE.
- */
- public void setRunFullRecovery(boolean runRecovery) {
- mRunFullRecovery = runRecovery;
- }
-
- /**
- * Returns true if a full (catastrophic) recovery should be performed when
- * environment is opened.
- */
- public boolean getRunFullRecovery() {
- return mRunFullRecovery;
- }
-
- /**
- * Disable automatic checkpointing of database if another process is
- * responsible for that. The false setting is implied for read-only
- * databases.
- */
- public void setRunCheckpointer(boolean runCheckpointer) {
- mRunCheckpointer = runCheckpointer;
- }
-
- /**
- * Returns true if checkpointer is run automatically.
- */
- public boolean getRunCheckpointer() {
- return mRunCheckpointer;
- }
-
- /**
- * Set the interval to run checkpoints. This setting is ignored if the
- * checkpointer is not configured to run.
- *
- * @param intervalMillis interval between checkpoints, in milliseconds
- */
- public void setCheckpointInterval(int intervalMillis) {
- mCheckpointInterval = intervalMillis;
- }
-
- /**
- * @return interval between checkpoints, in milliseconds
- */
- public int getCheckpointInterval() {
- return mCheckpointInterval;
- }
-
- /**
- * Set the size threshold to run checkpoints. This setting is ignored if
- * the checkpointer is not configured to run. Default value is 1024 KB.
- *
- * <p>Checkpoint threshold is only used by Carbonado's built-in
- * checkpointer, and is ignored when using BDB-JE.
- *
- * @param thresholdKB run checkpoint if at least this many kilobytes in log
- */
- public void setCheckpointThresholdKB(int thresholdKB) {
- mCheckpointThresholdKB = thresholdKB;
- }
-
- /**
- * @return run checkpoint if at least this many kilobytes in log
- */
- public int getCheckpointThresholdKB() {
- return mCheckpointThresholdKB;
- }
-
- /**
- * Set the time threshold to run checkpoints. This setting is ignored if
- * the checkpointer is not configured to run. Default value is 1 minute.
- *
- * <p>Checkpoint threshold is only used by Carbonado's built-in
- * checkpointer, and is ignored when using BDB-JE.
- *
- * @param thresholdMinutes run checkpoint if at least this many minutes
- * passed since last checkpoint
- */
- public void setCheckpointThresholdMinutes(int thresholdMinutes) {
- mCheckpointThresholdMinutes = thresholdMinutes;
- }
-
- /**
- * @return run checkpoint if at least this many minutes passed since last
- * checkpoint
- */
- public int getCheckpointThresholdMinutes() {
- return mCheckpointThresholdMinutes;
- }
-
- /**
- * By default, transaction log files are deleted when no longer needed.
- * Keeping log files can be used for incremental backups or for diagnosing
- * problems. If using BDB-JE, old log files are renamed with a ".del"
- * extension. If using BDB-core, the db_archive utility is required for
- * identifying old log files.
- */
- public void setKeepOldLogFiles(boolean keep) {
- mKeepOldLogFiles = keep;
- }
-
- /**
- * Returns false by default.
- */
- public boolean getKeepOldLogFiles() {
- return mKeepOldLogFiles;
- }
-
- /**
- * Disable automatic deadlock detection of database if another thread is
- * responsible for that.
- */
- public void setRunDeadlockDetector(boolean runDeadlockDetector) {
- mRunDeadlockDetector = runDeadlockDetector;
- }
-
- /**
- * Returns true if deadlock detector is configured to run.
- */
- public boolean getRunDeadlockDetector() {
- return mRunDeadlockDetector;
- }
-
- /**
- * When true, enable checksum verification of pages read into the cache
- * from the backing filestore. By default checksum is enabled for BDB-JE,
- * and disabled for BDB-C.
- */
- public void setChecksumEnabled(Boolean checksumEnabled) {
- mChecksumEnabled = checksumEnabled;
- }
-
- /**
- * Returns true if checksum verification is enabled. Returns null if the
- * BDB default is used.
- */
- public Boolean getChecksumEnabled() {
- return mChecksumEnabled;
- }
-
- /**
- * Optionally set the BDB specific environment configuration to
- * use. The builder will verify that needed configuration values are set.
- */
- public void setInitialEnvironmentConfig(Object envConfig) {
- mInitialEnvConfig = envConfig;
- }
-
- /**
- * Returns the optional BDB specific environment configuration to use.
- */
- public Object getInitialEnvironmentConfig() {
- return mInitialEnvConfig;
- }
-
- /**
- * Optionally set the BDB specific database configuration to use
- * for all databases created. The storage will verify that needed
- * configuration values are set.
- */
- public void setInitialDatabaseConfig(Object dbConfig) {
- mInitialDBConfig = dbConfig;
- }
-
- /**
- * Returns the optional BDB specific database configuration to use
- * for all databases created.
- */
- public Object getInitialDatabaseConfig() {
- return mInitialDBConfig;
- }
-
- /**
- * Override the default storable codec factory.
- */
- public void setStorableCodecFactory(StorableCodecFactory factory) {
- mStorableCodecFactory = factory;
- }
-
- /**
- * Returns the storable codec factory used.
- */
- public StorableCodecFactory getStorableCodecFactory() {
- return mStorableCodecFactory;
- }
-
- /**
- * Sets a callback to be invoked before the repository has finished running
- * its own shutdown hooks. This method is also invoked when repository is
- * manually closed.
- */
- public void setPreShutdownHook(Runnable hook) {
- mPreShutdownHook = hook;
- }
-
- /**
- * Returns the custom shutdown hook that runs before the repository has
- * finished running its own shutdown hooks, or null if none.
- */
- public Runnable getPreShutdownHook() {
- return mPreShutdownHook;
- }
-
- /**
- * Sets a callback to be invoked after repository has finished running its
- * own shutdown hooks. This method is also invoked when repository is
- * manually closed.
- */
- public void setShutdownHook(Runnable hook) {
- mPostShutdownHook = hook;
- }
-
- /**
- * Returns the custom shutdown hook that runs after the repository has
- * finished running its own shutdown hooks, or null if none.
- */
- public Runnable getShutdownHook() {
- return mPostShutdownHook;
- }
-
- /**
- * Sets a hook to be called whenever a database is opened.
- */
- public void setDatabaseHook(DatabaseHook hook) {
- mDatabaseHook = hook;
- }
-
- /**
- * Returns the custom open database hook, or null if none.
- */
- public DatabaseHook getDatabaseHook() {
- return mDatabaseHook;
- }
-
- /**
- * Set the compressor for the given class, overriding a custom StorableCodecFactory.
-
- * @param type Storable to compress.
- * @param compressionType String representation of type of
- * compression. Available options are "NONE" for no compression or "GZIP"
- * for gzip compression
- */
- public void setCompressor(String type, String compressionType) {
- mStorableCodecFactory = null;
- compressionType = compressionType.toUpperCase();
- if (mCompressionMap == null) {
- mCompressionMap = new HashMap<String, CompressionType>();
- }
- CompressionType compressionEnum = CompressionType.valueOf(compressionType);
- if (compressionEnum != null) {
- mCompressionMap.put(type, compressionEnum);
- }
- }
-
- /**
- * Return the compressor used for the given storable.
- * @param type Storable to compress
- * @return String representation of the type of compression used. Available options are "NONE"
- * for no compression and "GZIP" for gzip compression.
- */
- public String getCompressor(String type) {
- if (mCompressionMap == null) {
- return null;
- }
-
- return mCompressionMap.get(type).toString();
- }
-
- private long inMicros(double seconds) {
- if (seconds >= Long.MAX_VALUE) {
- return Long.MAX_VALUE;
- }
- if (seconds <= 0 || Double.isNaN(seconds)) {
- return 0L;
- }
- return (long) (seconds * 1000000);
- }
-
- @Override
- public void errorCheck(Collection<String> messages) throws ConfigurationException {
- super.errorCheck(messages);
-
- checkClass: {
- Exception error;
- try {
- getRepositoryConstructor();
- break checkClass;
- } catch (ClassCastException e) {
- error = e;
- } catch (ClassNotFoundException e) {
- error = e;
- } catch (NoSuchMethodException e) {
- error = e;
- }
- messages.add("BDB product \"" + getProduct() + "\" not supported: " + error);
- }
-
- File envHome = getEnvironmentHomeFile();
- if (envHome == null) {
- messages.add("environmentHome missing");
- } else {
- if (envHome.exists() && !envHome.isDirectory()) {
- messages.add("environment home is not a directory: " + envHome);
- }
- }
- }
-
- /**
- * Looks up appropriate repository via reflection, whose name is derived
- * from the BDB product string.
- */
- @SuppressWarnings("unchecked")
- private Constructor<BDBRepository> getRepositoryConstructor()
- throws ClassCastException, ClassNotFoundException, NoSuchMethodException
- {
- String packageName;
- {
- String thisClassName = getClass().getName();
- packageName = thisClassName.substring(0, thisClassName.lastIndexOf('.'));
- }
- String className = packageName + '.' + getBDBProduct().name() + "_Repository";
- Class repoClass = Class.forName(className);
- if (BDBRepository.class.isAssignableFrom(repoClass)) {
- return repoClass.getDeclaredConstructor
- (AtomicReference.class, BDBRepositoryBuilder.class);
- }
- throw new ClassCastException("Not an instance of BDBRepository: " + repoClass.getName());
- }
-
- public static interface DatabaseHook {
- /**
- * Returns an appropriate database name for the given type. Simply
- * return the type name as-is to support default behavior.
- */
- String databaseName(String typeName);
-
- /**
- * Called right before database is opened.
- *
- * @param db reference to database or config - actual type depends on BDB
- * implementation.
- */
- void prepareForOpening(Object db) throws RepositoryException;
- }
-}
+/*
+ * Copyright 2006-2012 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.sleepycat;
+
+import java.lang.reflect.Constructor;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.cojen.util.ThrowUnchecked;
+
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.repo.indexed.IndexedRepositoryBuilder;
+
+import com.amazon.carbonado.raw.CompressionType;
+import com.amazon.carbonado.raw.CompressedStorableCodecFactory;
+import com.amazon.carbonado.raw.StorableCodecFactory;
+
+import com.amazon.carbonado.spi.AbstractRepositoryBuilder;
+
+import com.amazon.carbonado.ConfigurationException;
+
+/**
+ * Builder and configuration options for BDBRepository.
+ *
+ * <pre>
+ * BDBRepositoryBuilder builder = new BDBRepositoryBuilder();
+ *
+ * builder.setProduct("JE");
+ * builder.setName("test");
+ * builder.setEnvironmentHome("/tmp/testRepo");
+ * builder.setTransactionWriteNoSync(true);
+ *
+ * Repository repo = builder.build();
+ * </pre>
+ *
+ * <p>
+ * The following extra capabilities are supported:
+ * <ul>
+ * <li>{@link com.amazon.carbonado.capability.IndexInfoCapability IndexInfoCapability}
+ * <li>{@link com.amazon.carbonado.capability.StorableInfoCapability StorableInfoCapability}
+ * <li>{@link com.amazon.carbonado.capability.ShutdownCapability ShutdownCapability}
+ * <li>{@link com.amazon.carbonado.layout.LayoutCapability LayoutCapability}
+ * <li>{@link com.amazon.carbonado.sequence.SequenceCapability SequenceCapability}
+ * <li>{@link CheckpointCapability CheckpointCapability}
+ * <li>{@link EnvironmentCapability EnvironmentCapability}
+ * </ul>
+ *
+ * @author Brian S O'Neill
+ * @author Vidya Iyer
+ * @author Nicole Deflaux
+ */
+public final class BDBRepositoryBuilder extends AbstractRepositoryBuilder {
+
+ private static final BDBProduct DEFAULT_PRODUCT = BDBProduct.JE;
+
+ private static final int DEFAULT_CHECKPOINT_INTERVAL = 10000;
+
+ private String mName;
+ private boolean mIsMaster = true;
+ private BDBProduct mProduct = DEFAULT_PRODUCT;
+ private File mEnvHome;
+ private File mDataHome;
+ private String mSingleFileName;
+ private Map<String, String> mFileNames;
+ private boolean mIndexSupport = true;
+ private boolean mIndexRepairEnabled = true;
+ private double mIndexThrottle = 1.0;
+ private boolean mReadOnly;
+ private Long mCacheSize;
+ private Integer mCachePercent;
+ private Integer mLogRegionSize;
+ private double mLockTimeout = 0.5;
+ private Integer mMaxLocks;
+ private double mTxnTimeout = 300.0;
+ private boolean mTxnNoSync;
+ private boolean mTxnWriteNoSync;
+ private Integer mTxnMaxActive = 1000;
+ private Boolean mDatabasesTransactional = null;
+ private boolean mReverseSplitOff;
+ private Map<Class<?>, Integer> mDatabasePageSizes;
+ private boolean mPrivate;
+ private boolean mMultiversion;
+ private boolean mLogInMemory;
+ private Integer mLogFileMaxSize;
+ private boolean mInitializeLogging;
+ private boolean mRunFullRecovery;
+ private boolean mRunCheckpointer = true;
+ private int mCheckpointInterval = DEFAULT_CHECKPOINT_INTERVAL;
+ private int mCheckpointThresholdKB = 1024;
+ private int mCheckpointThresholdMinutes = 1;
+ private boolean mKeepOldLogFiles;
+ private boolean mRunDeadlockDetector = true;
+ private Boolean mChecksumEnabled;
+ private Object mInitialEnvConfig = null;
+ private Object mInitialDBConfig = null;
+ private StorableCodecFactory mStorableCodecFactory;
+ private Runnable mPreShutdownHook;
+ private Runnable mPostShutdownHook;
+ private DatabaseHook mDatabaseHook;
+ private Map<String, CompressionType> mCompressionMap;
+
+ private BDBPanicHandler mPanicHandler;
+
+ public BDBRepositoryBuilder() {
+ }
+
+ public Repository build(AtomicReference<Repository> rootRef) throws RepositoryException {
+ if (mIndexSupport) {
+ // Wrap BDBRepository with IndexedRepository.
+
+ // Temporarily set to false to avoid infinite recursion.
+ mIndexSupport = false;
+ try {
+ IndexedRepositoryBuilder ixBuilder = new IndexedRepositoryBuilder();
+ ixBuilder.setWrappedRepository(this);
+ ixBuilder.setMaster(isMaster());
+ ixBuilder.setIndexRepairEnabled(mIndexRepairEnabled);
+ ixBuilder.setIndexRepairThrottle(mIndexThrottle);
+ return ixBuilder.build(rootRef);
+ } finally {
+ mIndexSupport = true;
+ }
+ }
+
+ if (mStorableCodecFactory == null) {
+ mStorableCodecFactory = new CompressedStorableCodecFactory(mCompressionMap);
+ }
+
+ assertReady();
+
+ // Make environment directory if it doesn't exist.
+ File homeFile = getEnvironmentHomeFile();
+ if (!homeFile.exists()) {
+ if (!homeFile.mkdirs()) {
+ throw new RepositoryException
+ ("Unable to make environment home directory: " + homeFile);
+ }
+ }
+
+ BDBRepository repo;
+
+ try {
+ repo = getRepositoryConstructor().newInstance(rootRef, this);
+ } catch (Exception e) {
+ ThrowUnchecked.fireFirstDeclaredCause(e, RepositoryException.class);
+ // Not reached.
+ return null;
+ }
+
+ rootRef.set(repo);
+ return repo;
+ }
+
+ /**
+ * Opens the BDB environment, checks if it is corrupt, and then closes it.
+ * Only one process should open the environment for verification. Expect it
+ * to take a long time.
+ *
+ * @param out optional stream to capture any verfication errors
+ * @return true if environment passes verification
+ */
+ public boolean verify(PrintStream out) throws RepositoryException {
+ final StorableCodecFactory codecFactory = mStorableCodecFactory;
+ final String name = mName;
+ final boolean readOnly = mReadOnly;
+ final boolean runCheckpointer = mRunCheckpointer;
+ final boolean runDeadlockDetector = mRunDeadlockDetector;
+ final boolean isPrivate = mPrivate;
+
+ if (mName == null) {
+ // Allow a dummy name for verification.
+ mName = "BDB verification";
+ }
+
+ if (mStorableCodecFactory == null) {
+ mStorableCodecFactory = new CompressedStorableCodecFactory(mCompressionMap);
+ }
+
+ mReadOnly = true;
+ mRunCheckpointer = false;
+ mRunDeadlockDetector = false;
+
+ try {
+ assertReady();
+
+ File homeFile = getEnvironmentHomeFile();
+ if (!homeFile.exists()) {
+ throw new RepositoryException
+ ("Environment home directory does not exist: " + homeFile);
+ }
+
+ AtomicReference<Repository> rootRef = new AtomicReference<Repository>();
+ BDBRepository repo;
+
+ try {
+ repo = getRepositoryConstructor().newInstance(rootRef, this);
+ } catch (Exception e) {
+ ThrowUnchecked.fireFirstDeclaredCause(e, RepositoryException.class);
+ // Not reached.
+ return false;
+ }
+
+ rootRef.set(repo);
+
+ try {
+ return repo.verify(out);
+ } catch (Exception e) {
+ throw repo.toRepositoryException(e);
+ } finally {
+ repo.close();
+ }
+ } finally {
+ mName = name;
+ mStorableCodecFactory = codecFactory;
+ mReadOnly = readOnly;
+ mRunCheckpointer = runCheckpointer;
+ mRunDeadlockDetector = runDeadlockDetector;
+ }
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setName(String name) {
+ mName = name;
+ }
+
+ public boolean isMaster() {
+ return mIsMaster;
+ }
+
+ public void setMaster(boolean b) {
+ mIsMaster = b;
+ }
+
+ /**
+ * Sets the BDB product to use, which defaults to JE. Also supported is DB
+ * and DB_HA. If not supported, an IllegalArgumentException is thrown.
+ */
+ public void setProduct(String product) {
+ mProduct = product == null ? DEFAULT_PRODUCT : BDBProduct.forString(product);
+ }
+
+ /**
+ * Returns the BDB product to use, which is JE by default.
+ */
+ public String getProduct() {
+ return mProduct.toString();
+ }
+
+ /**
+ * Sets the BDB product to use, which defaults to JE.
+ */
+ public void setBDBProduct(BDBProduct product) {
+ mProduct = product == null ? DEFAULT_PRODUCT : product;
+ }
+
+ /**
+ * Returns the BDB product to use, which is JE by default.
+ */
+ public BDBProduct getBDBProduct() {
+ return mProduct;
+ }
+
+ /**
+ * Sets the repository environment home directory, which is required.
+ */
+ public void setEnvironmentHomeFile(File envHome) {
+ try {
+ // Switch to canonical for more detailed error messages.
+ envHome = envHome.getCanonicalFile();
+ } catch (IOException e) {
+ }
+ mEnvHome = envHome;
+ }
+
+ /**
+ * Returns the repository environment home directory.
+ */
+ public File getEnvironmentHomeFile() {
+ return mEnvHome;
+ }
+
+ /**
+ * Sets the repository environment home directory, which is required.
+ *
+ * @throws RepositoryException if environment home is not valid
+ */
+ public void setEnvironmentHome(String envHome) {
+ setEnvironmentHomeFile(new File(envHome));
+ }
+
+ /**
+ * Returns the repository environment home directory.
+ */
+ public String getEnvironmentHome() {
+ return mEnvHome.getPath();
+ }
+
+ /**
+ * By default, data files are stored relative to the environment home. Call
+ * this method to override. For BDBRepositories that are log files only,
+ * this configuration is ignored.
+ */
+ public void setDataHomeFile(File dir) {
+ if (dir != null) {
+ try {
+ // Switch to canonical for more detailed error messages.
+ dir = dir.getCanonicalFile();
+ } catch (IOException e) {
+ }
+ }
+ mDataHome = dir;
+ }
+
+ /**
+ * Returns the optional directory to store data files. Returns null if data
+ * files are expected to be relative to the environment home.
+ */
+ public File getDataHomeFile() {
+ if (mDataHome == null) {
+ return getEnvironmentHomeFile();
+ }
+ return mDataHome;
+ }
+
+ /**
+ * By default, data files are stored relative to the environment home. Call
+ * this method to override. For BDBRepositories that are log files only,
+ * this configuration is ignored.
+ */
+ public void setDataHome(String dir) {
+ if (dir == null) {
+ mDataHome = null;
+ } else {
+ setDataHomeFile(new File(dir));
+ }
+ }
+
+ /**
+ * Returns the directory to store data files.
+ */
+ public String getDataHome() {
+ return getDataHomeFile().getPath();
+ }
+
+ /**
+ * Specify that all BDB databases should reside in one file, except for log
+ * files and caches. The filename is relative to the environment home,
+ * unless data directories have been specified. For BDBRepositories that
+ * are log files only, this configuration is ignored.
+ *
+ * <p>Note: When setting this option, the storable codec factory must also
+ * be changed, since the default storable codec factory is unable to
+ * distinguish storable types that reside in a single database file. Call
+ * setFileName instead to use built-in BDB feature for supporting multiple
+ * databases in one file.
+ */
+ public void setSingleFileName(String filename) {
+ mSingleFileName = filename;
+ mFileNames = null;
+ }
+
+ /**
+ * Returns the single file that all BDB databases should reside in.
+ */
+ public String getSingleFileName() {
+ return mSingleFileName;
+ }
+
+ /**
+ * Specify the file that a BDB database should reside in, except for log
+ * files and caches. The filename is relative to the environment home,
+ * unless data directories have been specified. For BDBRepositories that
+ * are log files only, this configuration is ignored.
+ *
+ * @param filename BDB database filename
+ * @param typeName type to store in file; if null, the file is used by default
+ * for all types
+ */
+ public void setFileName(String filename, String typeName) {
+ mSingleFileName = null;
+ if (mFileNames == null) {
+ mFileNames = new HashMap<String, String>();
+ }
+ mFileNames.put(typeName, filename);
+ }
+
+ Map<String, String> getFileNameMap() {
+ if (mFileNames == null) {
+ return null;
+ }
+ return new HashMap<String, String>(mFileNames);
+ }
+
+ /**
+ * By default, user specified indexes are supported. Pass false to disable
+ * this, and no indexes will be built. Another consequence of this option
+ * is that no unique constraint checks will be applied to alternate keys.
+ */
+ public void setIndexSupport(boolean indexSupport) {
+ mIndexSupport = indexSupport;
+ }
+
+ /**
+ * Returns true if indexes are supported, which is true by default.
+ */
+ public boolean getIndexSupport() {
+ return mIndexSupport;
+ }
+
+ /**
+ * @see #setIndexRepairEnabled(boolean)
+ *
+ * @return true by default
+ */
+ public boolean isIndexRepairEnabled() {
+ return mIndexRepairEnabled;
+ }
+
+ /**
+ * By default, index repair is enabled. In this mode, the first time a
+ * Storable type is used, new indexes are populated and old indexes are
+ * removed. Until finished, access to the Storable is blocked.
+ *
+ * <p>When index repair is disabled, the Storable is immediately
+ * available. This does have consequences, however. The set of indexes
+ * available for queries is defined by the <i>intersection</i> of the old
+ * and new index sets. The set of indexes that are kept up-to-date is
+ * defined by the <i>union</i> of the old and new index sets.
+ *
+ * <p>While index repair is disabled, another process can safely repair the
+ * indexes in the background. When it is complete, index repair can be
+ * enabled for this repository too.
+ */
+ public void setIndexRepairEnabled(boolean enabled) {
+ mIndexRepairEnabled = enabled;
+ }
+
+ /**
+ * Returns the throttle parameter used when indexes are added, dropped or
+ * bulk repaired. By default this value is 1.0, or maximum speed.
+ */
+ public double getIndexRepairThrottle() {
+ return mIndexThrottle;
+ }
+
+ /**
+ * Sets the throttle parameter used when indexes are added, dropped or bulk
+ * repaired. By default this value is 1.0, or maximum speed.
+ *
+ * @param desiredSpeed 1.0 = perform work at full speed,
+ * 0.5 = perform work at half speed, 0.0 = fully suspend work
+ */
+ public void setIndexRepairThrottle(double desiredSpeed) {
+ mIndexThrottle = desiredSpeed;
+ }
+
+ /**
+ * Sets the repository to read-only mode. By default, repository is opened
+ * for reads and writes.
+ */
+ public void setReadOnly(boolean readOnly) {
+ mReadOnly = readOnly;
+ }
+
+ /**
+ * Returns true if repository should be opened read-only.
+ */
+ public boolean getReadOnly() {
+ return mReadOnly;
+ }
+
+ /**
+ * Set the repository cache size, in bytes. Actual BDB implementation will
+ * select a suitable default if this is not set.
+ */
+ public void setCacheSize(long cacheSize) {
+ mCacheSize = cacheSize;
+ }
+
+ /**
+ * Set the repository cache size, in bytes. Actual BDB implementation will
+ * select a suitable default if this is not set.
+ *
+ * @param cacheSize cache size to use, or null for default
+ */
+ public void setCacheSize(Long cacheSize) {
+ mCacheSize = cacheSize;
+ }
+
+ /**
+ * Returns the repository cache size, or null if default should be
+ * selected.
+ */
+ public Long getCacheSize() {
+ return mCacheSize;
+ }
+
+ /**
+ * Set the repository log region size, in bytes.
+ */
+ public void setLogRegionSize(int logRegionSize) {
+ mLogRegionSize = logRegionSize;
+ }
+
+ /**
+ * Set the repository log region size, in bytes.
+ */
+ public void setLogRegionSize(Integer logRegionSize) {
+ mLogRegionSize = logRegionSize;
+ }
+
+ /**
+ * Returns the repository log region size, or null if the default
+ * should be selected.
+ */
+ public Integer getLogRegionSize() {
+ return mLogRegionSize;
+ }
+
+ /**
+ * Set the percent of JVM heap used by the repository cache. Actual
+ * BDB implementation will select a suitable default if this is not
+ * set. This is overridden by setting an explicit cacheSize.
+ */
+ public void setCachePercent(int cachePercent) {
+ mCachePercent = cachePercent;
+ }
+
+ /**
+ * Set the percent of JVM heap used by the repository cache. Actual
+ * BDB implementation will select a suitable default if this is not
+ * set. This is overridden by setting an explicit cacheSize.
+ *
+ * @param cachePercent percent of JVM heap to use, or null for default
+ */
+ public void setCachePercent(Integer cachePercent) {
+ mCachePercent = cachePercent;
+ }
+
+ /**
+ * Returns the percent of JVM heap used by the repository cache, or
+ * null if default should be selected.
+ */
+ public Integer getCachePercent() {
+ return mCachePercent;
+ }
+
+ /**
+ * Set the lock timeout, in seconds. Default value is 0.5 seconds.
+ */
+ public void setLockTimeout(double lockTimeout) {
+ mLockTimeout = lockTimeout;
+ }
+
+ /**
+ * Returns the lock timeout, in seconds.
+ */
+ public double getLockTimeout() {
+ return mLockTimeout;
+ }
+
+ /**
+ * Returns the lock timeout, in microseconds, limited to max long value.
+ */
+ public long getLockTimeoutInMicroseconds() {
+ return inMicros(mLockTimeout);
+ }
+
+ public void setMaxLocks(Integer max) {
+ mMaxLocks = max;
+ }
+
+ public Integer getMaxLocks() {
+ return mMaxLocks;
+ }
+
+ /**
+ * Set the transaction timeout, in seconds. Default value is 300 seconds.
+ */
+ public void setTransactionTimeout(double txnTimeout) {
+ mTxnTimeout = txnTimeout;
+ }
+
+ /**
+ * Returns the repository transaction timeout, in seconds.
+ */
+ public double getTransactionTimeout() {
+ return mTxnTimeout;
+ }
+
+ /**
+ * Returns the repository transaction timeout, in microseconds, limited to
+ * max long value.
+ */
+ public long getTransactionTimeoutInMicroseconds() {
+ return inMicros(mTxnTimeout);
+ }
+
+ /**
+ * When true, commits are not immediately written or flushed to disk. This
+ * improves performance, but there is a chance of losing the most recent
+ * commits if the process is killed or if the machine crashes.
+ */
+ public void setTransactionNoSync(boolean noSync) {
+ mTxnNoSync = noSync;
+ }
+
+ /**
+ * Returns true if transactions are not written or flushed to disk.
+ */
+ public boolean getTransactionNoSync() {
+ return mTxnNoSync;
+ }
+
+ /**
+ * When true, commits are written, but they are not flushed to disk. This
+ * improves performance, but there is a chance of losing the most recent
+ * commits if the machine crashes.
+ */
+ public void setTransactionWriteNoSync(boolean noSync) {
+ mTxnWriteNoSync = noSync;
+ }
+
+ /**
+ * Returns true if transactions are not flushed to disk.
+ */
+ public boolean getTransactionWriteNoSync() {
+ return mTxnWriteNoSync;
+ }
+
+ /**
+ * Set the maximum number of concurrent transactions, or pass null to use
+ * the default. This setting has no effect for BDB-JE.
+ */
+ public void setTransactionMaxActive(Integer max) {
+ mTxnMaxActive = max;
+ }
+
+ /**
+ * Returns the maximum number of concurrent transactions, or null if the
+ * default is used.
+ */
+ public Integer getTransactionMaxActive() {
+ return mTxnMaxActive;
+ }
+
+ /**
+ * When true, allows databases to be transactional. This setting affects
+ * the databases, not the environment. If this is not explicitly set, the
+ * environment getTransactional is used.
+ */
+ public void setDatabasesTransactional(Boolean transactional) {
+ mDatabasesTransactional = transactional;
+ }
+
+ /**
+ * Returns true if the databases are configured to be transactional,
+ * false if configured to not be transactional, null if this override was never set
+ */
+ public Boolean getDatabasesTransactional() {
+ return mDatabasesTransactional;
+ }
+
+ /**
+ * Pass true to disable reverse split of B-tree nodes to reduce deadlocks.
+ * This setting has no effect for BDB-JE.
+ */
+ public void setReverseSplitOff(boolean off) {
+ mReverseSplitOff = off;
+ }
+
+ public boolean isReverseSplitOff() {
+ return mReverseSplitOff;
+ }
+
+ /**
+ * Sets the desired page size for a given type. If not specified, the page
+ * size applies to all types.
+ */
+ public void setDatabasePageSize(Integer bytes, Class<? extends Storable> type) {
+ if (mDatabasePageSizes == null) {
+ mDatabasePageSizes = new HashMap<Class<?>, Integer>();
+ }
+ mDatabasePageSizes.put(type, bytes);
+ }
+
+ Map<Class<?>, Integer> getDatabasePagesMap() {
+ if (mDatabasePageSizes == null) {
+ return null;
+ }
+ return new HashMap<Class<?>, Integer>(mDatabasePageSizes);
+ }
+
+ /**
+ * When true, BDB environment cannot be shared by other processes, and
+ * region files are not created. By default, environment is shared, if
+ * supported.
+ */
+ public void setPrivate(boolean b) {
+ mPrivate = b;
+ }
+
+ /**
+ * Returns true if BDB environment is private. By default, environment is
+ * shared, if supported.
+ */
+ public boolean isPrivate() {
+ return mPrivate;
+ }
+
+ /**
+ * Set true to enable multiversion concurrency control (MVCC) on BDB
+ * environment. This enables snapshot isolation, and is it is not supported
+ * by all BDB products and versions.
+ */
+ public void setMultiversion(boolean multiversion) {
+ mMultiversion = multiversion;
+ }
+
+ /**
+ * Returns false by default because multiversion concurrency control (MVCC)
+ * is not enabled.
+ */
+ public boolean isMultiversion() {
+ return mMultiversion;
+ }
+
+ /**
+ * Set true to store transaction logs in memory only instead of persistent
+ * storage. For BDB products which are entirely log based, no records are
+ * ever persisted.
+ */
+ public void setLogInMemory(boolean logInMemory) {
+ mLogInMemory = logInMemory;
+ }
+
+ /**
+ * Returns false by default, indicating that transaction logs are persisted.
+ */
+ public boolean getLogInMemory() {
+ return mLogInMemory;
+ }
+
+ /**
+ * Set the maximum transaction log file size for the BDB environment.
+ */
+ public void setLogFileMaxSize(Integer sizeInBytes) {
+ mLogFileMaxSize = sizeInBytes;
+ }
+
+ /**
+ * Returns null if default size will be used.
+ */
+ public Integer getLogFileMaxSize() {
+ return mLogFileMaxSize;
+ }
+
+ /**
+ * Ensure the transaction logging sub-system is initialized, which is
+ * usually implied.
+ */
+ public void setInitializeLogging(boolean b) {
+ mInitializeLogging = b;
+ }
+
+ public boolean getInitializeLogging() {
+ return mInitializeLogging;
+ }
+
+ /**
+ * Pass true to override the default and run a full (catastrophic) recovery
+ * when environment is opened. This setting has no effect for BDB-JE.
+ */
+ public void setRunFullRecovery(boolean runRecovery) {
+ mRunFullRecovery = runRecovery;
+ }
+
+ /**
+ * Returns true if a full (catastrophic) recovery should be performed when
+ * environment is opened.
+ */
+ public boolean getRunFullRecovery() {
+ return mRunFullRecovery;
+ }
+
+ /**
+ * Disable automatic checkpointing of database if another process is
+ * responsible for that. The false setting is implied for read-only
+ * databases.
+ */
+ public void setRunCheckpointer(boolean runCheckpointer) {
+ mRunCheckpointer = runCheckpointer;
+ }
+
+ /**
+ * Returns true if checkpointer is run automatically.
+ */
+ public boolean getRunCheckpointer() {
+ return mRunCheckpointer;
+ }
+
+ /**
+ * Set the interval to run checkpoints. This setting is ignored if the
+ * checkpointer is not configured to run.
+ *
+ * @param intervalMillis interval between checkpoints, in milliseconds
+ */
+ public void setCheckpointInterval(int intervalMillis) {
+ mCheckpointInterval = intervalMillis;
+ }
+
+ /**
+ * @return interval between checkpoints, in milliseconds
+ */
+ public int getCheckpointInterval() {
+ return mCheckpointInterval;
+ }
+
+ /**
+ * Set the size threshold to run checkpoints. This setting is ignored if
+ * the checkpointer is not configured to run. Default value is 1024 KB.
+ *
+ * <p>Checkpoint threshold is only used by Carbonado's built-in
+ * checkpointer, and is ignored when using BDB-JE.
+ *
+ * @param thresholdKB run checkpoint if at least this many kilobytes in log
+ */
+ public void setCheckpointThresholdKB(int thresholdKB) {
+ mCheckpointThresholdKB = thresholdKB;
+ }
+
+ /**
+ * @return run checkpoint if at least this many kilobytes in log
+ */
+ public int getCheckpointThresholdKB() {
+ return mCheckpointThresholdKB;
+ }
+
+ /**
+ * Set the time threshold to run checkpoints. This setting is ignored if
+ * the checkpointer is not configured to run. Default value is 1 minute.
+ *
+ * <p>Checkpoint threshold is only used by Carbonado's built-in
+ * checkpointer, and is ignored when using BDB-JE.
+ *
+ * @param thresholdMinutes run checkpoint if at least this many minutes
+ * passed since last checkpoint
+ */
+ public void setCheckpointThresholdMinutes(int thresholdMinutes) {
+ mCheckpointThresholdMinutes = thresholdMinutes;
+ }
+
+ /**
+ * @return run checkpoint if at least this many minutes passed since last
+ * checkpoint
+ */
+ public int getCheckpointThresholdMinutes() {
+ return mCheckpointThresholdMinutes;
+ }
+
+ /**
+ * By default, transaction log files are deleted when no longer needed.
+ * Keeping log files can be used for incremental backups or for diagnosing
+ * problems. If using BDB-JE, old log files are renamed with a ".del"
+ * extension. If using BDB-core, the db_archive utility is required for
+ * identifying old log files.
+ */
+ public void setKeepOldLogFiles(boolean keep) {
+ mKeepOldLogFiles = keep;
+ }
+
+ /**
+ * Returns false by default.
+ */
+ public boolean getKeepOldLogFiles() {
+ return mKeepOldLogFiles;
+ }
+
+ /**
+ * Disable automatic deadlock detection of database if another thread is
+ * responsible for that.
+ */
+ public void setRunDeadlockDetector(boolean runDeadlockDetector) {
+ mRunDeadlockDetector = runDeadlockDetector;
+ }
+
+ /**
+ * Returns true if deadlock detector is configured to run.
+ */
+ public boolean getRunDeadlockDetector() {
+ return mRunDeadlockDetector;
+ }
+
+ /**
+ * When true, enable checksum verification of pages read into the cache
+ * from the backing filestore. By default checksum is enabled for BDB-JE,
+ * and disabled for BDB-C.
+ */
+ public void setChecksumEnabled(Boolean checksumEnabled) {
+ mChecksumEnabled = checksumEnabled;
+ }
+
+ /**
+ * Returns true if checksum verification is enabled. Returns null if the
+ * BDB default is used.
+ */
+ public Boolean getChecksumEnabled() {
+ return mChecksumEnabled;
+ }
+
+ /**
+ * Optionally set the BDB specific environment configuration to
+ * use. The builder will verify that needed configuration values are set.
+ */
+ public void setInitialEnvironmentConfig(Object envConfig) {
+ mInitialEnvConfig = envConfig;
+ }
+
+ /**
+ * Returns the optional BDB specific environment configuration to use.
+ */
+ public Object getInitialEnvironmentConfig() {
+ return mInitialEnvConfig;
+ }
+
+ /**
+ * Optionally set the BDB specific database configuration to use
+ * for all databases created. The storage will verify that needed
+ * configuration values are set.
+ */
+ public void setInitialDatabaseConfig(Object dbConfig) {
+ mInitialDBConfig = dbConfig;
+ }
+
+ /**
+ * Returns the optional BDB specific database configuration to use
+ * for all databases created.
+ */
+ public Object getInitialDatabaseConfig() {
+ return mInitialDBConfig;
+ }
+
+ /**
+ * Override the default storable codec factory.
+ */
+ public void setStorableCodecFactory(StorableCodecFactory factory) {
+ mStorableCodecFactory = factory;
+ }
+
+ /**
+ * Returns the storable codec factory used.
+ */
+ public StorableCodecFactory getStorableCodecFactory() {
+ return mStorableCodecFactory;
+ }
+
+ /**
+ * Sets a callback to be invoked before the repository has finished running
+ * its own shutdown hooks. This method is also invoked when repository is
+ * manually closed.
+ */
+ public void setPreShutdownHook(Runnable hook) {
+ mPreShutdownHook = hook;
+ }
+
+ /**
+ * Returns the custom shutdown hook that runs before the repository has
+ * finished running its own shutdown hooks, or null if none.
+ */
+ public Runnable getPreShutdownHook() {
+ return mPreShutdownHook;
+ }
+
+ /**
+ * Sets a callback to be invoked after repository has finished running its
+ * own shutdown hooks. This method is also invoked when repository is
+ * manually closed.
+ */
+ public void setShutdownHook(Runnable hook) {
+ mPostShutdownHook = hook;
+ }
+
+ /**
+ * Returns the custom shutdown hook that runs after the repository has
+ * finished running its own shutdown hooks, or null if none.
+ */
+ public Runnable getShutdownHook() {
+ return mPostShutdownHook;
+ }
+
+ /**
+ * Sets a hook to be called whenever a database is opened.
+ */
+ public void setDatabaseHook(DatabaseHook hook) {
+ mDatabaseHook = hook;
+ }
+
+ /**
+ * Returns the custom open database hook, or null if none.
+ */
+ public DatabaseHook getDatabaseHook() {
+ return mDatabaseHook;
+ }
+
+ /**
+ * Set the compressor for the given class, overriding a custom StorableCodecFactory.
+
+ * @param type Storable to compress.
+ * @param compressionType String representation of type of
+ * compression. Available options are "NONE" for no compression or "GZIP"
+ * for gzip compression
+ */
+ public void setCompressor(String type, String compressionType) {
+ mStorableCodecFactory = null;
+ compressionType = compressionType.toUpperCase();
+ if (mCompressionMap == null) {
+ mCompressionMap = new HashMap<String, CompressionType>();
+ }
+ CompressionType compressionEnum = CompressionType.valueOf(compressionType);
+ if (compressionEnum != null) {
+ mCompressionMap.put(type, compressionEnum);
+ }
+ }
+
+ /**
+ * Return the compressor used for the given storable.
+ * @param type Storable to compress
+ * @return String representation of the type of compression used. Available options are "NONE"
+ * for no compression and "GZIP" for gzip compression.
+ */
+ public String getCompressor(String type) {
+ if (mCompressionMap == null) {
+ return null;
+ }
+
+ return mCompressionMap.get(type).toString();
+ }
+
+ /**
+ * Set the handler to call if the database panics.
+ *
+ * @param handler
+ */
+ public void setPanicHandler(BDBPanicHandler handler) {
+ mPanicHandler = handler;
+ }
+
+ /**
+ * Return the panic handler to call if the database panics.
+ *
+ * @return The BDBPanicHandler or null if unset.
+ */
+ public BDBPanicHandler getPanicHandler() {
+ return mPanicHandler;
+ }
+
+ private long inMicros(double seconds) {
+ if (seconds >= Long.MAX_VALUE) {
+ return Long.MAX_VALUE;
+ }
+ if (seconds <= 0 || Double.isNaN(seconds)) {
+ return 0L;
+ }
+ return (long) (seconds * 1000000);
+ }
+
+ @Override
+ public void errorCheck(Collection<String> messages) throws ConfigurationException {
+ super.errorCheck(messages);
+
+ checkClass: {
+ Exception error;
+ try {
+ getRepositoryConstructor();
+ break checkClass;
+ } catch (ClassCastException e) {
+ error = e;
+ } catch (ClassNotFoundException e) {
+ error = e;
+ } catch (NoSuchMethodException e) {
+ error = e;
+ }
+ messages.add("BDB product \"" + getProduct() + "\" not supported: " + error);
+ }
+
+ File envHome = getEnvironmentHomeFile();
+ if (envHome == null) {
+ messages.add("environmentHome missing");
+ } else {
+ if (envHome.exists() && !envHome.isDirectory()) {
+ messages.add("environment home is not a directory: " + envHome);
+ }
+ }
+ }
+
+ /**
+ * Looks up appropriate repository via reflection, whose name is derived
+ * from the BDB product string.
+ */
+ @SuppressWarnings("unchecked")
+ private Constructor<BDBRepository> getRepositoryConstructor()
+ throws ClassCastException, ClassNotFoundException, NoSuchMethodException
+ {
+ String packageName;
+ {
+ String thisClassName = getClass().getName();
+ packageName = thisClassName.substring(0, thisClassName.lastIndexOf('.'));
+ }
+ String className = packageName + '.' + getBDBProduct().name() + "_Repository";
+ Class repoClass = Class.forName(className);
+ if (BDBRepository.class.isAssignableFrom(repoClass)) {
+ return repoClass.getDeclaredConstructor
+ (AtomicReference.class, BDBRepositoryBuilder.class);
+ }
+ throw new ClassCastException("Not an instance of BDBRepository: " + repoClass.getName());
+ }
+
+ public static interface DatabaseHook {
+ /**
+ * Returns an appropriate database name for the given type. Simply
+ * return the type name as-is to support default behavior.
+ */
+ String databaseName(String typeName);
+
+ /**
+ * Called right before database is opened.
+ *
+ * @param db reference to database or config - actual type depends on BDB
+ * implementation.
+ */
+ void prepareForOpening(Object db) throws RepositoryException;
+ }
+}
+