EhcacheXAStoreImpl.java :  » Cache » ehcache-2.1.0 » net » sf » ehcache » transaction » xa » Java Open Source

Java Open Source » Cache » ehcache 2.1.0 
ehcache 2.1.0 » net » sf » ehcache » transaction » xa » EhcacheXAStoreImpl.java
/**
 *  Copyright 2003-2010 Terracotta, Inc.
 *
 *  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 net.sf.ehcache.transaction.xa;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;

import net.sf.ehcache.store.Store;
import net.sf.ehcache.transaction.TransactionContext;

/**
 * Default implementation of {@link EhcacheXAStore}.<p>
 * It uses {@link java.util.concurrent.ConcurrentHashMap} for the local data (non safe in case of failure) and requires
 * a "safe" {@link net.sf.ehcache.store.Store} for the oldVersionStore, and a reference to the underlying Store.
 *
 * @author Alex Snaps
 */
public class EhcacheXAStoreImpl implements EhcacheXAStore {
    
    /** protected for testing **/
    protected final ConcurrentMap<Xid, XATransactionContext>   transactionContextXids = new ConcurrentHashMap<Xid, XATransactionContext>();
    /** protected for testing **/
    protected final ConcurrentMap<Xid, PreparedContext>        prepareXids            = new ConcurrentHashMap<Xid, PreparedContext>();
    /** protected for testing **/
    protected final VersionTable                               versionTable           = new VersionTable();
    /** protected for testing **/
    protected Store underlyingStore;
    /** protected for testing **/
    protected Store oldVersionStore;

    private final boolean bypassValidation;

    /**
     * Constructor
     * @param underlyingStore the real underlying store
     * @param oldVersionStore the old version, read-only, used to access keys during 2pc
     * @param bypassValidation whether versioning for checked out elements should be traced
     */
    public EhcacheXAStoreImpl(Store underlyingStore, Store oldVersionStore, boolean bypassValidation) {
        this.underlyingStore  = underlyingStore;
        this.oldVersionStore  = oldVersionStore;
        this.bypassValidation = bypassValidation;
    }

