/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can obtain * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. * Sun designates this particular file as subject to the "Classpath" exception * as provided by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the License * Header, with the fields enclosed by brackets [] replaced by your own * identifying information: "Portions Copyrighted [year] * [name of copyright owner]" * * Contributor(s): * * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.enterprise.jxtamgmt; import net.jxta.id.ID; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.TreeMap; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; /** * Manages Cluster Views and notifies cluster view listeners when cluster view * changes */ public class ClusterViewManager { private static final Logger LOG = JxtaUtil.getLogger(ClusterViewManager.class.getName()); private TreeMap view = new TreeMap(); private SystemAdvertisement advertisement = null; private SystemAdvertisement masterAdvertisement = null; private List cvListeners = new ArrayList(); private long viewId = 0; private long masterViewID = 0; private ClusterManager manager; private ReentrantLock viewLock = new ReentrantLock(); /** * Constructor for the ClusterViewManager object * * @param advertisement the advertisement of the instance associated with * this object * @param manager the cluster manager * @param listeners List of ClusterViewEventListener */ public ClusterViewManager(final SystemAdvertisement advertisement, final ClusterManager manager, final List listeners) { this.advertisement = advertisement; this.manager = manager; cvListeners.addAll(listeners); } public void start() { //Self appointed as master and then discover so that we resolve to the right one // avoid notifying listener setMaster(advertisement, false); } public void addClusterViewEventListener( final ClusterViewEventListener listener) { cvListeners.add(listener); } public void removeClusterViewEventListener(final ClusterViewEventListener listener) { cvListeners.remove(listener); } /** * adds a system advertisement * * @param advertisement system adverisement to add */ void add(final SystemAdvertisement advertisement) { lockLog("add()"); viewLock.lock(); try { if (!view.containsKey(advertisement.getID().toString())) { LOG.log(Level.FINER, new StringBuffer().append("Adding ") .append(advertisement.getName()) .append(" ") .append(advertisement.getID().toString()) .toString()); view.put(advertisement.getID().toString(), advertisement); LOG.log(Level.FINER, MessageFormat.format("Cluster view now contains {0} entries", getViewSize())); } } finally { viewLock.unlock(); } } /** * Set the master instance * * @param advertisement Master system adverisement * @param notify if true, notifies registered listeners */ void setMaster(final SystemAdvertisement advertisement, boolean notify) { if (!advertisement.equals(masterAdvertisement)) { masterAdvertisement = advertisement; lockLog("setMaster()"); viewLock.lock(); try { view.put(masterAdvertisement.getID().toString(), masterAdvertisement); } finally { viewLock.unlock(); } if (notify) { notifyListeners(new ClusterViewEvent( ClusterViewEvents.MASTER_CHANGE_EVENT, advertisement)); } if (advertisement.getID().equals(this.advertisement.getID())) { LOG.log(Level.FINER, "Setting MasterNode Role"); } else { LOG.log(Level.FINER, new StringBuffer().append("Setting Master Node :") .append(advertisement.getName()).append(' ') .append(advertisement.getID()).toString()); } } } /** * Set the master instance with new view * * @param newView list of advertisements * @param advertisement Master system adverisement * @return true if there is master's change, false otherwise */ boolean setMaster( final List newView, final SystemAdvertisement advertisement ) { if( advertisement.equals( masterAdvertisement ) ) return false; if ( newView != null ) addToView( newView ); setMaster( advertisement, true ); return true; } /** * Gets the master advertisement * * @return SystemAdvertisement Master system adverisement */ public SystemAdvertisement getMaster() { return masterAdvertisement; } /** * Retrieves a system advertisement from a the table * * @param id instance id * @return Returns the SystemAdvertisement associated with id */ public SystemAdvertisement get(final ID id) { final SystemAdvertisement adv; lockLog("get()"); viewLock.lock(); try { adv = view.get(id.toString()); } finally { viewLock.unlock(); } return adv; } /** * removes an entry from the table. This is only called when a * failure occurs. * * @param advertisement Instance advertisement * @return SystemAdvertisement removed or null if not in view. */ SystemAdvertisement remove(final SystemAdvertisement advertisement) { ID id = advertisement.getID(); if (containsKey(id)) { lockLog("remove()"); viewLock.lock(); try { view.remove(id.toString()); } finally { viewLock.unlock(); } LOG.log(Level.FINER, "Removed " + advertisement.getName() + " "+ advertisement.getID().toString()); } else { LOG.log(Level.FINEST, "Skipping removal of " + id+ " Not in view"); } return advertisement; } public boolean containsKey(final ID id) { final boolean contains; viewLock.lock(); try { contains = view.containsKey(id.toString()); } finally { viewLock.unlock(); } return contains; } /** * Resets the view */ void reset() { LOG.log(Level.FINEST, "Resetting View"); lockLog("reset()"); viewLock.lock(); try { view.clear(); view.put(advertisement.getID().toString(), advertisement); } finally { viewLock.unlock(); } } /** * Returns a sorted localView * * @return The localView List */ public ClusterView getLocalView() { final TreeMap temp; lockLog("getLocalView()"); viewLock.lock(); try { temp = (TreeMap) view.clone(); } finally { viewLock.unlock(); } LOG.log(Level.FINEST, "returning new ClusterView with view size:" + view.size()); return new ClusterView(temp, viewId++); } /** * Gets the viewSize attribute of the ClusterViewManager object * * @return The viewSize */ public int getViewSize() { int size; lockLog("getViewSize()"); viewLock.lock(); try { size = view.size(); } finally { viewLock.unlock(); } return size; } /** * Returns the top node on the list * * @return the top node on the list */ SystemAdvertisement getMasterCandidate() { final SystemAdvertisement adv; final String id = view.firstKey(); lockLog("getMasterCandidate()"); viewLock.lock(); try { adv = view.get(id); } finally { viewLock.unlock(); } LOG.log(Level.FINER, new StringBuffer().append("Returning Master Candidate Node :") .append(adv.getName()).append(' ').append(adv.getID()) .toString()); return adv; } /** * Determines whether this node is the Master * * @return true if this node is the master node */ public boolean isMaster() { return masterAdvertisement != null && masterAdvertisement.getID().equals(advertisement.getID()); } /** * Determines whether this node is at the top of the list * * @return true if this node is a the top of the list, false otherwise */ public boolean isFirst() { final String id = view.firstKey(); return advertisement.getID().toString().equals(id); } /** * the index of id this view, or -1 if this view does not contain this * element. * * @param id id * @return the index of id this view, or -1 if this view does not * contain this element. */ public int indexOf(final ID id) { if (id == null) { return -1; } final String idStr = id.toString(); int index = 0; String key; for (String s : view.keySet()) { key = s; if (key.equals(idStr)) { return index; } index++; } return -1; } public ID getID(final String name) { return manager.getID(name); } /** * Adds a list of advertisements to the view * * @param newView list of advertisements * @param cvEvent the cluster event * @param notifyForcefully if true, notifies registered listeners forcefully. if false, notifies listeners when view changed. */ void addToView( final List newView, final boolean notifyForcefully, final ClusterViewEvent cvEvent ) { if( cvEvent == null ) return; boolean changed = addToView( newView ); if( notifyForcefully || changed ) notifyListeners( cvEvent ); } /** * Adds a list of advertisements to the view * * @param newView list of advertisements * @return true if there are changes, false otherwise */ private boolean addToView( final List newView ) { boolean changed = false; // We need old view's snapshot for becoming aware of changes before reset() // Though reset() also uses viewLock for view.clear(), // if reset() is called before addToView() acquires the viewLock, view can be changed in a short time // If view was changed in a short time, unexpected result occurred in becoming aware of changes // So for safety, if we need to become aware of real changes, snapshooting and view.clear() should be called in addToView()'s viewLock //reset(); lockLog( "addToView()" ); viewLock.lock(); // old view's snapshot TreeMap oldView = (TreeMap)view.clone(); LOG.log( Level.FINER, "Resetting View" ); // we should clear view after old view's snapshot view.clear(); view.put(advertisement.getID().toString(), advertisement); try { // we don't need put manager.getSystemAdvertisement(). this operation is maybe duplicated. if( !newView.contains( manager.getSystemAdvertisement() ) ) { view.put( manager.getSystemAdvertisement().getID().toString(), manager.getSystemAdvertisement() ); } for( SystemAdvertisement elem : newView ) { LOG.log( Level.FINER, new StringBuffer().append( "Adding " ) .append( elem.getID() ).append( " to view" ) .toString() ); if( !changed && !oldView.containsKey( elem.getID().toString() ) ) { changed = true; } // Always add the wire version of the adv view.put( elem.getID().toString(), elem ); } } finally { viewLock.unlock(); } return changed; } // this method should be thread-safe synchronized void notifyListeners(final ClusterViewEvent event) { LOG.log(Level.FINER, MessageFormat.format("Notifying the {0} to listeners, peer in event is {1}", event.getEvent().toString(), event.getAdvertisement().getName())); for (ClusterViewEventListener elem : cvListeners) { elem.clusterViewEvent(event, getLocalView()); } } public void setInDoubtPeerState(final SystemAdvertisement adv) { if (adv == null) { throw new IllegalArgumentException("SystemAdvertisment may not be null"); } notifyListeners(new ClusterViewEvent(ClusterViewEvents.IN_DOUBT_EVENT, adv)); } public void setPeerStoppingState(final SystemAdvertisement adv) { if (adv == null) { throw new IllegalArgumentException("SystemAdvertisment may not be null"); } notifyListeners(new ClusterViewEvent(ClusterViewEvents.PEER_STOP_EVENT, adv)); } public void setClusterStoppingState(final SystemAdvertisement adv) { if (adv == null) { throw new IllegalArgumentException("SystemAdvertisment may not be null"); } notifyListeners(new ClusterViewEvent(ClusterViewEvents.CLUSTER_STOP_EVENT, adv)); } public void setPeerNoLongerInDoubtState(final SystemAdvertisement adv) { if (adv == null) { throw new IllegalArgumentException("SystemAdvertisment may not be null"); } notifyListeners(new ClusterViewEvent(ClusterViewEvents.NO_LONGER_INDOUBT_EVENT, adv)); } public long getMasterViewID() { return masterViewID; } public void setMasterViewID(long masterViewID) { this.masterViewID = masterViewID; } private void lockLog(String method) { LOG.log(Level.FINE, MessageFormat.format("{0} viewLock Hold count :{1}, lock queue count:{2}", method, viewLock.getHoldCount(), viewLock.getQueueLength())); } public void setPeerReadyState(SystemAdvertisement adv) { if (adv == null) { throw new IllegalArgumentException("SystemAdvertisment may not be null"); } notifyListeners(new ClusterViewEvent(ClusterViewEvents.JOINED_AND_READY_EVENT, adv)); } }