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.HashMap; import java.util.Random; import org.apache.log4j.Logger; import com.linkedin.databus.core.util.ConfigBuilder; import com.linkedin.databus.core.util.InvalidConfigException; import com.linkedin.databus.groupleader.impl.zkclient.GroupLeadershipConnectionFactoryZkClientImpl; import com.linkedin.databus.groupleader.pub.GroupLeadershipConnection; import com.linkedin.databus.groupleader.pub.GroupLeadershipConnectionFactory; /** * * @author snagaraj *Abstraction for cluster node; each of these connect to a zk server; it is possible to have many "members" share the connection; *This is not thread safe. It is meant to be used within a thread; the moment the thread dies; the shared state - in this case zk will delete all 'ephemeral' data created *by the connection and its members. Note that the connection itself is physical, the sharing of data, group membership is handled at the domain and group level. */ public class DatabusClientNode { public static final String MODULE = DatabusClientNode.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); //represents a member belonging to a certain domain ; group ; private HashMap<String,DatabusClientGroupMember> _members; //represents the 'physical' node connection to group; private GroupLeadershipConnection _connection = null; // config private final StaticConfig _config; public DatabusClientNode(StaticConfig config) throws InvalidConfigException { _config = config; if (config.isEnabled()) { if (config.getClusterServerList().isEmpty()) { throw new InvalidConfigException("Property val is empty! clusterServerList "); } _members = new HashMap<String,DatabusClientGroupMember> (); open(); } } //used only for testing; void open() { if (isEnabled()) { GroupLeadershipConnectionFactory groupLeadershipConnFactory = new GroupLeadershipConnectionFactoryZkClientImpl(_config.getClusterServerList(), _config.getSessionTimeoutMillis(), _config.getConnectionTimeoutMillis()); _connection = groupLeadershipConnFactory.getConnection(); addMember(_config.getDomain(),_config.getGroup(),_config.getName(),_config.getSharedDataPath(),_config.isMastershipRequiredForWrite()); } } public DatabusClientNode(Config config) throws InvalidConfigException { this(config.build()); } /** Is cluster mode enabled */ public boolean isEnabled() { return _config.isEnabled(); } /** Is the node connected to the set of cluster machines */ public boolean isConnected() { return _connection != null; } public StaticConfig getStaticConfig() { return _config; } public HashMap<String,DatabusClientGroupMember> getMembers() { return _members; } /** * retrieve the DatbusClientGroupMember from the connection ; whose domain, group and name matches the input * @return the DatabusClientGroupMember if found, null otherwise; */ public DatabusClientGroupMember getMember(String domain,String group,String name) { if (isConnected()) { String key = makeKey(domain,group,name); return _members.get(key); } return null; } /** * create a DatabusClientGroupMember object given namespace, group and name * This does not attempt to join the group ; invoke join on the member object; * @return the DatabusClientGroupMember if successful, null if either the member already existed or if the client isn't connected to cluster ; */ public DatabusClientGroupMember addMember(String domain,String group,String name,String sharedDataPath,boolean requiredMastership) { if (isConnected()) { String key = makeKey(domain,group,name); if (!_members.containsKey(key)) { DatabusClientGroupMember member = new DatabusClientGroupMember(domain,group,name,sharedDataPath, _connection,null,requiredMastership); _members.put(key,member); return member; } else { LOG.info("Error! Inserting duplicate member with key: " + key); } } return null; } /** * find a member and execute member.leave(); it is possible for the same member to be created at a later point using addMember() * @return true if a member was removed ; */ public boolean removeMember(String domain, String group,String name) { if (isConnected()) { String key = makeKey(domain,group,name); DatabusClientGroupMember groupMember = this.getMember(domain,group,name); if (groupMember != null) { groupMember.leave(); _members.remove(key); return true; } } return false; } private String makeKey(String domain,String group,String name) { return domain + "_" + group + "_" + name; } public void join() { if (_connection != null) { for (DatabusClientGroupMember member: _members.values()) { member.join(); } } } public void leave() { if (_connection != null) { for (DatabusClientGroupMember member: _members.values()) { member.leave(); } } } public void close() { if (_connection!=null) { //members have to leave() explicitly _members.clear(); _connection.close(); } } /** Static configuration for the DatabusClientNode. * * @see DatabusClientNode */ public static class StaticConfig { private final boolean _enabled; private final String _clusterServerList; private final int _sessionTimeoutMillis; private final int _connectionTimeoutMillis; private final String _group; private final String _domain; private final String _name; private final boolean _mastershipRequiredForWrite; private final String _sharedDataPath; public StaticConfig(boolean enabled, String clusterServerList,int sessionTimeoutMillis, int connectionTimeoutMillis,String domain,String group, String name,boolean requiresMastership,String sharedDataPath) { _enabled = enabled; _clusterServerList = clusterServerList; _sessionTimeoutMillis = sessionTimeoutMillis ; _connectionTimeoutMillis = connectionTimeoutMillis; _domain = domain; _name = name; _group = group; _mastershipRequiredForWrite = requiresMastership; _sharedDataPath = sharedDataPath; } public String getSharedDataPath() { return _sharedDataPath; } public String getClusterServerList() { return _clusterServerList; } public int getSessionTimeoutMillis() { return _sessionTimeoutMillis; } public int getConnectionTimeoutMillis() { return _connectionTimeoutMillis; } public String getGroup() { return _group; } public String getDomain() { return _domain; } public String getName() { return _name; } public boolean isMastershipRequiredForWrite() { return _mastershipRequiredForWrite ; } public boolean isEnabled() { return _enabled; } } public static class Config implements ConfigBuilder<StaticConfig> { private String _clusterServerList=""; private int _sessionTimeoutMillis=5000; private int _connectionTimeoutMillis=10000; private String _group="default-databus-group"; private String _domain="/Databus-Client-Domain"; private String _name="default-name"; private boolean _enabled = false; private boolean _mastershipRequiredForWrite = true; private String _sharedDataPath = null; public Config() { super(); Random r = new Random(System.currentTimeMillis()); _name = _name + r.nextInt(); } public String getClusterServerList() { return _clusterServerList; } public int getSessionTimeoutMillis() { return _sessionTimeoutMillis; } public int getConnectionTimeoutMillis() { return _connectionTimeoutMillis; } public void setClusterServerList(String clusterServerList) { _clusterServerList = clusterServerList; } public void setSessionTimeoutMillis(int sessionTimeoutMillis) { _sessionTimeoutMillis = sessionTimeoutMillis; } public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { _connectionTimeoutMillis = connectionTimeoutMillis; } @Override public StaticConfig build() throws InvalidConfigException { return new StaticConfig(_enabled, _clusterServerList,_sessionTimeoutMillis,_connectionTimeoutMillis,_domain,_group,_name,_mastershipRequiredForWrite,_sharedDataPath ); } public String getGroup() { return _group; } public String getDomain() { return _domain; } public String getName() { return _name; } public void setGroup(String group) { _group = group; } public void setDomain(String domain) { _domain = domain; } public void setName(String name) { _name = name; } public boolean isEnabled() { return _enabled; } public void setEnabled(boolean enabled) { _enabled = enabled; } public boolean isMastershipRequiredForWrite() { return _mastershipRequiredForWrite; } public void setMastershipRequiredForWrite(boolean requireMastershipForWrite) { _mastershipRequiredForWrite = requireMastershipForWrite; } public String getSharedDataPath() { return _sharedDataPath; } public void setSharedDataPath(String sharedDataPath) { _sharedDataPath = sharedDataPath; } } }