Package org.apache.jackrabbit.core.version

Source Code of org.apache.jackrabbit.core.version.XAVersionManager

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.jackrabbit.core.version;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;

import org.apache.jackrabbit.core.InternalXAResource;
import org.apache.jackrabbit.core.id.ItemId;
import org.apache.jackrabbit.core.id.NodeId;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.TransactionContext;
import org.apache.jackrabbit.core.TransactionException;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
import org.apache.jackrabbit.core.state.ChangeLog;
import org.apache.jackrabbit.core.state.ItemState;
import org.apache.jackrabbit.core.state.ItemStateCacheFactory;
import org.apache.jackrabbit.core.state.ItemStateException;
import org.apache.jackrabbit.core.state.ItemStateListener;
import org.apache.jackrabbit.core.state.NoSuchItemStateException;
import org.apache.jackrabbit.core.state.NodeReferences;
import org.apache.jackrabbit.core.id.NodeReferencesId;
import org.apache.jackrabbit.core.state.NodeState;
import org.apache.jackrabbit.core.state.XAItemStateManager;
import org.apache.jackrabbit.core.state.ISMLocking.ReadLock;
import org.apache.jackrabbit.core.state.ISMLocking.WriteLock;
import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
import org.apache.jackrabbit.core.virtual.VirtualNodeState;
import org.apache.jackrabbit.core.virtual.VirtualPropertyState;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.commons.name.NameConstants;

