/* * 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.indexed; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import com.amazon.carbonado.Cursor; import com.amazon.carbonado.IsolationLevel; import com.amazon.carbonado.MalformedTypeException; import com.amazon.carbonado.Repository; import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; import com.amazon.carbonado.Storage; import com.amazon.carbonado.SupportException; import com.amazon.carbonado.Transaction; import com.amazon.carbonado.capability.Capability; import com.amazon.carbonado.capability.IndexInfo; import com.amazon.carbonado.capability.IndexInfoCapability; import com.amazon.carbonado.capability.StorableInfoCapability; import com.amazon.carbonado.info.StorableIntrospector; import com.amazon.carbonado.qe.RepositoryAccess; import com.amazon.carbonado.qe.StorageAccess; import com.amazon.carbonado.spi.StoragePool; /** * Wraps another repository in order to make it support indexes. The wrapped * repository must support creation of new types. * * @author Brian S O'Neill */ class IndexedRepository implements Repository, RepositoryAccess, IndexInfoCapability, StorableInfoCapability, IndexEntryAccessCapability { private final AtomicReference<Repository> mRootRef; private final Repository mRepository; private final String mName; private final boolean mIndexRepairEnabled; private final double mIndexThrottle; private final boolean mIndexDiscardDuplicates; private final boolean mIndexRepairVerifyOnly; private final boolean mAllClustered; private final boolean mStrictTriggers; private final StoragePool mStoragePool; private final IndexAnalysisPool mIndexAnalysisPool; IndexedRepository(AtomicReference<Repository> rootRef, String name, Repository repository, boolean indexRepairEnabled, double indexThrottle, boolean indexDiscardDuplicates, boolean indexRepairVerifyOnly, boolean allClustered, boolean strictTriggers) { if (repository.getCapability(IndexInfoCapability.class) == null) { throw new UnsupportedOperationException ("Wrapped repository doesn't support being indexed -- " + "it must support IndexInfoCapability."); } mRootRef = rootRef; mRepository = repository; mName = name; mIndexRepairEnabled = indexRepairEnabled; mIndexThrottle = indexThrottle; mIndexDiscardDuplicates = indexDiscardDuplicates; mIndexRepairVerifyOnly = indexRepairVerifyOnly; mAllClustered = allClustered; mStrictTriggers = strictTriggers; mIndexAnalysisPool = new IndexAnalysisPool(this); mStoragePool = new StoragePool() { @Override protected <S extends Storable> Storage<S> createStorage(Class<S> type) throws RepositoryException { Storage<S> masterStorage = mRepository.storageFor(type); if (Unindexed.class.isAssignableFrom(type)) { // Verify no indexes. int indexCount = IndexAnalysis .gatherDesiredIndexes(StorableIntrospector.examine(type)).size(); if (indexCount > 0) { throw new MalformedTypeException (type, "Storable cannot have any indexes: " + type + ", " + indexCount); } return masterStorage; } IndexAnalysis<S> analysis = mIndexAnalysisPool.get(masterStorage); return new IndexedStorage<S>(analysis); } }; } public String getName() { return mName; } @SuppressWarnings("unchecked") public <S extends Storable> Storage<S> storageFor(Class<S> type) throws MalformedTypeException, SupportException, RepositoryException { return mStoragePool.get(type); } public Transaction enterTransaction() { return mRepository.enterTransaction(); } public Transaction enterTransaction(IsolationLevel level) { return mRepository.enterTransaction(level); } public Transaction enterTopTransaction(IsolationLevel level) { return mRepository.enterTopTransaction(level); } public IsolationLevel getTransactionIsolationLevel() { return mRepository.getTransactionIsolationLevel(); } @SuppressWarnings("unchecked") public <C extends Capability> C getCapability(Class<C> capabilityType) { if (capabilityType.isInstance(this)) { return (C) this; } return mRepository.getCapability(capabilityType); } // Required by IndexInfoCapability. public <S extends Storable> IndexInfo[] getIndexInfo(Class<S> storableType) throws RepositoryException { if (Unindexed.class.isAssignableFrom(storableType)) { return new IndexInfo[0]; } Storage<S> masterStorage = mRepository.storageFor(storableType); IndexAnalysis<S> analysis = mIndexAnalysisPool.get(masterStorage); IndexInfo[] infos = new IndexInfo[analysis.allIndexInfoMap.size()]; return analysis.allIndexInfoMap.values().toArray(infos); } // Required by IndexEntryAccessCapability. public <S extends Storable> IndexEntryAccessor<S>[] getIndexEntryAccessors(Class<S> storableType) throws RepositoryException { if (Unindexed.class.isAssignableFrom(storableType)) { return new IndexEntryAccessor[0]; } Storage<S> masterStorage = mRepository.storageFor(storableType); IndexAnalysis<S> analysis = mIndexAnalysisPool.get(masterStorage); List<IndexEntryAccessor<S>> accessors = new ArrayList<IndexEntryAccessor<S>>(analysis.allIndexInfoMap.size()); for (IndexInfo info : analysis.allIndexInfoMap.values()) { if (info instanceof IndexEntryAccessor) { accessors.add((IndexEntryAccessor<S>) info); } } return accessors.toArray(new IndexEntryAccessor[accessors.size()]); } public String[] getUserStorableTypeNames() throws RepositoryException { StorableInfoCapability cap = mRepository.getCapability(StorableInfoCapability.class); if (cap == null) { return new String[0]; } ArrayList<String> names = new ArrayList<String>(Arrays.asList(cap.getUserStorableTypeNames())); // Exclude our own metadata types as well as indexes. names.remove(StoredIndexInfo.class.getName()); Cursor<StoredIndexInfo> cursor = mRepository.storageFor(StoredIndexInfo.class) .query().fetch(); try { while (cursor.hasNext()) { StoredIndexInfo info = cursor.next(); names.remove(info.getIndexName()); } } finally { cursor.close(); } return names.toArray(new String[names.size()]); } public boolean isSupported(Class<Storable> type) { StorableInfoCapability cap = mRepository.getCapability(StorableInfoCapability.class); return (cap == null) ? false : cap.isSupported(type); } public boolean isPropertySupported(Class<Storable> type, String name) { StorableInfoCapability cap = mRepository.getCapability(StorableInfoCapability.class); return (cap == null) ? false : cap.isPropertySupported(type, name); } public void close() { mRepository.close(); } /* public boolean isClosed() { return mRepository.isClosed(); } */ public Repository getRootRepository() { return mRootRef.get(); } public <S extends Storable> StorageAccess<S> storageAccessFor(Class<S> type) throws SupportException, RepositoryException { return (StorageAccess<S>) storageFor(type); } Storage<?> getIndexEntryStorageFor(Class<? extends Storable> indexEntryClass) throws RepositoryException { return mRepository.storageFor(indexEntryClass); } Repository getWrappedRepository() { return mRepository; } boolean isIndexRepairEnabled() { return mIndexRepairEnabled; } double getIndexRepairThrottle() { return mIndexThrottle; } boolean getIndexDiscardDuplicates() { return mIndexDiscardDuplicates; } boolean getIndexRepairVerifyOnly() { return mIndexRepairVerifyOnly; } boolean isAllClustered() { return mAllClustered; } boolean isStrictTriggers() { return mStrictTriggers; } }