/* * This file is part of the HyperGraphDB source distribution. This is copyrighted software. For permitted * uses, licensing options and redistribution, please see the LicensingInformation file at the root level of * the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb.storage.bje; import java.util.Comparator; import org.hypergraphdb.HGBidirectionalIndex; import org.hypergraphdb.HGException; import org.hypergraphdb.HGRandomAccessResult; import org.hypergraphdb.HGSearchResult; import org.hypergraphdb.storage.ByteArrayConverter; import org.hypergraphdb.transaction.HGTransactionManager; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.LockMode; import com.sleepycat.je.OperationStatus; import com.sleepycat.je.SecondaryConfig; import com.sleepycat.je.SecondaryCursor; import com.sleepycat.je.SecondaryDatabase; @SuppressWarnings("unchecked") public class DefaultBiIndexImpl<KeyType, ValueType> extends DefaultIndexImpl<KeyType, ValueType> implements HGBidirectionalIndex<KeyType, ValueType> { private static final String SECONDARY_DB_NAME_PREFIX = DB_NAME_PREFIX + "_secondary"; private DatabaseEntry dummy = new DatabaseEntry(); private SecondaryDatabase secondaryDb = null; public DefaultBiIndexImpl(String indexName, BJEStorageImplementation storage, HGTransactionManager transactionManager, ByteArrayConverter<KeyType> keyConverter, ByteArrayConverter<ValueType> valueConverter, Comparator<?> comparator) { super(indexName, storage, transactionManager, keyConverter, valueConverter, comparator); } public void open() { sort_duplicates = false; super.open(); try { SecondaryConfig dbConfig = new SecondaryConfig(); dbConfig.setAllowCreate(true); if (storage.getBerkleyEnvironment().getConfig().getTransactional()) { dbConfig.setTransactional(true); } dbConfig.setKeyCreator(PlainSecondaryKeyCreator.getInstance()); dbConfig.setSortedDuplicates(true); secondaryDb = storage.getBerkleyEnvironment().openSecondaryDatabase(null, SECONDARY_DB_NAME_PREFIX + name, db, dbConfig); } catch (Throwable t) { throw new HGException("While attempting to open index ;" + name + "': " + t.toString(), t); } } public void close() { HGException exception = null; try { super.close(); } catch (HGException ex) { exception = ex; } if (secondaryDb == null) return; // Attempt to close secondary database even if there was an exception // during the close of the primary. try { secondaryDb.close(); } catch (Throwable t) { if (exception == null) exception = new HGException(t); } finally { secondaryDb = null; } if (exception != null) throw exception; } public boolean isOpen() { return super.isOpen() && secondaryDb != null; } public void addEntry(KeyType key, ValueType value) { checkOpen(); DatabaseEntry dbkey = new DatabaseEntry(keyConverter.toByteArray(key)); DatabaseEntry dbvalue = new DatabaseEntry(valueConverter.toByteArray(value)); try { OperationStatus result = db.put(txn().getBJETransaction(), dbkey, dbvalue); if (result != OperationStatus.SUCCESS && result != OperationStatus.KEYEXIST) throw new Exception("OperationStatus: " + result); } catch (Exception ex) { throw new HGException("Failed to add entry to index '" + name + "': " + ex.toString(), ex); } } public HGRandomAccessResult<KeyType> findByValue(ValueType value) { if (!isOpen()) throw new HGException("Attempting to lookup index '" + name + "' while it is closed."); /* * if (value == null) throw new HGException("Attempting to lookup index '" + name + "' with a null key."); */ DatabaseEntry keyEntry = new DatabaseEntry(valueConverter.toByteArray(value)); DatabaseEntry valueEntry = new DatabaseEntry(); HGRandomAccessResult<KeyType> result = null; SecondaryCursor cursor = null; try { TransactionBJEImpl tx = txn(); cursor = secondaryDb.openCursor(tx.getBJETransaction(), cursorConfig); OperationStatus status = cursor.getSearchKey(keyEntry, valueEntry, dummy, LockMode.DEFAULT); if (status == OperationStatus.SUCCESS /* && cursor.count() > 0 */) result = new SingleValueResultSet<KeyType>(tx.attachCursor(cursor), keyEntry, keyConverter); else { try { cursor.close(); } catch (Throwable t) { } result = (HGRandomAccessResult<KeyType>)HGSearchResult.EMPTY; } } catch (Exception ex) { if (cursor != null) try { cursor.close(); } catch (Throwable t) { } throw new HGException("Failed to lookup index '" + name + "': " + ex.toString(), ex); } return result; } public KeyType findFirstByValue(ValueType value) { if (!isOpen()) throw new HGException("Attempting to lookup by value index '" + name + "' while it is closed."); /* * if (value == null) throw new HGException("Attempting to lookup by value index '" + name + * "' with a null value."); */ DatabaseEntry keyEntry = new DatabaseEntry(valueConverter.toByteArray(value)); DatabaseEntry valueEntry = new DatabaseEntry(); KeyType result = null; SecondaryCursor cursor = null; try { cursor = secondaryDb.openCursor(txn().getBJETransaction(), cursorConfig); OperationStatus status = cursor.getSearchKey(keyEntry, valueEntry, dummy, LockMode.DEFAULT); if (status == OperationStatus.SUCCESS) result = keyConverter.fromByteArray(valueEntry.getData(), valueEntry.getOffset(), valueEntry.getSize()); } catch (Exception ex) { throw new HGException("Failed to lookup index '" + name + "': " + ex.toString(), ex); } finally { if (cursor != null) { try { cursor.close(); } catch (Throwable t) { } } } return result; } public long countKeys(ValueType value) { DatabaseEntry keyEntry = new DatabaseEntry(valueConverter.toByteArray(value)); DatabaseEntry valueEntry = new DatabaseEntry(); SecondaryCursor cursor = null; try { cursor = secondaryDb.openCursor(txn().getBJETransaction(), cursorConfig); OperationStatus status = cursor.getSearchKey(keyEntry, valueEntry, dummy, LockMode.DEFAULT); if (status == OperationStatus.SUCCESS) return cursor.count(); else return 0; } catch (DatabaseException ex) { throw new HGException(ex); } finally { if (cursor != null) { try { cursor.close(); } catch (Throwable t) { } } } } }