/**
* Implementation of a {@link VersionManager} that works in an XA environment.
* Works as a filter between a version manager client and the global version
* manager.
*/
public class XAVersionManager extends AbstractVersionManager
        implements EventStateCollectionFactory, VirtualItemStateProvider, InternalXAResource {

    /**
     * Attribute name for associated change log.
     */
    private static final String CHANGE_LOG_ATTRIBUTE_NAME = "XAVersionManager.ChangeLog";

    /**
     * Attribute name for items.
     */
    private static final String ITEMS_ATTRIBUTE_NAME = "VersionItems";

    /**
     * Repository version manager.
     */
    private final VersionManagerImpl vMgr;

    /**
     * The session that uses this version manager.
     */
    private SessionImpl session;

    /**
     * Items that have been modified and are part of the XA environment.
     */
    private Map<NodeId, InternalVersionItem> xaItems;

    /**
     * flag that indicates if the version manager was locked during prepare
     */
    private boolean vmgrLocked = false;

    /**
     * The global write lock on the version manager.
     */
    private WriteLock vmgrLock;

    /**
     * Creates a new instance of this class.
     *
     * @param vMgr the underlying version manager
     * @param ntReg node type registry
     * @param session the session
     * @param cacheFactory cache factory
     * @throws RepositoryException if a an error occurs
     */
    public XAVersionManager(VersionManagerImpl vMgr, NodeTypeRegistry ntReg,
                            SessionImpl session, ItemStateCacheFactory cacheFactory)
            throws RepositoryException {
        super(ntReg);
        this.vMgr = vMgr;
        this.session = session;
        this.stateMgr = XAItemStateManager.createInstance(vMgr.getSharedStateMgr(),
                this, CHANGE_LOG_ATTRIBUTE_NAME, cacheFactory);

        NodeState state;
        try {
            state = (NodeState) stateMgr.getItemState(vMgr.getHistoryRootId());
        } catch (ItemStateException e) {
            throw new RepositoryException("Unable to retrieve history root", e);
        }
        this.historyRoot = new NodeStateEx(stateMgr, ntReg, state, NameConstants.JCR_VERSIONSTORAGE);
    }

    //------------------------------------------< EventStateCollectionFactory >

    /**
     * @inheritDoc
     */
    public EventStateCollection createEventStateCollection()
            throws RepositoryException {
        return vMgr.getEscFactory().createEventStateCollection(session);
    }

    //-------------------------------------------------------< VersionManager >

    /**
     * {@inheritDoc}
     */
    public VirtualItemStateProvider getVirtualItemStateProvider() {
        return this;
    }

    /**
     * {@inheritDoc}
     */
    protected VersionHistoryInfo createVersionHistory(Session session,
                                                      NodeState node,
                                                      NodeId copiedFrom)
            throws RepositoryException {

        if (isInXA()) {
            NodeStateEx state = internalCreateVersionHistory(node, copiedFrom);
            InternalVersionHistory history =
                new InternalVersionHistoryImpl(vMgr, state);
            xaItems.put(state.getNodeId(), history);
            Name root = NameConstants.JCR_ROOTVERSION;
            return new VersionHistoryInfo(
                    state.getNodeId(),
                    state.getState().getChildNodeEntry(root, 1).getId());
        }
        return vMgr.createVersionHistory(session, node, copiedFrom);
    }

    /**
     * {@inheritDoc}
     */
    public NodeId createActivity(Session session, String title)
            throws RepositoryException {

        if (isInXA()) {
            NodeStateEx state = internalCreateActivity(title);
            InternalActivityImpl activity =
                new InternalActivityImpl(vMgr, state);
            xaItems.put(state.getNodeId(), activity);
            return state.getNodeId();
        }
        return vMgr.createActivity(session, title);
    }

    /**
     * {@inheritDoc}
     */
    public void removeActivity(Session session, NodeId nodeId)
            throws RepositoryException {

        if (isInXA()) {
            InternalActivityImpl act = (InternalActivityImpl) getItem(nodeId);
            internalRemoveActivity(act);
        }
        vMgr.removeActivity(session, nodeId);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Before modifying activity, make a local copy of it.
     */
    protected void internalRemoveActivity(InternalActivityImpl activity)
            throws VersionException, RepositoryException {
        if (activity.getVersionManager() != this) {
            activity = makeLocalCopy(activity);
            xaItems.put(activity.getId(), activity);
        }
        super.internalRemoveActivity(activity);
    }

    /**
     * {@inheritDoc}
     */
    public Version checkout(NodeImpl node) throws RepositoryException {
        return vMgr.checkout(node);
    }

    /**
     * {@inheritDoc}
     */
    public Version checkin(NodeImpl node) throws RepositoryException {
        if (isInXA()) {
            InternalVersionHistory vh;
            InternalVersion version;
            if (node.isNodeType(NameConstants.MIX_VERSIONABLE)) {
                // in full versioning, the history id can be retrieved via
                // the property
                String histUUID = node.getProperty(NameConstants.JCR_VERSIONHISTORY).getString();
                vh = getVersionHistory(NodeId.valueOf(histUUID));
                version = internalCheckin((InternalVersionHistoryImpl) vh, node, false);
            } else {
                // in simple versioning the history id needs to be calculated
                vh = getVersionHistoryOfNode(node.getNodeId());
                version = internalCheckin((InternalVersionHistoryImpl) vh, node, true);
            }
            return (Version) ((SessionImpl) node.getSession()).getNodeById(version.getId());
        }
        return vMgr.checkin(node);
    }

    /**
     * {@inheritDoc}
     */
    public void removeVersion(VersionHistory history, Name versionName)
            throws RepositoryException {

        if (isInXA()) {
            InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
                    ((VersionHistoryImpl) history).getInternalVersionHistory();
            internalRemoveVersion(vh, versionName);
            return;
        }
        vMgr.removeVersion(history, versionName);
    }

    /**
     * {@inheritDoc}
     */
    public Version setVersionLabel(VersionHistory history, Name version,
                                   Name label, boolean move)
            throws RepositoryException {

        if (isInXA()) {
            InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
                    ((VersionHistoryImpl) history).getInternalVersionHistory();
            InternalVersion v = setVersionLabel(vh, version, label, move);
            if (v == null) {
                return null;
            } else {
                return (Version) ((SessionImpl) history.getSession()).getNodeById(v.getId());
            }
        }
        return vMgr.setVersionLabel(history, version, label, move);
    }

    /**
     * {@inheritDoc}
     */
    public void close() throws Exception {
        stateMgr.dispose();
    }

    //---------------------------------------------< VirtualItemStateProvider >

    /**
     * {@inheritDoc}
     */
    public boolean isVirtualRoot(ItemId id) {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public NodeId getVirtualRootId() {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public VirtualPropertyState createPropertyState(VirtualNodeState parent,
                                                    Name name, int type,
                                                    boolean multiValued)
            throws RepositoryException {

        throw new IllegalStateException("Read-only");
    }

    /**
     * {@inheritDoc}
     */
    public VirtualNodeState createNodeState(VirtualNodeState parent, Name name,
                                            NodeId id, Name nodeTypeName)
            throws RepositoryException {

        throw new IllegalStateException("Read-only");
    }

    /**
     * {@inheritDoc}
     */
    public boolean setNodeReferences(ChangeLog references) {
        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
        if (changeLog != null) {
            Iterator iterator = references.modifiedRefs();
            while (iterator.hasNext()) {
                changeLog.modified((NodeReferences) iterator.next());
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Return item states for changes only. Global version manager will return
     * other items.
     */
    public ItemState getItemState(ItemId id)
            throws NoSuchItemStateException, ItemStateException {

        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
        if (changeLog != null) {
            return changeLog.get(id);
        }
        throw new NoSuchItemStateException("State not in change log: " + id);
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasItemState(ItemId id) {
        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
        if (changeLog != null) {
            return changeLog.has(id);
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public NodeReferences getNodeReferences(NodeReferencesId id)
            throws NoSuchItemStateException, ItemStateException {

        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
        if (changeLog != null) {
            return changeLog.get(id);
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasNodeReferences(NodeReferencesId id) {
        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
        if (changeLog != null) {
            return changeLog.get(id) != null;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Not needed.
     */
    public void addListener(ItemStateListener listener) {
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Not needed.
     */
    public void removeListener(ItemStateListener listener) {
    }

    //-----------------------------------------------< AbstractVersionManager >

    /**
     * {@inheritDoc}
     */
     protected InternalVersionItem getItem(NodeId id) throws RepositoryException {
        InternalVersionItem item = null;
        if (xaItems != null) {
            item = xaItems.get(id);
        }
        if (item == null) {
            item = vMgr.getItem(id);
        }
        return item;
    }

    /**
     * {@inheritDoc}
     */
    protected boolean hasItem(NodeId id) {
        if (xaItems != null && xaItems.containsKey(id)) {
            return true;
        }
        return vMgr.hasItem(id);
    }

    /**
     * {@inheritDoc}
     */
    protected boolean hasItemReferences(NodeId id)
            throws RepositoryException {
        return session.getNodeById(id).getReferences().hasNext();
    }

    /**
     * {@inheritDoc}
     */
    protected NodeStateEx getNodeStateEx(NodeId parentNodeId)
            throws RepositoryException {
        try {
            NodeState state = (NodeState) stateMgr.getItemState(parentNodeId);
            return new NodeStateEx(stateMgr, ntReg, state, null);
        } catch (ItemStateException e) {
            throw new RepositoryException(e);
        }
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Before modifying version history given, make a local copy of it.
     */
    protected InternalVersion internalCheckin(InternalVersionHistoryImpl history,
                                      NodeImpl node, boolean simple)
            throws RepositoryException {

        if (history.getVersionManager() != this) {
            history = makeLocalCopy(history);
            xaItems.put(history.getId(), history);
        }
        InternalVersion version = super.internalCheckin(history, node, simple);
        NodeId frozenNodeId = version.getFrozenNodeId();
        InternalVersionItem frozenNode = createInternalVersionItem(frozenNodeId);
        if (frozenNode != null) {
            xaItems.put(frozenNodeId, frozenNode);
        }
        return version;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Before modifying version history given, make a local copy of it.
     */
    protected void internalRemoveVersion(InternalVersionHistoryImpl history, Name name)
            throws VersionException, RepositoryException {

        if (history.getVersionManager() != this) {
            history = makeLocalCopy(history);
            xaItems.put(history.getId(), history);
            // also put 'successor' and 'predecessor' version items to xaItem sets
            InternalVersion v = history.getVersion(name);
            InternalVersion[] vs = v.getSuccessors();
            for (InternalVersion v1 : vs) {
                xaItems.put(v1.getId(), v1);
            }
            vs = v.getPredecessors();
            for (InternalVersion v1 : vs) {
                xaItems.put(v1.getId(), v1);
            }
        }
        super.internalRemoveVersion(history, name);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Before modifying version history given, make a local copy of it.
     */
    protected InternalVersion setVersionLabel(InternalVersionHistoryImpl history,
                                              Name version, Name label,
                                              boolean move)
            throws RepositoryException {

        if (history.getVersionManager() != this) {
            history = makeLocalCopy(history);
            xaItems.put(history.getId(), history);
        }
        return super.setVersionLabel(history, version, label, move);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Put the version object into our cache.
     */
    protected void versionCreated(InternalVersion version) {
        xaItems.put(version.getId(), version);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Remove the version object from our cache.
     */
    protected void versionDestroyed(InternalVersion version) {
        xaItems.remove(version.getId());
    }

    //-------------------------------------------------------------------< XA >

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public void associate(TransactionContext tx) {
        ((XAItemStateManager) stateMgr).associate(tx);

        Map<NodeId, InternalVersionItem> xaItems = null;
        if (tx != null) {
            xaItems = (Map<NodeId, InternalVersionItem>) tx.getAttribute(ITEMS_ATTRIBUTE_NAME);
            if (xaItems == null) {
                xaItems = new HashMap<NodeId, InternalVersionItem>();
                tx.setAttribute(ITEMS_ATTRIBUTE_NAME, xaItems);
            }
        }
        this.xaItems = xaItems;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Delegate the call to our XA item state manager.
     */
    public void beforeOperation(TransactionContext tx) {
        ((XAItemStateManager) stateMgr).beforeOperation(tx);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Delegate the call to our XA item state manager.
     */
    public void prepare(TransactionContext tx) throws TransactionException {
        if (vmgrLocked) {
            ((XAItemStateManager) stateMgr).prepare(tx);
        }
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Delegate the call to our XA item state manager. If successful, inform
     * global repository manager to update its caches.
     */
    @SuppressWarnings("unchecked")
    public void commit(TransactionContext tx) throws TransactionException {
        if (vmgrLocked) {
            ((XAItemStateManager) stateMgr).commit(tx);
            Map<NodeId, InternalVersionItem> xaItems =
                    (Map<NodeId, InternalVersionItem>) tx.getAttribute(ITEMS_ATTRIBUTE_NAME);
            vMgr.itemsUpdated(xaItems.values());
        }
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Delegate the call to our XA item state manager.
     */
    public void rollback(TransactionContext tx) {
        if (vmgrLocked) {
            ((XAItemStateManager) stateMgr).rollback(tx);
        }
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Delegate the call to our XA item state manager.
     */
    public void afterOperation(TransactionContext tx) {
        ((XAItemStateManager) stateMgr).afterOperation(tx);
    }

    /**
     * Returns an {@link InternalXAResource} that acquires a write lock on the
     * version manager in {@link InternalXAResource#prepare(TransactionContext)}.
     *
     * @return an internal XA resource.
     */
    public InternalXAResource getXAResourceBegin() {
        return new InternalXAResource() {
            public void associate(TransactionContext tx) {
            }

            public void beforeOperation(TransactionContext tx) {
            }

            public void prepare(TransactionContext tx) {
                vmgrLock = vMgr.acquireWriteLock();
                vmgrLocked = true;
            }

            public void commit(TransactionContext tx) {
            }

            public void rollback(TransactionContext tx) {
            }

            public void afterOperation(TransactionContext tx) {
            }
        };
    }

    /**
     * Returns an {@link InternalXAResource} that releases the write lock on the
     * version manager in {@link InternalXAResource#commit(TransactionContext)}
     * or {@link InternalXAResource#rollback(TransactionContext)}.
     *
     * @return an internal XA resource.
     */
    public InternalXAResource getXAResourceEnd() {
        return new InternalXAResource() {
            public void associate(TransactionContext tx) {
            }

            public void beforeOperation(TransactionContext tx) {
            }

            public void prepare(TransactionContext tx) {
            }

            public void commit(TransactionContext tx) {
                internalReleaseWriteLock();
            }

            public void rollback(TransactionContext tx) {
                internalReleaseWriteLock();
            }

            public void afterOperation(TransactionContext tx) {
            }

            private void internalReleaseWriteLock() {
                if (vmgrLocked) {
                    vmgrLock.release();
                    vmgrLocked = false;
                }
            }
        };
    }

    //-------------------------------------------------------< implementation >

    /**
     * Return a flag indicating whether this version manager is currently
     * associated with an XA transaction.
     * @return <code>true</code> if the version manager is in a transaction
     */
    private boolean isInXA() {
        return xaItems != null;
    }

    /**
     * Make a local copy of an internal version item. This will recreate the
     * (global) version item with state information from our own state
     * manager.
     * @param history source
     * @return the new copy
     * @throws RepositoryException if an error occurs
     */
    private InternalVersionHistoryImpl makeLocalCopy(InternalVersionHistoryImpl history)
            throws RepositoryException {
        ReadLock lock = acquireReadLock();
        try {
            NodeState state = (NodeState) stateMgr.getItemState(history.getId());
            NodeStateEx stateEx = new NodeStateEx(stateMgr, ntReg, state, null);
            return new InternalVersionHistoryImpl(this, stateEx);
        } catch (ItemStateException e) {
            throw new RepositoryException("Unable to make local copy", e);
        } finally {
            lock.release();
        }
    }

    /**
     * Make a local copy of an internal version item. This will recreate the
     * (global) version item with state information from our own state
     * manager.
     * @param act source
     * @return the new copy
     * @throws RepositoryException if an error occurs
     */
    private InternalActivityImpl makeLocalCopy(InternalActivityImpl act)
            throws RepositoryException {
        ReadLock lock = acquireReadLock();
        try {
            NodeState state = (NodeState) stateMgr.getItemState(act.getId());
            NodeStateEx stateEx = new NodeStateEx(stateMgr, ntReg, state, null);
            return new InternalActivityImpl(this, stateEx);
        } catch (ItemStateException e) {
            throw new RepositoryException("Unable to make local copy", e);
        } finally {
            lock.release();
        }
    }

    /**
     * Return a flag indicating whether an internal version item belongs to
     * a different XA environment.
     * @param item the item to check
     * @return <code>true</code> if in a different env
     */
    boolean differentXAEnv(InternalVersionItemImpl item) {
        if (item.getVersionManager() == this) {
            if (xaItems == null || !xaItems.containsKey(item.getId())) {
                return true;
            }
        }
        return false;
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.version.XAVersionManager

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.