/*
* 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.listener;
import com.hazelcast.client.impl.HazelcastClientInstanceImpl;
import com.hazelcast.client.impl.protocol.ClientMessage;
import com.hazelcast.client.spi.EventHandler;
import com.hazelcast.client.spi.impl.ClientInvocation;
import com.hazelcast.client.spi.impl.ClientInvocationFuture;
import com.hazelcast.client.spi.impl.ListenerMessageCodec;
import com.hazelcast.core.HazelcastException;
import com.hazelcast.nio.Connection;
import com.hazelcast.nio.ConnectionListener;
import com.hazelcast.util.ExceptionUtil;
import com.hazelcast.util.UuidUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
public class ClientNonSmartListenerService extends ClientListenerServiceImpl implements ConnectionListener {
private final Map<ClientRegistrationKey, ClientEventRegistration> registrations
= new ConcurrentHashMap<ClientRegistrationKey, ClientEventRegistration>();
public ClientNonSmartListenerService(HazelcastClientInstanceImpl client,
int eventThreadCount, int eventQueueCapacity) {
super(client, eventThreadCount, eventQueueCapacity);
}
@Override
public String registerListener(final ListenerMessageCodec codec, final EventHandler handler) {
//This method should not be called from registrationExecutor
assert (!Thread.currentThread().getName().contains("eventRegistration"));
Future<String> future = registrationExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
String userRegistrationId = UuidUtil.newUnsecureUuidString();
ClientRegistrationKey registrationKey = new ClientRegistrationKey(userRegistrationId, handler, codec);
try {
ClientEventRegistration registration = invoke(registrationKey);
registrations.put(registrationKey, registration);
} catch (Exception e) {
throw new HazelcastException("Listener can not be added", e);
}
return userRegistrationId;
}
});
try {
return future.get();
} catch (Exception e) {
throw ExceptionUtil.rethrow(e);
}
}
private ClientEventRegistration invoke(ClientRegistrationKey registrationKey) throws Exception {
//This method should only be called from registrationExecutor
assert (Thread.currentThread().getName().contains("eventRegistration"));
EventHandler handler = registrationKey.getHandler();
handler.beforeListenerRegister();
ClientMessage request = registrationKey.getCodec().encodeAddRequest(false);
ClientInvocation invocation = new ClientInvocation(client, request);
invocation.setEventHandler(handler);
ClientInvocationFuture future = invocation.invoke();
String registrationId = registrationKey.getCodec().decodeAddResponse(future.get());
handler.onListenerRegister();
Connection connection = future.getInvocation().getSendConnection();
return new ClientEventRegistration(registrationId,
request.getCorrelationId(), connection, registrationKey.getCodec());
}
@Override
public boolean deregisterListener(final String userRegistrationId) {
//This method should not be called from registrationExecutor
assert (!Thread.currentThread().getName().contains("eventRegistration"));
Future<Boolean> future = registrationExecutor.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
ClientRegistrationKey key = new ClientRegistrationKey(userRegistrationId);
ClientEventRegistration registration = registrations.get(key);
if (registration == null) {
return false;
}
ClientMessage request = registration.getCodec().encodeRemoveRequest(registration.getServerRegistrationId());
try {
Future future = new ClientInvocation(client, request).invoke();
future.get();
removeEventHandler(registration.getCallId());
registrations.remove(key);
} catch (Exception e) {
throw new HazelcastException("Listener with id " + userRegistrationId + " could not be removed", e);
}
return true;
}
});
try {
return future.get();
} catch (Exception e) {
throw ExceptionUtil.rethrow(e);
}
}
@Override
public void start() {
client.getConnectionManager().addConnectionListener(this);
}
@Override
public void connectionAdded(final Connection connection) {
//This method should not be called from registrationExecutor
assert (!Thread.currentThread().getName().contains("eventRegistration"));
registrationExecutor.submit(new Runnable() {
@Override
public void run() {
Map<ClientRegistrationKey, ClientEventRegistration> tempMap =
new HashMap<ClientRegistrationKey, ClientEventRegistration>();
for (ClientRegistrationKey registrationKey : registrations.keySet()) {
try {
ClientEventRegistration eventRegistration = invoke(registrationKey);
tempMap.put(registrationKey, eventRegistration);
} catch (Exception e) {
logger.warning("Listener " + registrationKey + " can not be added to new connection: "
+ connection, e);
}
}
registrations.putAll(tempMap);
}
});
}
@Override
public void connectionRemoved(Connection connection) {
//This method should not be called from registrationExecutor
assert (!Thread.currentThread().getName().contains("eventRegistration"));
registrationExecutor.submit(new Runnable() {
@Override
public void run() {
for (ClientEventRegistration registration : registrations.values()) {
removeEventHandler(registration.getCallId());
}
}
});
}
//For Testing
public Collection<ClientEventRegistration> getActiveRegistrations(final String uuid) {
//This method should not be called from registrationExecutor
assert (!Thread.currentThread().getName().contains("eventRegistration"));
Future<Collection<ClientEventRegistration>> future = registrationExecutor.submit(
new Callable<Collection<ClientEventRegistration>>() {
@Override
public Collection<ClientEventRegistration> call() throws Exception {
ClientEventRegistration registration = registrations.get(new ClientRegistrationKey(uuid));
if (registration == null) {
return Collections.EMPTY_LIST;
}
LinkedList<ClientEventRegistration> activeRegistrations = new LinkedList<ClientEventRegistration>();
if (getEventHandler(registration.getCallId()) != null) {
activeRegistrations.add(registration);
}
return activeRegistrations;
}
});
try {
return future.get();
} catch (Exception e) {
throw ExceptionUtil.rethrow(e);
}
}
}