package org.pieShare.pieTools.piePlate.service.cluster.jgroupsCluster;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.Validate;
import org.jgroups.Address;
import org.jgroups.JChannel;
import org.pieShare.pieTools.piePlate.model.IPieAddress;
import org.pieShare.pieTools.piePlate.model.message.api.IClusterMessage;
import org.pieShare.pieTools.piePlate.model.serializer.jacksonSerializer.JGroupsPieAddress;
import org.pieShare.pieTools.piePlate.service.channel.api.IIncomingChannel;
import org.pieShare.pieTools.piePlate.service.channel.api.IOutgoingChannel;
import org.pieShare.pieTools.piePlate.service.channel.api.ITwoWayChannel;
import org.pieShare.pieTools.piePlate.service.cluster.api.IClusterService;
import org.pieShare.pieTools.piePlate.service.cluster.event.ClusterRemovedEvent;
import org.pieShare.pieTools.piePlate.service.cluster.event.IClusterRemovedListener;
import org.pieShare.pieTools.piePlate.service.cluster.exception.ClusterServiceException;
import org.pieShare.pieTools.piePlate.service.serializer.api.ISerializerService;
import org.pieShare.pieTools.pieUtilities.model.EncryptedPassword;
import org.pieShare.pieTools.pieUtilities.service.eventBase.IEventBase;
import org.pieShare.pieTools.pieUtilities.service.pieLogger.PieLogger;
import org.pieShare.pieTools.pieUtilities.service.security.encodeService.api.IEncodeService;
import org.pieShare.pieTools.pieUtilities.service.shutDownService.api.IShutdownService;
import org.pieShare.pieTools.pieUtilities.service.shutDownService.api.IShutdownableService;
public class JGroupsClusterService implements IClusterService, IShutdownableService {
//todo-sv: the model / service seperation is fuzzy in here: check it out!!!
private List<IIncomingChannel> incomingChannels;
private Map<String, IOutgoingChannel> outgoingChannels;
private ObjectBasedReceiver receiver;
private JChannel channel;
private IEventBase<IClusterRemovedListener, ClusterRemovedEvent> clusterRemovedEventBase;
private String id;
public JGroupsClusterService() {
//todo: bean service here?
this.incomingChannels = new ArrayList<>();
this.outgoingChannels = new HashMap<>();
}
@Override
public IEventBase<IClusterRemovedListener, ClusterRemovedEvent> getClusterRemovedEventBase() {
return this.clusterRemovedEventBase;
}
public void setClusterRemovedEventBase(IEventBase<IClusterRemovedListener, ClusterRemovedEvent> clusterRemovedEventBase) {
this.clusterRemovedEventBase = clusterRemovedEventBase;
}
public void setChannel(JChannel channel) {
this.channel = channel;
}
public void setReceiver(ObjectBasedReceiver receiver) {
this.receiver = receiver;
}
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public void connect(String clusterName) throws ClusterServiceException {
try {
Validate.notNull(this.receiver);
this.receiver.setClusterService(this);
this.channel.setReceiver(this.receiver);
this.channel.setDiscardOwnMessages(true);
this.channel.connect(clusterName);
}
catch (NullPointerException e) {
throw new ClusterServiceException("Receiver not set!");
}
catch (Exception e) {
throw new ClusterServiceException(e);
}
}
@Override
public boolean isMaster() {
if (this.channel.getView().getMembers().get(0).equals(this.channel.getAddress())) {
return true;
}
return false;
}
@Override
public void sendMessage(IClusterMessage msg) throws ClusterServiceException {
Address ad = null;
IPieAddress address = msg.getAddress();
if (!this.outgoingChannels.containsKey(address.getChannelId())) {
throw new ClusterServiceException(String.format("This outgoing channel doesn't exists: %s", address.getChannelId()));
}
if (msg.getAddress() instanceof JGroupsPieAddress) {
ad = ((JGroupsPieAddress) msg.getAddress()).getAddress();
}
try {
PieLogger.debug(this.getClass(), "Sending: {}", msg.getClass());
byte[] message = this.outgoingChannels.get(msg.getAddress().getChannelId()).prepareMessage(msg);
this.channel.send(ad, message);
}
catch (Exception e) {
throw new ClusterServiceException(e);
}
}
@Override
public int getMembersCount() {
return this.channel.getView().getMembers().size();
}
@Override
public boolean isConnectedToCluster() {
return this.channel.isConnected();
}
@Override
public void disconnect() throws ClusterServiceException {
//todo: this needs additional fixing: the true error is that this class doesn't get removed from the shutdown event listeners after manually shuting down
//todo: all listeners need to proper handle event unsubscription
if(this.channel == null) {
return;
}
this.channel.setReceiver(null);
this.channel.disconnect();
PieLogger.trace(this.getClass(), "Channel {} disconnected!", this.id);
this.channel.close();
PieLogger.trace(this.getClass(), "Channel {} closed!", this.id);
this.channel = null;
clusterRemovedEventBase.fireEvent(new ClusterRemovedEvent(this));
}
@Override
public List<IIncomingChannel> getIncomingChannels() {
return this.incomingChannels;
}
@Override
public void registerIncomingChannel(IIncomingChannel channel) {
this.incomingChannels.add(channel);
}
@Override
public void registerOutgoingChannel(IOutgoingChannel channel) {
//todo-piePlate: throw exceptions if adding to map is not possible
this.outgoingChannels.put(channel.getChannelId(), channel);
}
@Override
public void shutdown() {
try {
this.disconnect();
} catch (ClusterServiceException ex) {
PieLogger.error(this.getClass(), "Could not disconnect!", ex);
}
}
}