Java tutorial
/******************************************************************************* * Copyright 2015 xWic group (http://www.xwic.de) * * 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 de.xwic.appkit.core.cluster.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.xwic.appkit.core.cluster.ClusterConfiguration; import de.xwic.appkit.core.cluster.ClusterEvent; import de.xwic.appkit.core.cluster.ClusterEventListener; import de.xwic.appkit.core.cluster.EventTimeOutException; import de.xwic.appkit.core.cluster.ICluster; import de.xwic.appkit.core.cluster.IClusterService; import de.xwic.appkit.core.cluster.INode; import de.xwic.appkit.core.cluster.INode.NodeStatus; import de.xwic.appkit.core.cluster.Message; import de.xwic.appkit.core.cluster.NodeAddress; import de.xwic.appkit.core.cluster.Response; import de.xwic.appkit.core.cluster.TransportResult; /** * Central accessor to obtain information about the cluster, it's nodes and ways to interact with it. * The Cluster object is a singleton, which means a single VM can only be member of one cluster at a time. This * allows more convenient access to the cluster. * * This 'Cluster' implementation creates a grid of Nodes that are all connected to each other to broadcast messages. * There is no central master or hub, that delegates messages. Thus, all nodes are equal. * * @author lippisch */ public class Cluster implements ICluster { private final static Log log = LogFactory.getLog(Cluster.class); private final static long MAX_EVENT_WAIT_TIME = 1000 * 60 * 1; // event time out is 1 minute. private ClusterConfiguration config; private InboundConnectionHandler inbConHandler = null; private INode[] nodes = new INode[0]; private Queue<EventWrapper> eventQueue = new ConcurrentLinkedQueue<EventWrapper>(); private Thread tEventQueue; private ClusterServiceManager clServiceManager; /** * Internal wrapper for event listeners to filter by namespace. * @author lippisch */ private class EventListenerWrapper { ClusterEventListener listener; String namespace = null; public EventListenerWrapper(ClusterEventListener listener, String namespace) { super(); this.listener = listener; this.namespace = namespace; } } private List<EventListenerWrapper> listeners = Collections .synchronizedList(new ArrayList<EventListenerWrapper>()); /** * Private Constructor. */ public Cluster(ClusterConfiguration config) { this.config = config; this.clServiceManager = new ClusterServiceManager(this); } /** * Internal initialization */ public void initInternal() { inbConHandler = new InboundConnectionHandler(this, config.getPortNumber()); Thread tConHandler = new Thread(inbConHandler, "InboundConnectionHandler"); tConHandler.setDaemon(true); tConHandler.start(); for (NodeAddress na : config.getKnownNodes()) { registerNode(na); } // initiate the NodeController Thread tNC = new Thread(new NodeController(this), "NodeController"); tNC.setDaemon(true); tNC.start(); tEventQueue = new Thread(new EventQueueController(this), "EventQueueController"); tEventQueue.setDaemon(true); tEventQueue.start(); } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#getConfig() */ @Override public ClusterConfiguration getConfig() { return config; } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#registerNode(de.xwic.appkit.cluster.NodeAddress) */ @Override public void registerNode(NodeAddress na) { registerNode(new ClusterNode(na)); } /** * Add a known node to the cluster * @param node */ public void registerNode(INode node) { synchronized (nodes) { // check if node already exists for (INode n : nodes) { if (node.sameNode(n)) { return; } } int l = nodes.length; INode[] newNodes = new INode[l + 1]; System.arraycopy(nodes, 0, newNodes, 0, l); newNodes[l] = node; nodes = newNodes; } } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#getNodes() */ @Override public INode[] getNodes() { return nodes; } /** * @param remoteNodeName */ public INode getNodeByName(String remoteNodeName) { for (INode node : nodes) { if (node.getName() != null && remoteNodeName.equals(node.getName())) { return node; } } return null; } /** * @param internalNodeId * @return */ public INode getNodeById(int internalNodeId) { for (INode node : nodes) { if (((ClusterNode) node).getInternalNumber() == internalNodeId) { return node; } } return null; } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#addEventListener(de.xwic.appkit.cluster.ClusterEventListener) */ @Override public void addEventListener(ClusterEventListener listener) { addEventListener(listener, null); } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#addEventListener(de.xwic.appkit.cluster.ClusterEventListener, java.lang.String) */ @Override public void addEventListener(ClusterEventListener listener, String namespace) { listeners.add(new EventListenerWrapper(listener, namespace)); } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#sendEvent(de.xwic.appkit.cluster.ClusterEvent, boolean) */ @Override public void sendEvent(ClusterEvent event, boolean asynchronous) throws EventTimeOutException { EventWrapper ew = new EventWrapper(event, Thread.currentThread(), asynchronous); eventQueue.add(ew); tEventQueue.interrupt(); // wake up the queue if (!asynchronous) { // wait until the event is completed long start = System.currentTimeMillis(); while (!ew.isCompleted()) { try { Thread.sleep(1000); } catch (InterruptedException e) { } if (System.currentTimeMillis() - start > MAX_EVENT_WAIT_TIME) { throw new EventTimeOutException(); } } } } /** * INTERNAL - handle an incoming event. * @param event */ public void _receivedEvent(ClusterEvent event) { EventListenerWrapper[] lst = new EventListenerWrapper[listeners.size()]; lst = listeners.toArray(lst); for (EventListenerWrapper elw : lst) { if (elw.namespace == null || elw.namespace.equals(event.getNamespace())) { try { elw.listener.receivedEvent(event); } catch (Throwable t) { log.error("Event Listener '" + elw.listener.getClass().getName() + "' did throw exception for handling event '" + event + "'", t); } } } } /** * Returns the next event waiting to be processed. Removes it fromt he queue. * @return */ public EventWrapper nextEvent() { return eventQueue.poll(); } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#sendMessage(de.xwic.appkit.cluster.Message) */ @Override public TransportResult[] sendMessage(Message message) { INode[] nList = getNodes(); TransportResult[] results = new TransportResult[nList.length]; for (int i = 0; i < nList.length; i++) { INode node = nList[i]; Response response = null; Throwable exception = null; if (node.getStatus() == NodeStatus.CONNECTED) { try { response = node.sendMessage(message); } catch (Throwable e) { exception = e; } } results[i] = new TransportResult(response, node, exception); } return results; } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#registerClusterService(java.lang.String, de.xwic.appkit.cluster.IClusterService) */ @Override public void registerClusterService(String name, IClusterService service) { clServiceManager.registerClusterService(name, service); } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#getClusterService(java.lang.String) */ @Override public IClusterService getClusterService(String name) { return clServiceManager.getClusterService(name); } /* (non-Javadoc) * @see de.xwic.appkit.cluster.ICluster#getInstalledClusterServiceNames() */ @Override public Collection<String> getInstalledClusterServiceNames() { return clServiceManager.getInstalledClusterServiceNames(); } /** * @return the clServiceManager */ public ClusterServiceManager getClusterServiceManager() { return clServiceManager; } /** * A node was disconnected. * @param remoteNode */ public void nodeDisconnected(INode remoteNode) { clServiceManager.handleDisconnectedNode(remoteNode); } }