Package org.apache.slide.store

Source Code of org.apache.slide.store.ExtendedStore$TxContentCacheWrapper

/*
* $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/store/ExtendedStore.java,v 1.2.2.3 2004/03/30 13:47:33 ozeigermann Exp $
* $Revision: 1.2.2.3 $
* $Date: 2004/03/30 13:47:33 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* 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 org.apache.slide.store;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionAlreadyExistException;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.content.RevisionNotFoundException;
import org.apache.slide.lock.LockTokenNotFoundException;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.security.NodePermission;
import org.apache.slide.structure.LinkNode;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.*;
import org.apache.slide.util.ObjectCache;

import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import org.apache.slide.util.logger.Logger;

/**
* Store that allows for transactional caching of data. Takes over much modified code from StandardStore.
* That's why Remy is listed as author as well.
*
* @author <a href="mailto:ozeigermann@c1-fse.de">Oliver Zeigermann</a>
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
* @version $Revision: 1.2.2.3 $
*/
public class ExtendedStore extends AbstractStore {

    private static final String LOG_CHANNEL = ExtendedStore.class.getName();
    // just for debugging
    private static final boolean globalCacheOff = false;

    protected static final int DEFAULT_OBJECT_GLOBAL_CACHE_SIZE = 10000;
    protected static final String GLOBAL_OBJECT_CACHE_SIZE_PARAMETER = "object-cache-size";

    protected static final int DEFAULT_GLOBAL_PERMISSION_CACHE_SIZE = 10000;
    protected static final String GLOBAL_PERMISSION_CACHE_SIZE_PARAMETER = "permission-cache-size";

    protected static final int DEFAULT_GLOBAL_LOCK_CACHE_SIZE = 100;
    protected static final String GLOBAL_LOCK_CACHE_SIZE_PARAMETER = "lock-cache-size";

    protected static final int DEFAULT_GLOBAL_DESCRIPTORS_CACHE_SIZE = 10000;
    protected static final String GLOBAL_DESCRIPTORS_CACHE_SIZE_PARAMETER = "descriptors-cache-size";

    protected static final int DEFAULT_GLOBAL_DESCRIPTOR_CACHE_SIZE = 10000;
    protected static final String GLOBAL_DESCRIPTOR_CACHE_SIZE_PARAMETER = "descriptor-cache-size";

    protected static final int DEFAULT_GLOBAL_CONTENT_CACHE_SIZE = 10000;
    protected static final String GLOBAL_CONTENT_CACHE_SIZE_PARAMETER = "content-cache-size";

    protected static final boolean DEFAULT_ENABLE_CONTENT_CACHING = false;
    protected static final String ENABLE_CONTENT_CACHING_PARAMETER = "enable-content-caching";

    protected static final int DEFAULT_TX_CONTENT_CACHE_SIZE = 1000;
    protected static final String TX_CONTENT_CACHE_SIZE_PARAMETER = "tx-content-cache-size";

    protected static final long DEFAULT_CONTENT_CACHE_BYTES = 10000000; // 10 M
    protected static final String CONTENT_CACHE_BYTES_PARAMETER = "content-cache-bytes";

    protected static final long DEFAULT_TX_CONTENT_CACHE_BYTES = 1000000; // 1 M
    protected static final String TX_CONTENT_CACHE_BYTES_PARAMETER = "tx-content-cache-bytes";

    protected static final long DEFAULT_MAX_CONTENT_BYTES_PER_ENTRY = 50000; // 50 K
    protected static final String MAX_CONTENT_BYTES_PER_ENTRY_PARAMETER = "max-content-bytes-per-entry";

    // there might be at least one active transaction branch per thread
    protected ThreadLocal activeTransactionBranch = new ThreadLocal();

    protected TxContentCacheWrapper contentCache;
    protected TxCacheWrapper objectsCache;
    protected TxCacheWrapper permissionsCache;
    protected TxCacheWrapper locksCache;
    protected TxCacheWrapper descriptorsCache;
    protected TxCacheWrapper descriptorCache;

