com.amazon.carbonado.repo.indexed.IndexedCursor.java Source code

Java tutorial

Introduction

Here is the source code for com.amazon.carbonado.repo.indexed.IndexedCursor.java

Source

/*
 * 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;
        }
    }
}