    /**
     * {@inheritDoc}
     */
    public Store getOldVersionStore() {
        return this.oldVersionStore;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isPrepared(final Xid xid) {
        return prepareXids.containsKey(xid);
    }

    /**
     * {@inheritDoc}
     */
    public void removeData(final Xid xid) {
        prepareXids.remove(xid);
        transactionContextXids.remove(xid);
    }

    /**
     * Whether we currently do Versioning of Elements
     * @return true if versioning is bypassed, false otherwise
     */
    public boolean isBypassingValidation() {
        return bypassValidation;
    }

    /**
     * {@inheritDoc}
     */
    public void checkin(Object key, Xid xid, boolean readOnly) {
       versionTable.checkin(key, xid, readOnly);
    }

    /**
     * {@inheritDoc}
     */
    public long checkout(Object key, Xid xid) {
        return versionTable.checkout(key, xid);
    }
    
    /**
     * {@inheritDoc}
     */
    public TransactionContext createTransactionContext(Xid xid) {
        XATransactionContext context = new XATransactionContext(xid, this);
        XATransactionContext previous = transactionContextXids.putIfAbsent(xid, context);
        if (previous != null) {
            context = previous;
        }
        return context;
    }

    /**
     * {@inheritDoc}
     */
    public Xid[] getPreparedXids() {
        Set<Xid> xidSet = prepareXids.keySet();
        return xidSet.toArray(new Xid[0]);
    }

    /**
     * {@inheritDoc}
     */
    public TransactionContext getTransactionContext(Xid xid) {
        return transactionContextXids.get(xid);
    }

    /**
     * {@inheritDoc}
     */
    public boolean isValid(VersionAwareCommand command, Xid xid) {
        return versionTable.valid(command.getKey(), command.getVersion());
    }
    
    /**
     * {@inheritDoc}
     */
    public void prepare(Xid xid, PreparedContext context) throws EhcacheXAException {
        if (prepareXids.putIfAbsent(xid, context) != null) {
            throw new EhcacheXAException("A prepared context for that Xid already exists", XAException.XAER_INVAL);
        }
    }
    
    /**
     * {@inheritDoc}
     */
    public PreparedContext getPreparedContext(Xid xid) {
        return prepareXids.get(xid);
    }
    
    
    /**
     * {@inheritDoc}
     */
    public PreparedContext createPreparedContext() {
        return new PreparedContextImpl();
    }

    /**
     * {@inheritDoc}
     */
    public Store getUnderlyingStore() {
        return underlyingStore;
    }

    /**
     * A table containing element version information
     */
    public static class VersionTable {

        /**
         * The data underlying structure for the version info for Key (where the version is the Long)
         */
        protected final ConcurrentMap<Object, Version> versionStore = new ConcurrentHashMap<Object, Version>();

        /**
         * Checks whether a version is still up to date
         * @param key the key to check for
         * @param currentVersionNumber the version checked out
         * @return true if the version is still up to date
         */
        public boolean valid(Object key, long currentVersionNumber) {
            Version version = versionStore.get(key);
            if (version != null) {
                long currentVersion = version.getVersion();
                return (currentVersion == currentVersionNumber);
            } else {
                throw new RuntimeException("No version checked out for this element, this is... WRONG!");
            }
        }

        /**
         * Track a version for an element, potentially adding it to the store
         * @param key the key matching the element in the store
         * @param xid the Xid of the Transaction
         * @return the version
         */
        public long checkout(Object key, Xid xid) {
            long versionNumber;
            Version version = versionStore.get(key);
            if (version == null) {
                version = new Version();
                versionStore.put(key, version);
            }
            versionNumber = version.checkout(xid);

            return versionNumber;
        }

        /**
         * Increment versioning information of a mutated and stored information to the store
         * @param key the key matching the element in the store
         * @param xid the Xid of the Transaction
         * @param readOnly whether the element was mutated in the Store
         */
        public void checkin(Object key, Xid xid, boolean readOnly) {
            if (key != null) {
                Version version = versionStore.get(key);
                boolean removeEntry;
                if (readOnly) {
                    removeEntry = version.checkinRead(xid);
                } else {
                    removeEntry = version.checkinWrite(xid);
                }
                if (removeEntry) {
                    versionStore.remove(key);
                }
            }
        }

    }

    /**
     * Represents an Element's version for a Store
     */
    public static class Version {

        private final AtomicLong version = new AtomicLong(0);

        // TODO We need to figure out a more compressed data-structure (need to performance test to confirm
        private final ConcurrentMap<Xid, Long> txnVersions = new ConcurrentHashMap<Xid, Long>();

        
        /**
         * For testing, get current version
         * @return the version
         */
        public long getVersion() {
            return version.get();
          }
          
        /**
         * Checks whether a Transaction already accessed the key
         * @param xid the Xid for the Transaction
         * @return true, if known
         */
        public boolean hasTransaction(Xid xid) {
            return txnVersions.containsKey(xid);
        }

        /**
         * Gets the version known to a Transaction
         * @param xid the Xis for the Transaction
         * @return the versionNumber
         */
        public long getVersion(Xid xid) {
            try {
                return txnVersions.get(xid);
            } catch (NullPointerException e) {
                throw new AssertionError("Cannot get version for not existing transaction: " + xid);
            }
        }

        /**
         * Checks out a version for a Transaction
         * @param xid the Xid for the Transaction
         * @return the version number
         */
        public long checkout(Xid xid) {
            long v = version.get();
            txnVersions.put(xid, v);
            return v;
        }

        /**
         * Read check in: the transaction is done with the element, but did not mutate it
         * @param xid the Xid for the Transaction
         * @return false if the Element is still known to other Transaction, otherwise true
         */
        public boolean checkinRead(Xid xid) {
            txnVersions.remove(xid);
            return txnVersions.isEmpty();
        }

        /**
         * Write check in: the transaction is done with the element and did mutate it
         * @param xid the Xid for the Transaction
         * @return false if the Element is still known to other Transaction, otherwise true
         */
        public boolean checkinWrite(Xid xid) {
            long v = txnVersions.remove(xid);
            version.incrementAndGet();
            return txnVersions.isEmpty();
        }

        /**
         * Getter to the underlying structure
         * @return the mapping of Xid to Version
         */
        ConcurrentMap<Xid, Long> getTxnVersions() {
            return txnVersions;
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.