    public void setParameters(Hashtable parameters)
        throws ServiceParameterErrorException, ServiceParameterMissingException {
        super.setParameters(parameters);

        int globalObjectCacheSize = DEFAULT_OBJECT_GLOBAL_CACHE_SIZE;
        int globalPermissionCacheSize = DEFAULT_GLOBAL_PERMISSION_CACHE_SIZE;
        int globalLockCacheSize = DEFAULT_GLOBAL_LOCK_CACHE_SIZE;
        int globalDescrtiptorsCacheSize = DEFAULT_GLOBAL_DESCRIPTORS_CACHE_SIZE;
        int globalDescrtiptorCacheSize = DEFAULT_GLOBAL_DESCRIPTOR_CACHE_SIZE;
        boolean contentCachingEnabled = DEFAULT_ENABLE_CONTENT_CACHING;
        int globalContentCacheSize = DEFAULT_GLOBAL_CONTENT_CACHE_SIZE;
        long contentCacheBytes = DEFAULT_CONTENT_CACHE_BYTES;
        int txContentCacheSize = DEFAULT_TX_CONTENT_CACHE_SIZE;
        long txContentCacheBytes = DEFAULT_TX_CONTENT_CACHE_BYTES;
        long maxByteSizePerEntry = DEFAULT_MAX_CONTENT_BYTES_PER_ENTRY;

        String globalObjectCacheSizeString = (String) parameters.get(GLOBAL_OBJECT_CACHE_SIZE_PARAMETER);
        if (globalObjectCacheSizeString != null) {
            try {
                globalObjectCacheSize = Integer.parseInt(globalObjectCacheSizeString);
            } catch (NumberFormatException nfe) {
                getLogger().log("Cache size must be an integer! Using default size", LOG_CHANNEL, Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting object cache size for store " + getName() + " to " + globalObjectCacheSize,
            Logger.INFO);

        String globalPermissionCacheSizeString = (String) parameters.get(GLOBAL_PERMISSION_CACHE_SIZE_PARAMETER);
        if (globalPermissionCacheSizeString != null) {
            try {
                globalPermissionCacheSize = Integer.parseInt(globalPermissionCacheSizeString);
            } catch (NumberFormatException nfe) {
                getLogger().log("Cache size must be an integer! Using default size", LOG_CHANNEL, Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting permission cache size for store " + getName() + " to " + globalPermissionCacheSize,
            LOG_CHANNEL,
            Logger.INFO);

        String globalLockCacheSizeString = (String) parameters.get(GLOBAL_LOCK_CACHE_SIZE_PARAMETER);
        if (globalLockCacheSizeString != null) {
            try {
                globalLockCacheSize = Integer.parseInt(globalLockCacheSizeString);
            } catch (NumberFormatException nfe) {
                getLogger().log("Cache size must be an integer! Using default size", LOG_CHANNEL, Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting lock cache size for store " + getName() + " to " + globalLockCacheSize,
            LOG_CHANNEL,
            Logger.INFO);

        String globalDescriptorsCacheSizeString = (String) parameters.get(GLOBAL_DESCRIPTORS_CACHE_SIZE_PARAMETER);
        if (globalDescriptorsCacheSizeString != null) {
            try {
                globalDescrtiptorsCacheSize = Integer.parseInt(globalDescriptorsCacheSizeString);
            } catch (NumberFormatException nfe) {
                getLogger().log("Cache size must be an integer! Using default size", LOG_CHANNEL, Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting descriptors cache size for store " + getName() + " to " + globalDescrtiptorsCacheSize,
            LOG_CHANNEL,
            Logger.INFO);

        String globalDescriptorCacheSizeString = (String) parameters.get(GLOBAL_DESCRIPTOR_CACHE_SIZE_PARAMETER);
        if (globalDescriptorCacheSizeString != null) {
            try {
                globalDescrtiptorCacheSize = Integer.parseInt(globalDescriptorCacheSizeString);
            } catch (NumberFormatException nfe) {
                getLogger().log("Cache size must be an integer! Using default size", LOG_CHANNEL, Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting descriptor cache size for store " + getName() + " to " + globalDescrtiptorCacheSize,
            LOG_CHANNEL,
            Logger.INFO);

        String enableContentCachingString = (String) parameters.get(ENABLE_CONTENT_CACHING_PARAMETER);
        if (enableContentCachingString != null) {
            contentCachingEnabled = Boolean.valueOf(enableContentCachingString).booleanValue();
        }
        getLogger().log(
            "Setting content caching for store " + getName() + " to " + contentCachingEnabled,
            LOG_CHANNEL,
            Logger.INFO);

        String globalContentCacheSizeString = (String) parameters.get(GLOBAL_CONTENT_CACHE_SIZE_PARAMETER);
        if (globalContentCacheSizeString != null) {
            try {
                globalContentCacheSize = Integer.parseInt(globalContentCacheSizeString);
            } catch (NumberFormatException nfe) {
                getLogger().log("Cache size must be an integer! Using default size", LOG_CHANNEL, Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting content cache size for store " + getName() + " to " + globalContentCacheSize,
            LOG_CHANNEL,
            Logger.INFO);

        String contentCacheBytesString = (String) parameters.get(CONTENT_CACHE_BYTES_PARAMETER);
        if (contentCacheBytesString != null) {
            try {
                contentCacheBytes = Long.parseLong(contentCacheBytesString);
            } catch (NumberFormatException nfe) {
                getLogger().log(
                    "Content cache byte size must be an integer! Using default size",
                    LOG_CHANNEL,
                    Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting content cache byte size for store " + getName() + " to " + contentCacheBytes,
            LOG_CHANNEL,
            Logger.INFO);

        String txContentCacheSizeString = (String) parameters.get(TX_CONTENT_CACHE_SIZE_PARAMETER);
        if (txContentCacheSizeString != null) {
            try {
                txContentCacheSize = Integer.parseInt(txContentCacheSizeString);
            } catch (NumberFormatException nfe) {
                getLogger().log(
                    "Content transaction cache size must be an integer! Using default size",
                    LOG_CHANNEL,
                    Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting transaction content cache size for store " + getName() + " to " + txContentCacheSize,
            LOG_CHANNEL,
            Logger.INFO);

        String txContentCacheBytesString = (String) parameters.get(TX_CONTENT_CACHE_BYTES_PARAMETER);
        if (txContentCacheBytesString != null) {
            try {
                txContentCacheBytes = Long.parseLong(txContentCacheBytesString);
            } catch (NumberFormatException nfe) {
                getLogger().log(
                    "Transaction content cache byte size must be an integer! Using default size",
                    LOG_CHANNEL,
                    Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting transaction content cache byte size for store " + getName() + " to " + txContentCacheBytes,
            LOG_CHANNEL,
            Logger.INFO);

        String maxByteSizePerEntryString = (String) parameters.get(MAX_CONTENT_BYTES_PER_ENTRY_PARAMETER);
        if (maxByteSizePerEntryString != null) {
            try {
                maxByteSizePerEntry = Long.parseLong(maxByteSizePerEntryString);
            } catch (NumberFormatException nfe) {
                getLogger().log(
                    "Maximum byte size for content cache entry must be an integer! Using default size",
                    LOG_CHANNEL,
                    Logger.WARNING);
            }
        }
        getLogger().log(
            "Setting maximum byte size for content cache entry for store " + getName() + " to " + maxByteSizePerEntry,
            LOG_CHANNEL,
            Logger.INFO);

        init(
            globalObjectCacheSize,
            globalPermissionCacheSize,
            globalLockCacheSize,
            globalDescrtiptorsCacheSize,
            globalDescrtiptorCacheSize,
            contentCachingEnabled,
            globalContentCacheSize,
            contentCacheBytes,
            txContentCacheSize,
            txContentCacheBytes,
            maxByteSizePerEntry);
    }

    //
    // overloaded content methods with caching
    //

    public NodeRevisionContent retrieveRevisionContent(Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException, RevisionNotFoundException {
        if (contentStore.cacheResults() && contentCache != null && !omitCachingBecauseOfEvilHistoryHack(uri)) {
            if (isForceStoreEnlistment(uri)) {
                enlist(this);
            }
            try {
                String key = uri.toString() + "_" + revisionDescriptor.getRevisionNumber();
                Object result = contentCache.get(key);
                if (result != null) {
                    getLogger().log("Retrieving content at '" + key + "' from cache", LOG_CHANNEL, Logger.DEBUG);
                    // FIXME make a copy?! how?
                    return (NodeRevisionContent) result;
                } else {
                    NodeRevisionContent revisionContent = super.retrieveRevisionContent(uri, revisionDescriptor);
                    long size = revisionDescriptor.getContentLength();
                    // Do not add content to global cache, as this might violate repeatable read.
                    // When we reread the entry later and another thread has committed its stuff
                    // into the global cache we might get a different version on the second read.
                    contentCache.put(key, revisionContent, size);
//                    contentCache.putForRead(key, revisionContent, size);
                    // FIXME make a copy?! how?
                    return revisionContent;
                }
            } finally {
                if (isForceStoreEnlistment(uri)) {
                    delist(this);
                }
            }
        } else {
            return super.retrieveRevisionContent(uri, revisionDescriptor);
        }
    }

    public void createRevisionContent(
        Uri uri,
        NodeRevisionDescriptor revisionDescriptor,
        NodeRevisionContent revisionContent)
        throws ServiceAccessException, RevisionAlreadyExistException {

        long size = -1L;
        if (contentStore.cacheResults() && contentCache != null) {
            size = revisionDescriptor.getContentLength();
            contentCache.precache(revisionContent, size);
        }

        super.createRevisionContent(uri, revisionDescriptor, revisionContent);

        if (contentStore.cacheResults() && contentCache != null) {
            enlist(this);
            try {
                String key = uri.toString() + "_" + revisionDescriptor.getRevisionNumber();
                contentCache.put(key, revisionContent, size);
            } finally {
                delist(this);
            }
        }
    }

    public void storeRevisionContent(
        Uri uri,
        NodeRevisionDescriptor revisionDescriptor,
        NodeRevisionContent revisionContent)
        throws ServiceAccessException, RevisionNotFoundException {

        long size = -1L;
        if (contentStore.cacheResults() && contentCache != null) {
            size = revisionDescriptor.getContentLength();
            contentCache.precache(revisionContent, size);
        }

        super.storeRevisionContent(uri, revisionDescriptor, revisionContent);

        if (contentStore.cacheResults() && contentCache != null) {
            enlist(this);
            try {
                String key = uri.toString() + "_" + revisionDescriptor.getRevisionNumber();
                contentCache.put(key, revisionContent, size);
            } finally {
                delist(this);
            }
        }
    }

    public void removeRevisionContent(Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException {
        if (contentStore.cacheResults() && contentCache != null) {
            enlist(this);
            try {
                String key = uri.toString() + "_" + revisionDescriptor.getRevisionNumber();
                contentCache.remove(key);
            } finally {
                delist(this);
            }
        }
        super.removeRevisionContent(uri, revisionDescriptor);
    }

    //
    // overloaded retrieval methods guaranteeing cache enlistment in retrieval
    //

    public ObjectNode retrieveObject(Uri uri) throws ServiceAccessException, ObjectNotFoundException {
        if (nodeStore.cacheResults() && !omitCachingBecauseOfEvilHistoryHack(uri)) {
            if (isForceStoreEnlistment(uri)) {
                enlist(this);
            }
            try {
                Object tempObject = objectsCache.get(uri.toString());
                if (tempObject != null) {
                    return ((ObjectNode) tempObject).cloneObject();
                } else {
                    ObjectNode objectNode = super.retrieveObject(uri);
                    objectNode.validate(uri.toString());
                    objectsCache.put(uri.toString(), objectNode);
                    return objectNode.cloneObject();
                }
            } finally {
                if (isForceStoreEnlistment(uri)) {
                    delist(this);
                }
            }
        } else {
            return super.retrieveObject(uri);
        }
    }

    public void storeObject(Uri uri, ObjectNode object) throws ServiceAccessException, ObjectNotFoundException {
        super.storeObject(uri, object);
        if (nodeStore.cacheResults()) {
            enlist(this);
            try {
                objectsCache.put(uri.toString(), object.cloneObject());
            } finally {
                delist(this);
            }
        }
    }

    public void createObject(Uri uri, ObjectNode object) throws ServiceAccessException, ObjectAlreadyExistsException {
        super.createObject(uri, object);
        if (nodeStore.cacheResults()) {
            enlist(this);
            try {
               
                if (object instanceof LinkNode) {
                    // add the link into the "linked" object if it is in the cache
                   String linkedUri = ((LinkNode) object).getLinkedUri();
                   ObjectNode linkedObject = (ObjectNode) objectsCache.get(linkedUri);
                   if (linkedObject != null) {
                       linkedObject.addLink((LinkNode)object);
                   }
                }
                objectsCache.put(uri.toString(), object.cloneObject());
            } finally {
                delist(this);
            }
        }
    }

    public void removeObject(Uri uri, ObjectNode object) throws ServiceAccessException, ObjectNotFoundException {
        super.removeObject(uri, object);
        if (nodeStore.cacheResults()) {
            enlist(this);
            try {
                if (object instanceof LinkNode) {
                    // check if the linked object is in the cache, and remove the linknode  fromits link vector
                    ObjectNode linkedObject = (ObjectNode) objectsCache.get(((LinkNode)object).getLinkedUri());
                    if (linkedObject !=null) {
                        linkedObject.removeLink((LinkNode) object);
                    }
                }
                objectsCache.remove(uri.toString());
            } finally {
                delist(this);
            }
        }
    }

    public void grantPermission(Uri uri, NodePermission permission) throws ServiceAccessException {
        super.grantPermission(uri, permission);
        if (securityStore.cacheResults()) {
            enlist(this);
            try {
                NodePermission tempPermission = permission.cloneObject();
                Object value = permissionsCache.get(uri.toString());
                if (value == null) {
                    // populate the cache with the existing entries
                    enumeratePermissions(uri);
                    // and check if the cache contains a corresponding entry now
                    value = permissionsCache.get(uri.toString());
                    if (value == null) {
                        // no permissions for the Uri in the cache, so create a new
                        // entry
                        Vector permissionsVector = new Vector();
                        permissionsVector.addElement(tempPermission);
                        permissionsCache.put(uri.toString(), permissionsVector);
                    }
                } else {
                    Vector permissionsVector = new Vector((Vector) value);
                    permissionsVector.addElement(tempPermission);
                    permissionsCache.put(uri.toString(), permissionsVector);
                }
            } finally {
                delist(this);
            }
        }
    }

    public void revokePermission(Uri uri, NodePermission permission) throws ServiceAccessException {
        super.revokePermission(uri, permission);
        if (securityStore.cacheResults()) {
            enlist(this);
            try {
                Object value = permissionsCache.get(uri.toString());
                Vector permissionsVector = null;
                if (value != null) {
                    permissionsVector = new Vector((Vector) value);
                    permissionsVector.removeElement(permission);
                    permissionsCache.put(uri.toString(), permissionsVector);
                }
            } finally {
                delist(this);
            }

        }
    }

    public void revokePermissions(Uri uri) throws ServiceAccessException {
        super.revokePermissions(uri);
        if (securityStore.cacheResults()) {
            enlist(this);
            try {
                Object value = permissionsCache.get(uri.toString());
                if (value != null) {
                    permissionsCache.put(uri.toString(), new Vector());
                }
            } finally {
                delist(this);
            }
        }
    }

    public Enumeration enumeratePermissions(Uri uri) throws ServiceAccessException {
        if (securityStore.cacheResults()) {
            if (isForceStoreEnlistment(uri)) {
                enlist(this);
            }
            try {
                Object value = permissionsCache.get(uri.toString());
                Vector permissionsVector = null;
                if (value != null) {
                    permissionsVector = (Vector) value;
                } else {
                    permissionsVector = new Vector();
                    Enumeration tempEnum = securityStore.enumeratePermissions(uri);
                    while (tempEnum.hasMoreElements()) {
                        NodePermission tempPermission = (NodePermission) tempEnum.nextElement();
                        tempPermission.validate(uri.toString());
                        permissionsVector.addElement(tempPermission);
                    }
                    permissionsCache.put(uri.toString(), permissionsVector);
                }
                return ((Vector) permissionsVector.clone()).elements();
            } finally {
                if (isForceStoreEnlistment(uri)) {
                    delist(this);
                }
            }
        } else {
            return super.enumeratePermissions(uri);
        }
    }

    public void putLock(Uri uri, NodeLock lock) throws ServiceAccessException {
        super.putLock(uri, lock);
        if (lockStore.cacheResults()) {
            enlist(this);
            try {
                Object value = locksCache.get(uri.toString());
                Vector locksVector = null;
                if (value == null) {
                    locksVector = new Vector();
                } else {
                    locksVector = new Vector((Vector)value);
                }
                locksVector.addElement(lock.cloneObject());
                locksCache.put(uri.toString(), locksVector);
            } finally {
                delist(this);
            }
        }
    }

    public void renewLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
        super.renewLock(uri, lock);
        if (lockStore.cacheResults()) {
            enlist(this);
            try {
                Object value = locksCache.get(uri.toString());
                Vector locksVector = null;
                if (value != null) {
                    locksVector = new Vector((Vector)value);
                    boolean wasPresent = locksVector.removeElement(lock);
                    if (!wasPresent) {
                        throw new LockTokenNotFoundException(lock);
                    }
                    locksVector.addElement(lock.cloneObject());
                    locksCache.put(uri.toString(), locksVector);
                }
            } finally {
                delist(this);
            }
        }
    }

    public void removeLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
        super.removeLock(uri, lock);
        if (lockStore.cacheResults()) {
            enlist(this);
            try {
                Object value = locksCache.get(uri.toString());
                Vector locksVector = null;
                if (value != null) {
                    locksVector = new Vector((Vector)value);
                    boolean wasPresent = locksVector.removeElement(lock);
                    if (!wasPresent) {
                        throw new LockTokenNotFoundException(lock);
                    }
                }
                locksCache.put(uri.toString(), locksVector);
            } finally {
                delist(this);
            }
        }
    }

    public void killLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
        super.killLock(uri, lock);
        if (lockStore.cacheResults()) {
            enlist(this);
            try {
                Object value = locksCache.get(uri.toString());
                Vector locksVector = null;
                if (value != null) {
                    locksVector = new Vector((Vector)value);
                    boolean wasPresent = locksVector.removeElement(lock);
                    if (!wasPresent) {
                        throw new LockTokenNotFoundException(lock);
                    }
                }
                locksCache.put(uri.toString(), locksVector);
            } finally {
                delist(this);
            }
        }
    }

    public Enumeration enumerateLocks(Uri uri) throws ServiceAccessException {
        if (lockStore.cacheResults()) {
            if (isForceStoreEnlistment(uri)) {
                enlist(this);
            }
            try {
                Object value = locksCache.get(uri.toString());
                Vector locksVector = null;
                if (value == null) {
                    locksVector = new Vector();
                    Enumeration lockList = lockStore.enumerateLocks(uri);
                    while (lockList.hasMoreElements()) {
                        NodeLock tempLock = (NodeLock) lockList.nextElement();
                        tempLock.validate(uri.toString());
                        locksVector.addElement(tempLock);
                    }
                    locksCache.put(uri.toString(), locksVector);
                } else {
                    locksVector = (Vector) value;
                }
                return ((Vector) locksVector.clone()).elements();
            } finally {
                if (isForceStoreEnlistment(uri)) {
                    delist(this);
                }
            }
        } else {
            return super.enumerateLocks(uri);
        }
    }
   
    public NodeRevisionDescriptors retrieveRevisionDescriptors(Uri uri)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if (revisionDescriptorsStore.cacheResults() && !omitCachingBecauseOfEvilHistoryHack(uri)) {
            if (isForceStoreEnlistment(uri)) {
                enlist(this);
            }
            try {
                Object tempObject = descriptorsCache.get(uri.toString());
                if (tempObject != null) {
                    return ((NodeRevisionDescriptors) tempObject).cloneObject();
                } else {
                    NodeRevisionDescriptors revisionDescriptors =
                        revisionDescriptorsStore.retrieveRevisionDescriptors(uri);
                    descriptorsCache.put(uri.toString(), revisionDescriptors);
                    revisionDescriptors.validate(uri.toString());
                    return revisionDescriptors.cloneObject();
                }
            } finally {
                if (isForceStoreEnlistment(uri)) {
                    delist(this);
                }
            }
        } else {
            return super.retrieveRevisionDescriptors(uri);
        }
    }

    public void createRevisionDescriptors(Uri uri, NodeRevisionDescriptors revisionDescriptors)
        throws ServiceAccessException {
        super.createRevisionDescriptors(uri, revisionDescriptors);
        if (revisionDescriptorsStore.cacheResults()) {
            enlist(this);
            try {
                descriptorsCache.put(uri.toString(), revisionDescriptors.cloneObject());
            } finally {
                delist(this);
            }
        }
    }

    public void storeRevisionDescriptors(Uri uri, NodeRevisionDescriptors revisionDescriptors)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        super.storeRevisionDescriptors(uri, revisionDescriptors);
        if (revisionDescriptorsStore.cacheResults()) {
            enlist(this);
            try {
                descriptorsCache.put(uri.toString(), revisionDescriptors.cloneObject());
            } finally {
                delist(this);
            }
        }
    }

    public void removeRevisionDescriptors(Uri uri) throws ServiceAccessException {
        if (revisionDescriptorsStore.cacheResults()) {
            enlist(this);
            try {
                descriptorsCache.remove(uri.toString());
            } finally {
                delist(this);
            }
        }
        super.removeRevisionDescriptors(uri);
    }

    public NodeRevisionDescriptor retrieveRevisionDescriptor(Uri uri, NodeRevisionNumber revisionNumber)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if (revisionDescriptorStore.cacheResults() && !omitCachingBecauseOfEvilHistoryHack(uri)) {
            if (isForceStoreEnlistment(uri)) {
                enlist(this);
            }
            try {
                Object result = descriptorCache.get(uri + "-" + revisionNumber);
                if (result != null) {
                    return ((NodeRevisionDescriptor) result).cloneObject();
                } else {
                    NodeRevisionDescriptor revisionDescriptor =
                        revisionDescriptorStore.retrieveRevisionDescriptor(uri, revisionNumber);
                    revisionDescriptor.validate();
                    descriptorCache.put(uri + "-" + revisionNumber, revisionDescriptor);
                    return revisionDescriptor.cloneObject();
                }
            } finally {
                if (isForceStoreEnlistment(uri)) {
                    delist(this);
                }
            }
        } else {
            return super.retrieveRevisionDescriptor(uri, revisionNumber);
        }
    }

    public void createRevisionDescriptor(Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException {
        super.createRevisionDescriptor(uri, revisionDescriptor);
        if (revisionDescriptorStore.cacheResults()) {
            enlist(this);
            try {
                descriptorCache.put(
                    uri + "-" + revisionDescriptor.getRevisionNumber(),
                    revisionDescriptor.cloneObject());
            } finally {
                delist(this);
            }
        }
    }

    public void storeRevisionDescriptor(Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        super.storeRevisionDescriptor(uri, revisionDescriptor);
        if (revisionDescriptorStore.cacheResults()) {
            enlist(this);
            try {
                String key = uri + "-" + revisionDescriptor.getRevisionNumber();
                descriptorCache.put(key, revisionDescriptor.cloneObject());
            } finally {
                delist(this);
            }
        }
    }

    public void removeRevisionDescriptor(Uri uri, NodeRevisionNumber number) throws ServiceAccessException {
        super.removeRevisionDescriptor(uri, number);
        if (revisionDescriptorStore.cacheResults()) {
            enlist(this);
            try {
                descriptorCache.remove(uri + "-" + number);
            } finally {
                delist(this);
            }
        }
    }

    //
    // overloaded XAResource methods for transaction support
    //

    public void forget(Xid xid) throws XAException {
        Xid txId = (Xid) XidWrapper.wrap(xid);
        activeTransactionBranch.set(null);

        objectsCache.getTxCache().forget(txId);
        permissionsCache.getTxCache().forget(txId);
        locksCache.getTxCache().forget(txId);
        descriptorsCache.getTxCache().forget(txId);
        descriptorCache.getTxCache().forget(txId);
        if (contentCache != null)
            contentCache.getTxCache().forget(txId);
    }

    public void rollback(Xid xid) throws XAException {
        Xid txId = (Xid) XidWrapper.wrap(xid);
        activeTransactionBranch.set(null);

        objectsCache.getTxCache().rollback(txId);
        permissionsCache.getTxCache().rollback(txId);
        locksCache.getTxCache().rollback(txId);
        descriptorsCache.getTxCache().rollback(txId);
        descriptorCache.getTxCache().rollback(txId);
        if (contentCache != null)
            contentCache.getTxCache().rollback(txId);
    }

    public void commit(Xid xid, boolean onePhase) throws XAException {
        Xid txId = (Xid) XidWrapper.wrap(xid);
        activeTransactionBranch.set(null);

        objectsCache.getTxCache().commit(txId);
        permissionsCache.getTxCache().commit(txId);
        locksCache.getTxCache().commit(txId);
        descriptorsCache.getTxCache().commit(txId);
        descriptorCache.getTxCache().commit(txId);
        if (contentCache != null)
            contentCache.getTxCache().commit(txId);
    }

    public int prepare(Xid xid) throws XAException {
        // failure is notifyed directly on transaction, so there is no need to check it here
        return XA_OK;
    }

    public void start(Xid xid, int flags) throws XAException {
        // XXX we do not suspend, so we do not resume
        if (flags == TMNOFLAGS || flags == TMJOIN) {
            Xid txId = (Xid) XidWrapper.wrap(xid);
            activeTransactionBranch.set(txId);

            objectsCache.getTxCache().start(txId);
            permissionsCache.getTxCache().start(txId);
            locksCache.getTxCache().start(txId);
            descriptorsCache.getTxCache().start(txId);
            descriptorCache.getTxCache().start(txId);
            if (contentCache != null)
                contentCache.getTxCache().start(txId);
        }
    }

    public void end(Xid xid, int flags) throws XAException {
    }

    //

    public String toString() {
        return getName() + "(" + getClass().getName() + ")";
    }

    //

    // have it outside ctor to make it overloadable
    protected void init(
        int globalObjectCacheSize,
        int globalPermissionCacheSize,
        int globalLockCacheSize,
        int globalDescrtiptorsCacheSize,
        int globalDescrtiptorCacheSize,
        boolean contentCachingEnabled,
        int globalContentCacheSize,
        long contentCacheBytes,
        int txContentCacheSize,
        long txContentCacheBytes,
        long maxByteSizePerEntry) {
        try {
            objectsCache = new TxCacheWrapper(globalObjectCacheSize, "object");
            permissionsCache = new TxCacheWrapper(globalPermissionCacheSize, "permission");
            locksCache = new TxCacheWrapper(globalLockCacheSize, "lock");
            descriptorsCache = new TxCacheWrapper(globalDescrtiptorsCacheSize, "descriptors");
            descriptorCache = new TxCacheWrapper(globalDescrtiptorCacheSize, "descriptor");

            if (contentCachingEnabled) {
                contentCache =
                    new TxContentCacheWrapper(
                        new ByteSizeLimitedObjectCache(
                            globalContentCacheSize,
                            txContentCacheSize,
                            contentCacheBytes,
                            txContentCacheBytes,
                            maxByteSizePerEntry,
                            getName() + ".content",
                            getLogger()));
            } else {
                contentCache = null;
            }
        } catch (Error e) {
            fatalError(e);
        } catch (RuntimeException re) {
            fatalError(re);
        }
    }

    // could be out of memory etc.
    protected void fatalError(Error e) {
        getLogger().log("Fatal error: " + e, LOG_CHANNEL, Logger.CRITICAL);
        // XXX needed to guarantee, stack trace will be displayed
        getLogger().log(e, LOG_CHANNEL, Logger.CRITICAL);
        setRollbackOnly();
        throw e;
    }

    // could be  null pointer or any other programming error
    protected void fatalError(RuntimeException re) {
        getLogger().log("Fatal error: " + re, LOG_CHANNEL, Logger.CRITICAL);
        // XXX needed to guarantee, stack trace will be displayed
        getLogger().log(re, LOG_CHANNEL, Logger.CRITICAL);
        setRollbackOnly();
        throw re;
    }

    // XXX HACK checks if the requested uri is the history folder. In this
    // case caching must be disabled to pass any lock operations to the physical store.
    // This is needed as part of the fix to the problem described in
    // bug #26913
    protected boolean omitCachingBecauseOfEvilHistoryHack(Uri uri) {
        return ("/history".equals(uri.toString()));
    }

    protected class TxCacheWrapper implements ObjectCache {

        private TxLRUObjectCache txCache;

        public TxCacheWrapper(TxLRUObjectCache txCache) {
            this.txCache = txCache;
        }

        public TxCacheWrapper(int globalCacheSize, String name) {
            this(new TxLRUObjectCache(globalCacheSize, getName() + "." + name, getLogger()));
        }

        public Object get(Object key) {
            if (globalCacheOff)
                return null;
            try {
                Xid txId = (Xid) activeTransactionBranch.get();
                return txCache.get(txId, key);
            } catch (Error e) {
                fatalError(e);
            } catch (RuntimeException re) {
                fatalError(re);
            }
            // XXX will never be called, as fatalError will always throw a throwable, just satisfy compiler
            return null;
        }

        public void put(Object key, Object value) {
            if (globalCacheOff)
                return;
            try {
                Xid txId = (Xid) activeTransactionBranch.get();
                txCache.put(txId, key, value);
            } catch (Error e) {
                fatalError(e);
            } catch (RuntimeException re) {
                fatalError(re);
            }
        }

        public void remove(Object key) {
            if (globalCacheOff)
                return;
            try {
                Xid txId = (Xid) activeTransactionBranch.get();
                txCache.remove(txId, key);
                getLogger().log("Removing content at '" + key + "' from cache", LOG_CHANNEL, Logger.DEBUG);
            } catch (Error e) {
                fatalError(e);
            } catch (RuntimeException re) {
                fatalError(re);
            }
        }

        public void clear() {
            try {
                txCache.clear();
            } catch (Error e) {
                fatalError(e);
            } catch (RuntimeException re) {
                fatalError(re);
            }
        }

        public TxLRUObjectCache getTxCache() {
            return txCache;
        }

    }

    protected class TxContentCacheWrapper extends TxCacheWrapper {

        public TxContentCacheWrapper(ByteSizeLimitedObjectCache txCache) {
            super(txCache);
        }

        /*
         * Ensures that if revisionContent will be cached later, it will be accessible multiple times.
         * It does so by copying its input stream into a byte array. To avoid excessively large byte
         * arrays this is only done when revisionContent fits into the cache and will be cached later anyway.
         * <br>
         * <br>
         * <em>Caution</em>: This method must be called before any other method modifies revisionContent.
         *  
         */
        public void precache(NodeRevisionContent revisionContent, long byteSize) {
            if (globalCacheOff)
                return;
            try {
                Xid txId = (Xid) activeTransactionBranch.get();
                if (((ByteSizeLimitedObjectCache) getTxCache()).canCache(txId, byteSize)) {
                    // buffer stream content into byte array for multiple access
                    revisionContent.getContentBytes();
                }
            } catch (Error e) {
                fatalError(e);
            } catch (RuntimeException re) {
                fatalError(re);
            }
        }

        public void putForRead(Object key, NodeRevisionContent revisionContent, long byteSize) {
            if (globalCacheOff)
                return;
            try {
                if (((ByteSizeLimitedObjectCache) getTxCache()).canCache(null, byteSize)) {

                    // buffer stream content into byte array for multiple access
                    revisionContent.getContentBytes();

                    ((ByteSizeLimitedObjectCache) getTxCache()).put(null, key, revisionContent, byteSize);
                    getLogger().log(
                        "Globally caching content at '" + key + "' with " + byteSize + " bytes",
                        LOG_CHANNEL,
                        Logger.DEBUG);
                }

            } catch (Error e) {
                fatalError(e);
            } catch (RuntimeException re) {
                fatalError(re);
            }
        }

        public void put(Object key, NodeRevisionContent revisionContent, long byteSize) {
            if (globalCacheOff)
                return;
            try {
                Xid txId = (Xid) activeTransactionBranch.get();
                if (((ByteSizeLimitedObjectCache) getTxCache()).canCache(txId, byteSize)) {

                    // buffer stream content into byte array for multiple access
                    revisionContent.getContentBytes();

                    ((ByteSizeLimitedObjectCache) getTxCache()).put(txId, key, revisionContent, byteSize);
                    getLogger().log(
                        "Caching content at '" + key + "' with " + byteSize + " bytes",
                        LOG_CHANNEL,
                        Logger.DEBUG);
                } else {
                    // if we can not cache it, we need to invalidate global entry upon commit
                    getTxCache().remove(txId, key);
                }

            } catch (Error e) {
                fatalError(e);
            } catch (RuntimeException re) {
                fatalError(re);
            }
        }

    }

}
TOP

Related Classes of org.apache.slide.store.ExtendedStore$TxContentCacheWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.