Java tutorial
/* * 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.NoSuchElementException; import org.apache.commons.logging.LogFactory; import com.amazon.carbonado.CorruptEncodingException; import com.amazon.carbonado.Cursor; import com.amazon.carbonado.FetchException; import com.amazon.carbonado.PersistException; import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; import com.amazon.carbonado.Storage; import com.amazon.carbonado.Transaction; import com.amazon.carbonado.cursor.AbstractCursor; import com.amazon.carbonado.cursor.FetchAheadCursor; import com.amazon.carbonado.spi.RepairExecutor; import com.amazon.carbonado.synthetic.SyntheticStorableReferenceAccess; /** * Wraps another cursor which contains index entries and extracts master * objects from them. * * @author Brian S O'Neill */ class IndexedCursor<S extends Storable> extends AbstractCursor<S> { private static final int FETCH_AHEAD; static { String prefix = IndexedCursor.class.getName() + '.'; FETCH_AHEAD = Integer.getInteger(prefix + "fetchAhead", 0); } private final Cursor<? extends Storable> mCursor; private final IndexedStorage<S> mStorage; private final SyntheticStorableReferenceAccess<S> mAccessor; private S mNext; IndexedCursor(Cursor<? extends Storable> indexEntryCursor, IndexedStorage<S> storage, SyntheticStorableReferenceAccess<S> indexAccessor) { if (FETCH_AHEAD > 0) { indexEntryCursor = new FetchAheadCursor(indexEntryCursor, FETCH_AHEAD); } mCursor = indexEntryCursor; mStorage = storage; mAccessor = indexAccessor; } public void close() throws FetchException { mCursor.close(); } public boolean hasNext() throws FetchException { if (mNext != null) { return true; } try { while (mCursor.hasNext()) { final Storable indexEntry = mCursor.next(); S master = mStorage.mMasterStorage.prepare(); mAccessor.copyToMasterPrimaryKey(indexEntry, master); try { if (!master.tryLoad()) { LogFactory.getLog(getClass()).warn("Master is missing for index entry: " + indexEntry); continue; } } catch (CorruptEncodingException e) { LogFactory.getLog(getClass()).error("Master record for index entry is corrupt: " + indexEntry, e); continue; } if (mAccessor.isConsistent(indexEntry, master)) { mNext = master; return true; } // This index entry is stale. Repair is needed. // Insert a correct index entry, just to be sure. try { final IndexedRepository repo = mStorage.mRepository; final Storage<?> indexEntryStorage = repo .getIndexEntryStorageFor(mAccessor.getReferenceClass()); Storable newIndexEntry = indexEntryStorage.prepare(); mAccessor.copyFromMaster(newIndexEntry, master); if (newIndexEntry.tryLoad()) { // Good, the correct index entry exists. We'll see // the master record eventually, so skip. } else { // We have no choice but to return the master, at // the risk of seeing it multiple times. This is // better than seeing it never. LogFactory.getLog(getClass()) .warn("Inconsistent index entry: " + indexEntry + ", " + master); mNext = master; } // Repair the stale index entry. RepairExecutor.execute(new Runnable() { public void run() { Transaction txn = repo.enterTransaction(); try { // Reload master and verify inconsistency. S master = mStorage.mMasterStorage.prepare(); mAccessor.copyToMasterPrimaryKey(indexEntry, master); if (master.tryLoad()) { Storable newIndexEntry = indexEntryStorage.prepare(); mAccessor.copyFromMaster(newIndexEntry, master); newIndexEntry.tryInsert(); indexEntry.tryDelete(); txn.commit(); } } catch (FetchException fe) { LogFactory.getLog(IndexedCursor.class) .warn("Unable to check if repair required for " + "inconsistent index entry " + indexEntry, fe); } catch (PersistException pe) { LogFactory.getLog(IndexedCursor.class) .error("Unable to repair inconsistent index entry " + indexEntry, pe); } finally { try { txn.exit(); } catch (PersistException pe) { LogFactory.getLog(IndexedCursor.class) .error("Unable to repair inconsistent index entry " + indexEntry, pe); } } } }); } catch (Exception re) { LogFactory.getLog(getClass()).error("Unable to inspect inconsistent index entry " + indexEntry, re); } if (mNext != null) { return true; } } } catch (NoSuchElementException e) { } catch (FetchException e) { try { close(); } catch (Exception e2) { // Don't care. } throw e; } return false; } public S next() throws FetchException { try { if (hasNext()) { S next = mNext; mNext = null; return next; } } catch (FetchException e) { try { close(); } catch (Exception e2) { // Don't care. } throw e; } throw new NoSuchElementException(); } @Override public int skipNext(int amount) throws FetchException { try { if (mNext == null) { return mCursor.skipNext(amount); } if (amount <= 0) { if (amount < 0) { throw new IllegalArgumentException("Cannot skip negative amount: " + amount); } return 0; } mNext = null; return 1 + mCursor.skipNext(amount - 1); } catch (FetchException e) { try { close(); } catch (Exception e2) { // Don't care. } throw e; } } }