/*
* Copyright (c) 2008-2017, Hazelcast, Inc. 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.
*/
package com.hazelcast.client.spi.impl;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.client.connection.AddressProvider;
import com.hazelcast.client.connection.ClientConnectionManager;
import com.hazelcast.client.connection.nio.ClientConnection;
import com.hazelcast.client.impl.ClientImpl;
import com.hazelcast.client.impl.HazelcastClientInstanceImpl;
import com.hazelcast.client.spi.ClientClusterService;
import com.hazelcast.config.ListenerConfig;
import com.hazelcast.core.Client;
import com.hazelcast.core.Cluster;
import com.hazelcast.core.InitialMembershipEvent;
import com.hazelcast.core.InitialMembershipListener;
import com.hazelcast.core.Member;
import com.hazelcast.core.MemberAttributeEvent;
import com.hazelcast.core.MemberSelector;
import com.hazelcast.core.MembershipEvent;
import com.hazelcast.core.MembershipListener;
import com.hazelcast.internal.cluster.impl.MemberSelectingCollection;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.ClassLoaderUtil;
import com.hazelcast.util.Clock;
import com.hazelcast.util.UuidUtil;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
/**
* The {@link ClientClusterService} implementation.
*/
public class ClientClusterServiceImpl extends ClusterListenerSupport {
private final AtomicReference<Map<Address, Member>> members = new AtomicReference<Map<Address, Member>>();
private final ConcurrentMap<String, MembershipListener> listeners = new ConcurrentHashMap<String, MembershipListener>();
private final Object initialMembershipListenerMutex = new Object();
public ClientClusterServiceImpl(HazelcastClientInstanceImpl client, Collection<AddressProvider> addressProviders) {
super(client, addressProviders);
ILogger logger = client.getLoggingService().getLogger(ClientClusterService.class);
ClientConfig clientConfig = getClientConfig();
List<ListenerConfig> listenerConfigs = client.getClientConfig().getListenerConfigs();
for (ListenerConfig listenerConfig : listenerConfigs) {
EventListener listener = listenerConfig.getImplementation();
if (listener == null) {
try {
listener = ClassLoaderUtil.newInstance(clientConfig.getClassLoader(),
listenerConfig.getClassName());
} catch (Exception e) {
logger.severe(e);
}
}
if (listener instanceof MembershipListener) {
addMembershipListenerWithoutInit((MembershipListener) listener);
}
}
members.set(Collections.unmodifiableMap(new LinkedHashMap<Address, Member>()));
}
@Override
public Member getMember(Address address) {
return members.get().get(address);
}
@Override
public Member getMember(String uuid) {
final Collection<Member> memberList = getMemberList();
for (Member member : memberList) {
if (uuid.equals(member.getUuid())) {
return member;
}
}
return null;
}
@Override
public Collection<Member> getMemberList() {
return members.get().values();
}
@Override
public Collection<Member> getMembers(MemberSelector selector) {
return new MemberSelectingCollection<Member>(getMemberList(), selector);
}
@Override
public Address getMasterAddress() {
final Collection<Member> memberList = getMemberList();
return !memberList.isEmpty() ? new Address(memberList.iterator().next().getSocketAddress()) : null;
}
@Override
public int getSize() {
return getMemberList().size();
}
@Override
public int getSize(MemberSelector selector) {
int size = 0;
for (Member member : getMemberList()) {
if (selector.select(member)) {
size++;
}
}
return size;
}
@Override
public long getClusterTime() {
return Clock.currentTimeMillis();
}
@Override
public Client getLocalClient() {
Address address = getOwnerConnectionAddress();
final ClientConnectionManager cm = client.getConnectionManager();
final ClientConnection connection = (ClientConnection) cm.getConnection(address);
InetSocketAddress inetSocketAddress = connection != null ? connection.getLocalSocketAddress() : null;
final String uuid = getPrincipal().getUuid();
return new ClientImpl(uuid, inetSocketAddress);
}
@Override
public String addMembershipListener(MembershipListener listener) {
if (listener == null) {
throw new NullPointerException("listener can't be null");
}
synchronized (initialMembershipListenerMutex) {
String id = addMembershipListenerWithoutInit(listener);
initMembershipListener(listener);
return id;
}
}
private String addMembershipListenerWithoutInit(MembershipListener listener) {
String id = UuidUtil.newUnsecureUuidString();
listeners.put(id, listener);
return id;
}
private void initMembershipListener(MembershipListener listener) {
if (listener instanceof InitialMembershipListener) {
Cluster cluster = client.getCluster();
Collection<Member> memberCollection = members.get().values();
LinkedHashSet<Member> members = new LinkedHashSet<Member>(memberCollection);
InitialMembershipEvent event = new InitialMembershipEvent(cluster, members);
((InitialMembershipListener) listener).init(event);
}
}
@Override
public boolean removeMembershipListener(String registrationId) {
if (registrationId == null) {
throw new NullPointerException("registrationId can't be null");
}
return listeners.remove(registrationId) != null;
}
public void start() throws Exception {
init();
connectToCluster();
}
private ClientConfig getClientConfig() {
return client.getClientConfig();
}
void handleInitialMembershipEvent(InitialMembershipEvent event) {
synchronized (initialMembershipListenerMutex) {
Set<Member> initialMembers = event.getMembers();
LinkedHashMap<Address, Member> newMap = new LinkedHashMap<Address, Member>();
for (Member initialMember : initialMembers) {
newMap.put(initialMember.getAddress(), initialMember);
}
members.set(Collections.unmodifiableMap(newMap));
fireInitialMembershipEvent(event);
}
}
void handleMembershipEvent(MembershipEvent event) {
synchronized (initialMembershipListenerMutex) {
Member member = event.getMember();
if (event.getEventType() == MembershipEvent.MEMBER_ADDED) {
LinkedHashMap<Address, Member> newMap = new LinkedHashMap<Address, Member>(members.get());
newMap.put(member.getAddress(), member);
members.set(Collections.unmodifiableMap(newMap));
} else {
LinkedHashMap<Address, Member> newMap = new LinkedHashMap<Address, Member>(members.get());
newMap.remove(member.getAddress());
members.set(Collections.unmodifiableMap(newMap));
}
fireMembershipEvent(event);
}
}
private void fireInitialMembershipEvent(InitialMembershipEvent event) {
for (MembershipListener listener : listeners.values()) {
if (listener instanceof InitialMembershipListener) {
((InitialMembershipListener) listener).init(event);
}
}
}
private void fireMembershipEvent(MembershipEvent event) {
for (MembershipListener listener : listeners.values()) {
if (event.getEventType() == MembershipEvent.MEMBER_ADDED) {
listener.memberAdded(event);
} else {
listener.memberRemoved(event);
}
}
}
void fireMemberAttributeEvent(final MemberAttributeEvent event) {
for (MembershipListener listener : listeners.values()) {
listener.memberAttributeChanged(event);
}
}
}