Package org.apache.jackrabbit.core.cluster

Source Code of org.apache.jackrabbit.core.cluster.ClusterNode$WorkspaceUpdateChannel

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.jackrabbit.core.config.ClusterConfig;
import org.apache.jackrabbit.core.config.ConfigurationException;
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
import org.apache.jackrabbit.core.observation.EventState;
import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.state.ChangeLog;
import EDU.oswego.cs.dl.util.concurrent.Mutex;

import javax.jcr.RepositoryException;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Collection;

/**
* Default clustered node implementation.
*/
public class ClusterNode implements Runnable, UpdateEventChannel,
        NamespaceEventChannel, NodeTypeEventChannel  {

    /**
     * System property specifying a node id to use.
     */
    public static final String SYSTEM_PROPERTY_NODE_ID = "org.apache.jackrabbit.core.cluster.node_id";

    /**
     * Used for padding short string representations.
     */
    private static final String SHORT_PADDING = "0000";

    /**
     * Status constant.
     */
    private static final int NONE = 0;

    /**
     * Status constant.
     */
    private static final int STARTED = 1;

    /**
     * Status constant.
     */
    private static final int STOPPED = 2;

    /**
     * Logger.
     */
    private static Logger log = LoggerFactory.getLogger(ClusterNode.class);

    /**
     * Cluster context.
     */
    private ClusterContext clusterContext;

    /**
     * Cluster node id.
     */
    private String clusterNodeId;

    /**
     * Synchronization delay, in seconds.
     */
    private int syncDelay;

    /**
     * Journal used.
     */
    private Journal journal;

    /**
     * Mutex used when syncing.
     */
    private final Mutex syncLock = new Mutex();

    /**
     * Status flag, one of {@link #NONE}, {@link #STARTED} or {@link #STOPPED}.
     */
    private int status;

    /**
     * Map of available lock listeners, indexed by workspace name.
     */
    private final Map wspLockListeners = new HashMap();

    /**
     * Map of available update listeners, indexed by workspace name.
     */
    private final Map wspUpdateListeners = new HashMap();

    /**
     * Versioning update listener.
     */
    private UpdateEventListener versionUpdateListener;

    /**
     * Namespace listener.
     */
    private NamespaceEventListener namespaceListener;

    /**
     * Node type listener.
     */
    private NodeTypeEventListener nodeTypeListener;

    /**
     * Initialize this cluster node.
     *
     * @throws ClusterException if an error occurs
     */
    public void init(ClusterContext clusterContext) throws ClusterException {
        this.clusterContext = clusterContext;

        init();
    }

    /**
     * Initialize this cluster node (overridable).
     *
     * @throws ClusterException if an error occurs
     */
    protected void init() throws ClusterException {
        ClusterConfig cc = clusterContext.getClusterConfig();
        clusterNodeId = getClusterNodeId(cc.getId());
        syncDelay = cc.getSyncDelay();

        try {
            journal = (Journal) cc.getJournalConfig().newInstance();
            journal.init(clusterNodeId, new SyncListener(), clusterContext.getNamespaceResovler());
        } catch (ConfigurationException e) {
            throw new ClusterException(e.getMessage(), e.getCause());
        }
    }

    /**
     * Starts this cluster node.
     *
     * @throws ClusterException if an error occurs
     */
    public synchronized void start() throws ClusterException {
        if (status == NONE) {
            sync();

            Thread t = new Thread(this, "ClusterNode-" + clusterNodeId);
            t.setDaemon(true);
            t.start();

            status = STARTED;
        }
    }

    /**
     * Run loop that will sync this node after some delay.
     */
    public void run() {
        for (;;) {
            synchronized (this) {
                try {
                    wait(syncDelay * 1000);
                } catch (InterruptedException e) {}

                if (status == STOPPED) {
                    return;
                }
            }
            try {
                sync();
            } catch (ClusterException e) {
                String msg = "Periodic sync of journal failed: " + e.getMessage();
                log.error(msg);
            } catch (Exception e) {
                String msg = "Unexpected error while syncing of journal: " + e.getMessage();
                log.error(msg, e);
            } catch (Error e) {
                String msg = "Unexpected error while syncing of journal: " + e.getMessage();
                log.error(msg, e);
                throw e;
            }
        }
    }

    /**
     * Synchronize contents from journal.
     *
     * @throws ClusterException if an error occurs
     */
    public void sync() throws ClusterException {
        try {
            syncLock.acquire();
        } catch (InterruptedException e) {
            String msg = "Interrupted while waiting for mutex.";
            throw new ClusterException(msg);
        }
        try {
            journal.sync();
        } finally {
            syncLock.release();
        }
    }

    /**
     * Stops this cluster node.
     */
    public synchronized void stop() {
        if (status == STARTED) {
            status = STOPPED;

            journal.close();
            notifyAll();
        }
    }

    /**
     * Called when a node has been locked.
     *
     * @param workspace workspace name
     * @param nodeId node id
     * @param deep flag indicating whether lock is deep
     * @param owner lock owner
     */
    private void locked(String workspace, NodeId nodeId, boolean deep, String owner) {
        if (status != STARTED) {
            log.info("not started: lock operation ignored.");
            return;
        }
        boolean succeeded = false;

        try {
            journal.begin(workspace);
            journal.log(nodeId, deep, owner);
            journal.prepare();
            journal.commit();
            succeeded = true;
        } catch (JournalException e) {
            String msg = "Unable to create log entry: " + e.getMessage();
            log.error(msg);
        } catch (Throwable e) {
            String msg = "Unexpected error while creating log entry.";
            log.error(msg, e);
        } finally {
            if (!succeeded) {
                journal.cancel();
            }
        }
    }

    /**
     * Called when a node has been unlocked.
     *
     * @param workspace workspace name
     * @param nodeId node id
     */
    private void unlocked(String workspace, NodeId nodeId) {
        if (status != STARTED) {
            log.info("not started: unlock operation ignored.");
            return;
        }
        boolean succeeded = false;

        try {
            journal.begin(workspace);
            journal.log(nodeId);
            journal.prepare();
            journal.commit();
            succeeded = true;
        } catch (JournalException e) {
            String msg = "Unable to create log entry: " + e.getMessage();
            log.error(msg);
        } catch (Throwable e) {
            String msg = "Unexpected error while creating log entry.";
            log.error(msg, e);
        } finally {
            if (!succeeded) {
                journal.cancel();
            }
        }
    }

    /**
     * Create an {@link UpdateEventChannel} for some workspace.
     *
     * @param workspace workspace name
     * @return lock event channel
     */
    public UpdateEventChannel createUpdateChannel(String workspace) {
        return new WorkspaceUpdateChannel(workspace);
    }

    /**
     * Create a {@link LockEventChannel} for some workspace.
     *
     * @param workspace workspace name
     * @return lock event channel
     */
    public LockEventChannel createLockChannel(String workspace) {
        return new WorkspaceLockChannel(workspace);
    }

    /**
     * Return the instance id to be used for this node in the cluster.
     *
     * @param id configured id, <code>null</code> to take random id
     */
    private String getClusterNodeId(String id) {
        if (id == null) {
            id = System.getProperty(SYSTEM_PROPERTY_NODE_ID);
            if (id == null) {
                id = toHexString((short) (Math.random() * (Short.MAX_VALUE - Short.MIN_VALUE)));
            }
        }
        return id;
    }

    /**
     * Return a zero-padded short string representation.
     *
     * @param n short
     * @return string representation
     */
    private static String toHexString(short n) {
        String s = Integer.toHexString(n);
        int padlen = SHORT_PADDING.length() - s.length();
        if (padlen < 0) {
            s = s.substring(-padlen);
        } else if (padlen > 0) {
            s = SHORT_PADDING.substring(0, padlen) + s;
        }
        return s;
    }

    //--------------------------------------------------< UpdateEventListener >

    /**
     * {@inheritDoc}
     */
    public void updateCreated() {
        if (status != STARTED) {
            log.info("not started: update create ignored.");
            return;
        }
        try {
            sync();
        } catch (ClusterException e) {
            String msg = "Unable to sync with journal: " + e.getMessage();
            log.error(msg);
        } catch (Throwable e) {
            String msg = "Unexpected error while creating log entry.";
            log.error(msg, e);
        }
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Invoked when an update has been prepared inside versioning. Delegate
     * to common method with <code>null</code> workspace.
     */
    public void updatePrepared(ChangeLog changes, EventStateCollection esc) {
        updatePrepared(null, changes, esc);
    }

    /**
     * Called when an a update operation has been prepared.
     *
     * @param workspace workspace to use when writing journal entry
     * @param changes changes
     * @param esc events as they will be delivered on success
     */
    private void updatePrepared(String workspace, ChangeLog changes, EventStateCollection esc) {
        if (status != STARTED) {
            log.info("not started: update prepare ignored.");
            return;
        }
        boolean succeeded = false;

        try {
            journal.begin(workspace);
            journal.log(changes, esc);
            journal.prepare();
            succeeded = true;
        } catch (JournalException e) {
            String msg = "Unable to create log entry: " + e.getMessage();
            log.error(msg);
        } catch (Throwable e) {
            String msg = "Unexpected error while preparing log entry.";
            log.error(msg, e);
        } finally {
            if (!succeeded) {
                journal.cancel();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void updateCommitted() {
        if (status != STARTED) {
            log.info("not started: update commit ignored.");
            return;
        }
        try {
            journal.commit();
        } catch (JournalException e) {
            String msg = "Unable to create log entry: " + e.getMessage();
            log.error(msg);
        } catch (Throwable e) {
            String msg = "Unexpected error while committing log entry.";
            log.error(msg, e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void updateCancelled() {
        if (status != STARTED) {
            log.info("not started: update cancel ignored.");
            return;
        }
        journal.cancel();
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Invoked to set the update event listener responsible for delivering versioning events.
     */
    public void setListener(UpdateEventListener listener) {
        versionUpdateListener = listener;
    }

    //-----------------------------------------------< NamespaceEventListener >

    /**
     * {@inheritDoc}
     */
    public void remapped(String oldPrefix, String newPrefix, String uri) {
        if (status != STARTED) {
            log.info("not started: namespace operation ignored.");
            return;
        }
        boolean succeeded = false;

        try {
            journal.begin(null);
            journal.log(oldPrefix, newPrefix, uri);
            journal.prepare();
            journal.commit();
            succeeded = true;
        } catch (JournalException e) {
            String msg = "Unable to create log entry: " + e.getMessage();
            log.error(msg);
        } catch (Throwable e) {
            String msg = "Unexpected error while creating log entry.";
            log.error(msg, e);
        } finally {
            if (!succeeded) {
                journal.cancel();
            }
        }
    }

    public void setListener(NamespaceEventListener listener) {
        namespaceListener = listener;
    }

    //------------------------------------------------< NodeTypeEventListener >

    /**
     * {@inheritDoc}
     */
    public void registered(Collection ntDefs) {
        if (status != STARTED) {
            log.info("not started: nodetype operation ignored.");
            return;
        }
        boolean succeeded = false;

        try {
            journal.begin(null);
            journal.log(ntDefs);
            journal.prepare();
            journal.commit();
            succeeded = true;
        } catch (JournalException e) {
            String msg = "Unable to create log entry: " + e.getMessage();
            log.error(msg);
        } catch (Throwable e) {
            String msg = "Unexpected error while creating log entry.";
            log.error(msg, e);
        } finally {
            if (!succeeded) {
                journal.cancel();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void setListener(NodeTypeEventListener listener) {
        nodeTypeListener = listener;
    }


    /**
     * Workspace update channel.
     */
    class WorkspaceUpdateChannel implements UpdateEventChannel {

        /**
         * Workspace name.
         */
        private final String workspace;

        /**
         * Create a new instance of this class.
         *
         * @param workspace workspace name
         */
        public WorkspaceUpdateChannel(String workspace) {
            this.workspace = workspace;
        }

        /**
         * {@inheritDoc}
         */
        public void updateCreated() {
            ClusterNode.this.updateCreated();
        }

        /**
         * {@inheritDoc}
         */
        public void updatePrepared(ChangeLog changes, EventStateCollection esc) {
            ClusterNode.this.updatePrepared(workspace, changes, esc);
        }

        /**
         * {@inheritDoc}
         */
        public void updateCommitted() {
            ClusterNode.this.updateCommitted();
        }

        /**
         * {@inheritDoc}
         */
        public void updateCancelled() {
            ClusterNode.this.updateCancelled();
        }

        /**
         * {@inheritDoc}
         */
        public void setListener(UpdateEventListener listener) {
            wspUpdateListeners.remove(workspace);
            if (listener != null) {
                wspUpdateListeners.put(workspace, listener);
            }
        }
    }

    /**
     * Workspace lock channel.
     */
    class WorkspaceLockChannel implements LockEventChannel {

        /**
         * Workspace name.
         */
        private final String workspace;

        /**
         * Create a new instance of this class.
         *
         * @param workspace workspace name
         */
        public WorkspaceLockChannel(String workspace) {
            this.workspace = workspace;
        }

        /**
         * {@inheritDoc}
         */
        public void locked(NodeId nodeId, boolean deep, String owner) {
            ClusterNode.this.locked(workspace, nodeId, deep, owner);
        }

        /**
         * {@inheritDoc}
         */
        public void unlocked(NodeId nodeId) {
            ClusterNode.this.unlocked(workspace, nodeId);
        }

        /**
         * {@inheritDoc}
         */
        public void setListener(LockEventListener listener) {
            wspLockListeners.remove(workspace);
            if (listener != null) {
                wspLockListeners.put(workspace, listener);
            }
        }
    }

    /**
     * Sync listener on journal.
     */
    class SyncListener implements RecordProcessor {

        /**
         * Workspace name.
         */
        private String workspace;

        /**
         * Change log.
         */
        private ChangeLog changeLog;

        /**
         * List of recorded events.
         */
        private List events;

        /**
         * {@inheritDoc}
         */
        public void start(String workspace) {
            this.workspace = workspace;

            changeLog = new ChangeLog();
            events = new ArrayList();
        }

        /**
         * {@inheritDoc}
         */
        public void process(ItemOperation operation) {
            operation.apply(changeLog);
        }

        /**
         * {@inheritDoc}
         */
        public void process(EventState event) {
            events.add(event);
        }

        /**
         * {@inheritDoc}
         */
        public void process(NodeId nodeId, boolean isDeep, String owner) {
            LockEventListener listener = (LockEventListener) wspLockListeners.get(workspace);
            if (listener == null) {
                try {
                    clusterContext.lockEventsReady(workspace);
                } catch (RepositoryException e) {
                    String msg = "Unable to make lock listener for workspace " +
                            workspace + " online: " + e.getMessage();
                    log.warn(msg);
                }
                listener = (LockEventListener) wspLockListeners.get(workspace);
                if (listener ==  null) {
                    String msg = "Lock channel unavailable for workspace: " + workspace;
                    log.error(msg);
                    return;
                }
            }
            try {
                listener.externalLock(nodeId, isDeep, owner);
            } catch (RepositoryException e) {
                String msg = "Unable to deliver lock event: " + e.getMessage();
                log.error(msg);
            }
        }

        /**
         * {@inheritDoc}
         */
        public void process(NodeId nodeId) {
            LockEventListener listener = (LockEventListener) wspLockListeners.get(workspace);
            if (listener == null) {
                try {
                    clusterContext.lockEventsReady(workspace);
                } catch (RepositoryException e) {
                    String msg = "Unable to make lock listener for workspace " +
                            workspace + " online: " + e.getMessage();
                    log.warn(msg);
                }
                listener = (LockEventListener) wspLockListeners.get(workspace);
                if (listener ==  null) {
                    String msg = "Lock channel unavailable for workspace: " + workspace;
                    log.error(msg);
                    return;
                }
            }
            try {
                listener.externalUnlock(nodeId);
            } catch (RepositoryException e) {
                String msg = "Unable to deliver lock event: " + e.getMessage();
                log.error(msg);
            }
        }

        /**
         * {@inheritDoc}
         */
        public void process(String oldPrefix, String newPrefix, String uri) {
            if (namespaceListener == null) {
                String msg = "Namespace listener unavailable.";
                log.error(msg);
                return;
            }
            try {
                namespaceListener.externalRemap(oldPrefix, newPrefix, uri);
            } catch (RepositoryException e) {
                String msg = "Unable to deliver namespace operation: " + e.getMessage();
                log.error(msg);
            }
        }

        /**
         * {@inheritDoc}
         */
        public void process(Collection ntDefs) {
            if (nodeTypeListener == null) {
                String msg = "NodeType listener unavailable.";
                log.error(msg);
                return;
            }
            try {
                nodeTypeListener.externalRegistered(ntDefs);
            } catch (InvalidNodeTypeDefException e) {
                String msg = "Unable to deliver node type operation: " + e.getMessage();
                log.error(msg);
            } catch (RepositoryException e) {
                String msg = "Unable to deliver node type operation: " + e.getMessage();
                log.error(msg);
            }
        }

        /**
         * {@inheritDoc}
         */
        public void end() {
            UpdateEventListener listener = null;
            if (workspace != null) {
                listener = (UpdateEventListener) wspUpdateListeners.get(workspace);
                if (listener == null) {
                    try {
                        clusterContext.updateEventsReady(workspace);
                    } catch (RepositoryException e) {
                        String msg = "Error making update listener for workspace " +
                                workspace + " online: " + e.getMessage();
                        log.warn(msg);
                    }
                    listener = (UpdateEventListener) wspUpdateListeners.get(workspace);
                    if (listener ==  null) {
                        String msg = "Update listener unavailable for workspace: " + workspace;
                        log.error(msg);
                        return;
                    }
                }
            } else {
                if (versionUpdateListener != null) {
                    listener = versionUpdateListener;
                } else {
                    String msg = "Version update listener unavailable.";
                    log.error(msg);
                    return;
                }
            }
            try {
                listener.externalUpdate(changeLog, events);
            } catch (RepositoryException e) {
                String msg = "Unable to deliver update events: " + e.getMessage();
                log.error(msg);
            }
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.cluster.ClusterNode$WorkspaceUpdateChannel

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.