package com.linkedin.databus.client.pub; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * 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. * */ import java.util.List; import org.apache.log4j.Logger; import com.linkedin.databus.groupleader.impl.zkclient.GroupLeadershipConnectionZkClientImpl; import com.linkedin.databus.groupleader.pub.AcceptLeadershipCallback; import com.linkedin.databus.groupleader.pub.GroupLeadershipConnection; import com.linkedin.databus.groupleader.pub.GroupLeadershipInfo; import com.linkedin.databus.groupleader.pub.GroupLeadershipSession; /** * * @author snagaraj * Client group member that specifies a domain, group and name. After joining a group, the member can be ready to accept and relinquish leadership * responsibly using a previously existing connection to a shared state . * The client can either wait to acquire leadership ; or be notified of leadership asynchronously * The client has the responsibility to leave the group; if it couldn't complete leadership responsibilities * The client also has the facility to read and write shared data; the assumption here is that only the leader can write/read/remove data ; */ public class DatabusClientGroupMember { private final GroupLeadershipConnection _groupConnection; private final AcceptLeadershipCallback _callback; private GroupLeadershipSession _groupSession; private final String _groupName ; private final String _domainName; private final String _nodeName ; private final boolean _requiresMastership ; private final Logger _log = Logger.getLogger(getClass()); private String _sharedDataPath="shareddata"; /** * * @author snagaraj * Inner class that provides the default call back if none provided; notifies outer object; */ protected class DefaultLeaderCallBack implements AcceptLeadershipCallback { @Override public void doAcceptLeadership(GroupLeadershipSession groupLeadershipSession) { synchronized (DatabusClientGroupMember.this) { _log.info("Node acquired leadership: " + DatabusClientGroupMember.this.toString()); DatabusClientGroupMember.this.notify(); } } } public DatabusClientGroupMember(String domainName,String groupName, String nodeName, String sharedDataPath, GroupLeadershipConnection connection,AcceptLeadershipCallback callback, boolean requiresMastership) { _domainName = domainName; _groupName = groupName; _nodeName = nodeName; _callback= callback; _groupConnection = connection; _groupSession = null; _sharedDataPath = sharedDataPath; _requiresMastership = requiresMastership; } public synchronized boolean joinWithoutLeadershipDuties() { if (null != _groupConnection) { _groupSession = _groupConnection.joinGroupWithoutLeadershipDuties(getDomainName(),getGroupName(),getName()); if ((_sharedDataPath!=null) && !_sharedDataPath.isEmpty()) { _groupSession.setSharedDataPath(_sharedDataPath); } } return _groupSession != null; } public DatabusClientGroupMember(String domainName,String groupName, String nodeName, String sharedDataPath, GroupLeadershipConnection connection,AcceptLeadershipCallback callback) { this(domainName,groupName,nodeName,sharedDataPath, connection,callback,true); } public DatabusClientGroupMember(String domainName,String groupName, String nodeName, GroupLeadershipConnection connection) { this(domainName,groupName,nodeName,null, connection,null,true); } public DatabusClientGroupMember(String domainName,String groupName, String nodeName, GroupLeadershipConnection connection,AcceptLeadershipCallback callback) { this(domainName,groupName,nodeName,null, connection,callback,true); } public DatabusClientGroupMember(String domainName,String groupName, String nodeName,String sharedDataPath, GroupLeadershipConnection connection) { this(domainName,groupName,nodeName,sharedDataPath, connection,null,true); } public synchronized boolean join() { if (null != _groupConnection) { AcceptLeadershipCallback leaderCallback = (_callback != null) ? _callback : this.new DefaultLeaderCallBack(); _groupSession = _groupConnection.joinGroup(getDomainName(),getGroupName(), getName(),leaderCallback); if ((_sharedDataPath!=null) && !_sharedDataPath.isEmpty()) { _groupSession.setSharedDataPath(_sharedDataPath); } } return _groupSession != null; } public synchronized boolean leave() { if (_groupSession != null) { _groupSession.leaveGroup(); _groupSession = null; this.notifyAll(); return true; } return false; } public boolean waitForLeaderShip() { return waitForLeaderShip(0); } public boolean waitForLeaderShip(int timeOutInMs) { try { if (_callback != null) { //async api - doesn't block; return true; } synchronized (this) { //if already leader; then return if ((_groupSession != null) && _groupSession.isLeader() ) { return true; } if (timeOutInMs < 0) timeOutInMs=0; long remainingTime = timeOutInMs; //guard against spurious wakeups; long start = System.currentTimeMillis(); while ((_groupSession!=null) && !_groupSession.isLeader() && (timeOutInMs==0 || remainingTime>0)) { this.wait(remainingTime); _log.info("Waiting for leadership on node " + toString() + " awoken"); if (timeOutInMs > 0) { remainingTime -= (System.currentTimeMillis() - start); } } return (_groupSession==null) ? false :_groupSession.isLeader(); } } catch (InterruptedException e) { _log.error("Waiting for leadership on node" + toString() + " interrupted "); } return false; } public synchronized boolean removeSharedData(String key) { if (_groupSession != null) { if (!_requiresMastership || _groupSession.isLeader()) { return _groupSession.removeGroupData(key); } else { _log.error("Remove failed! Attempted when " + toString() + " was not leader!"); } } return false ; } public boolean removeSharedData() { return removeSharedData(null); } public Object readSharedData() { return readSharedData(null); } public synchronized Object readSharedData(String key) { if (_groupSession != null) { return _groupSession.readGroupData(key); } return null ; } public synchronized boolean writeSharedData(String key, Object obj) { if (_groupSession != null) { if (!_requiresMastership || _groupSession.isLeader()) { return _groupSession.writeGroupData(key,obj); } else { _log.error("Write failed! Attempted when " + toString() + " was not leader!"); } } return false; } public boolean writeSharedData(Object obj) { return writeSharedData(null,obj); } public String getName() { return _nodeName; } public String getGroupName() { return _groupName; } public String getDomainName() { return _domainName; } @Override public String toString() { return getDomainName() + "/" + getGroupName() + "/" + getName(); } public synchronized boolean isLeader() { if (_groupSession != null) { return _groupSession.isLeader(); } return false; } public synchronized String getLeader() { if (_groupSession != null) { return _groupSession.getLeaderName(); } return null; } public synchronized List<String> getMembers() { if (_groupSession != null) { GroupLeadershipInfo info = _groupSession.getGroupLeadershipInfo(); return info.getMemberNames(); } return null; } public synchronized List<String> getSharedKeys() { if (_groupSession != null) { return _groupSession.getKeysOfGroupData(); } return null; } public synchronized boolean createPaths() { if (_groupConnection != null) { return ( (GroupLeadershipConnectionZkClientImpl) _groupConnection).createBasePath(getDomainName(),getGroupName()); } return false; } }