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 org.apache.jackrabbit.core.InternalXAResource;
import org.apache.jackrabbit.core.ItemId;
import org.apache.jackrabbit.core.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.state.NodeReferencesId;
import org.apache.jackrabbit.core.state.NodeState;
import org.apache.jackrabbit.core.state.XAItemStateManager;
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.name.QName;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

    /**
     * Node type registry.
     */
    private NodeTypeRegistry ntReg;

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

    /**
     * Items that have been modified and are part of the XA environment.
     */
    private Map xaItems;

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

    /**
     * Creates a new instance of this class.
     */
    public XAVersionManager(VersionManagerImpl vMgr, NodeTypeRegistry ntReg,
                            SessionImpl session, ItemStateCacheFactory cacheFactory)
            throws RepositoryException {

        this.vMgr = vMgr;
        this.ntReg = ntReg;
        this.session = session;
        this.stateMgr = new XAItemStateManager(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, QName.JCR_VERSIONSTORAGE);
    }

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

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

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

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

    /**
     * {@inheritDoc}
     */
    public VersionHistory createVersionHistory(Session session, NodeState node)
            throws RepositoryException {

        if (isInXA()) {
            InternalVersionHistory history = createVersionHistory(node);
            xaItems.put(history.getId(), history);
            return (VersionHistory) ((SessionImpl) session).getNodeById(history.getId());
        }
        return vMgr.createVersionHistory(session, node);
    }

    /**
     * {@inheritDoc}
     */
    public Version checkin(NodeImpl node) throws RepositoryException {
        if (isInXA()) {
            String histUUID = node.getProperty(QName.JCR_VERSIONHISTORY).getString();
            InternalVersion version = checkin((InternalVersionHistoryImpl)
                    getVersionHistory(NodeId.valueOf(histUUID)), node);
            return (Version) ((SessionImpl) node.getSession()).getNodeById(version.getId());
        }
        return vMgr.checkin(node);
    }

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

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

    /**
     * {@inheritDoc}
     */
    public Version setVersionLabel(VersionHistory history, QName version,
                                   QName 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,
                                                    QName name, int type,
                                                    boolean multiValued)
            throws RepositoryException {

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

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

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

    /**
     * {@inheritDoc}
     */
    public boolean setNodeReferences(NodeReferences refs) {
        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
        if (changeLog != null) {
            changeLog.modified(refs);
            return true;
        }
        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 = (InternalVersionItem) 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 List getItemReferences(InternalVersionItem item) {
        return vMgr.getItemReferences(item);
    }

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

        if (history.getVersionManager() != this) {
            history = makeLocalCopy(history);
            xaItems.put(history.getId(), history);
        }
        return super.checkin(history, node);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Before modifying version history given, make a local copy of it.
     */
    protected void removeVersion(InternalVersionHistoryImpl history, QName 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 (int i=0; i<vs.length; i++) {
                xaItems.put(vs[i].getId(), vs[i]);
            }
            vs = v.getPredecessors();
            for (int i=0; i<vs.length; i++) {
                xaItems.put(vs[i].getId(), vs[i]);
            }
        }
        super.removeVersion(history, name);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Before modifying version history given, make a local copy of it.
     */
    protected InternalVersion setVersionLabel(InternalVersionHistoryImpl history,
                                              QName version, QName 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}
     */
    public void associate(TransactionContext tx) {
        ((XAItemStateManager) stateMgr).associate(tx);

        Map xaItems = null;
        if (tx != null) {
            xaItems = (Map) tx.getAttribute(ITEMS_ATTRIBUTE_NAME);
            if (xaItems == null) {
                xaItems = new HashMap();
                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 {
        ((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.
     */
    public void commit(TransactionContext tx) throws TransactionException {
        ((XAItemStateManager) stateMgr).commit(tx);
        Map xaItems = (Map) 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) {
        ((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)}
     * if there are any version related items involved in this transaction.
     *
     * @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) {
                Map vItems = (Map) tx.getAttribute(ITEMS_ATTRIBUTE_NAME);
                if (!vItems.isEmpty()) {
                    vMgr.acquireWriteLock();
                    vMgr.getSharedStateMgr().setNoLockHack(true);
                    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) {
                    vMgr.getSharedStateMgr().setNoLockHack(false);
                    vMgr.releaseWriteLock();
                    vmgrLocked = false;
                }
            }
        };
    }

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

    /**
     * Return a flag indicating whether this version manager is currently
     * associated with an XA 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.
     */
    private InternalVersionHistoryImpl makeLocalCopy(InternalVersionHistoryImpl history)
            throws RepositoryException {
        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 {
            releaseReadLock();
        }
    }

    /**
     * Return a flag indicating whether an internal version item belongs to
     * a different XA environment.
     */
    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.