/*
* Galaxy
* Copyright (c) 2012-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.galaxy.jgroups;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.InputStream;
import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Header;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.Receiver;
import org.jgroups.UpHandler;
import org.jgroups.View;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.stack.StateTransferInfo;
import org.jgroups.util.StateTransferResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author pron
*/
public class ControlChannel extends JChannelAdapter implements ExtendedChannel, UpHandler {
private static final Logger LOG = LoggerFactory.getLogger(ControlChannel.class);
private static final short MAGIC_HEADER = (short) 1199;
static {
ClassConfigurator.add(MAGIC_HEADER, ControlHeader.class);
}
private static final ControlHeader CONTROL_MARKER = new ControlHeader();
private Receiver receiver;
private final boolean dataDiscardOwnMessages;
private boolean discardOwnMessages;
/**
* The JChannel's setDiscardOwnMessages must be called before constructing the control channel (and the value must never change)
*
* @param channel
*/
public ControlChannel(JChannel channel) {
super(channel);
this.dataDiscardOwnMessages = channel.getDiscardOwnMessages();
channel.setDiscardOwnMessages(false);
channel.setUpHandler(this);
}
@Override
public void setReceiver(Receiver receiver) {
this.receiver = receiver;
}
@Override
public Receiver getReceiver() {
return receiver;
}
@Override
public void setDiscardOwnMessages(boolean flag) {
this.discardOwnMessages = flag;
}
@Override
public boolean getDiscardOwnMessages() {
return discardOwnMessages;
}
@Override
public void send(Message msg) throws Exception {
msg.putHeader(MAGIC_HEADER, CONTROL_MARKER);
super.send(msg);
}
@Override
public Object up(Event evt) {
final Receiver dataReceiver = jchannel.getReceiver();
if (receiver != null) {
switch (evt.getType()) {
case Event.MSG:
final Message msg = (Message) evt.getArg();
LOG.debug("up: " + msg);
if (msg.getHeader(MAGIC_HEADER) != null) {
LOG.debug("up: to control channel " + msg);
if (discardOwnMessages && isOwnMessage(msg))
LOG.debug("up: discarded");
else
receiver.receive(msg);
} else if (dataReceiver != null) {
LOG.debug("up: to data channel " + msg);
if (dataDiscardOwnMessages && isOwnMessage(msg))
LOG.debug("up: discarded");
else
dataReceiver.receive(msg);
}
break;
case Event.VIEW_CHANGE:
receiver.viewAccepted((View) evt.getArg());
break;
case Event.SUSPECT:
receiver.suspect((Address) evt.getArg());
break;
case Event.GET_STATE_OK:
final StateTransferResult result = (StateTransferResult) evt.getArg();
final byte[] state = result.getBuffer();
if (state != null) {
final ByteArrayInputStream input = new ByteArrayInputStream(state);
try {
receiver.setState(input);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
break;
case Event.STATE_TRANSFER_INPUTSTREAM:
final InputStream is = (InputStream) evt.getArg();
if (is != null) {
try {
receiver.setState(is);
} catch (Throwable t) {
LOG.error("Error while setting state", t);
throw new RuntimeException("failed calling setState() in state requester", t);
}
}
break;
case Event.GET_APPLSTATE:
byte[] tmpState = null;
if (receiver != null) {
ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
try {
receiver.getState(output);
tmpState = output.toByteArray();
} catch (Exception e) {
LOG.error("Error while getting state", e);
throw new RuntimeException(getAddress() + ": failed getting state from application", e);
}
}
return new StateTransferInfo(null, 0L, tmpState);
case Event.BLOCK:
receiver.block();
if (dataReceiver != null)
dataReceiver.block();
return true;
case Event.UNBLOCK:
receiver.unblock();
if (dataReceiver != null)
dataReceiver.unblock();
break;
}
}
return null;
}
private boolean isOwnMessage(Message msg) {
return getAddress() != null && msg.getSrc() != null && getAddress().equals(msg.getSrc());
}
public static class ControlHeader extends Header {
public ControlHeader() {
}
@Override
public int size() {
return 0;
}
@Override
public void writeTo(DataOutput out) throws Exception {
}
@Override
public void readFrom(DataInput in) throws Exception {
}
}
}