org.jgroups.fork.ForkChannel Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
The newest version!
package org.jgroups.fork;
import org.jgroups.*;
import org.jgroups.protocols.FORK;
import org.jgroups.stack.AddressGenerator;
import org.jgroups.stack.Protocol;
import org.jgroups.stack.ProtocolStack;
import org.jgroups.util.Util;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
/**
* Implementation of a ForkChannel, which is a light-weight channel. Not all methods are supported,
* UnsupportedOperationExceptions will be thrown if an unsupported operation is called.
* See doc/design/FORK.txt for details
* @author Bela Ban
* @since 3.4
*/
public class ForkChannel extends JChannel implements ChannelListener {
protected final JChannel main_channel;
protected final String fork_channel_id;
protected static final Field[] copied_fields;
static {
String[] fields={"state", "local_addr", "name", "cluster_name", "view"};
copied_fields=new Field[fields.length];
for(int i=0; i < fields.length; i++) {
Field field=Util.getField(JChannel.class, fields[i]);
if(field == null)
throw new IllegalStateException("field \"" + fields[i] + "\" not found in JChannel");
copied_fields[i]=field;
}
}
/**
* Creates a new fork-channel from a main-channel. The channel is unconnected and {@link ForkChannel#connect(String)}
* needs to be called to send and receive messages.
* @param main_channel The main-channel. The lifetime of the newly created channel will be less than or equal to
* the main-channel
* @param fork_stack_id The ID to associate the fork-stack with in FORK
* @param fork_channel_id The ID used to map fork-channel IDs to ForkChannels in the fork-channels protocol stack
* @param create_fork_if_absent If true, and FORK doesn't exist, a new FORK protocol will be created and inserted
* into the main-stack at the given position. If false, and FORK doesn't exist, an
* exception will be thrown
* @param position The position at which the newly created FORK will be inserted. {@link ProtocolStack.Position#ABOVE} or
* {@link ProtocolStack.Position#BELOW} are accepted. Ignored if create_fork_if_absent is false.
* @param neighbor The class of the neighbor protocol below or above which the newly created FORK protocol will
* be inserted. Ignored if create_fork_if_absent is false.
* @param protocols A list of protocols (from bottom to top !) to insert as the fork_stack in FORK under the
* given fork_stack_id. If the fork-stack with fork_stack_id already exists, an exception will be
* thrown.
* Can be null if no protocols should be added. This may be the case when an app only wants to use
* a ForkChannel to mux/demux messages, but doesn't need a different protocol stack.
*
* @throws Exception
*/
public ForkChannel(final JChannel main_channel, String fork_stack_id, String fork_channel_id,
boolean create_fork_if_absent, ProtocolStack.Position position, Class extends Protocol> neighbor,
Protocol ... protocols) throws Exception {
super(false);
if(main_channel == null) throw new IllegalArgumentException("main channel cannot be null");
if(fork_stack_id == null) throw new IllegalArgumentException("fork_stack_id cannot be null");
if(fork_channel_id == null) throw new IllegalArgumentException("fork_channel_id cannot be null");
this.main_channel=main_channel;
this.fork_channel_id=fork_channel_id;
FORK fork;
// To prevent multiple concurrent FORK creations https://issues.redhat.com/browse/JGRP-1842
synchronized(this.main_channel) {
fork=getFORK(main_channel, position, neighbor, create_fork_if_absent);
}
// Returns the existing fork stack for fork_stack_id, or creates a new one
prot_stack=fork.createForkStack(fork_stack_id, protocols == null? null : Arrays.asList(protocols), true);
flush_supported=main_channel.flushSupported();
state=State.OPEN;
}
/**
* Creates a new fork-channel from a main-channel. The channel is unconnected and {@link ForkChannel#connect(String)}
* needs to be called to send and receive messages. If FORK is not found in the stack, an exception will be thrown.
* @param main_channel The main-channel. The lifetime of the newly created channel will be less than or equal to
* the main-channel
* @param fork_stack_id The ID to associate the fork-stack with in FORK
* @param fork_channel_id The ID used to map fork-channel IDs to ForkChannels in the fork-channels protocol stack
* @param protocols A list of protocols (from bottom to top !) to insert as the fork_stack in FORK under the
* given fork_stack_id. If the fork-stack with fork_stack_id already exists, an exception will be
* thrown.
* Can be null if no protocols should be added. This may be the case when an app only wants to use
* a ForkChannel to mux/demux messages, but doesn't need a different protocol stack.
* @throws Exception
*/
public ForkChannel(final JChannel main_channel, String fork_stack_id, String fork_channel_id,
Protocol ... protocols) throws Exception {
this(main_channel, fork_stack_id, fork_channel_id, false, ProtocolStack.Position.ABOVE, null, protocols);
}
@Override
public ForkChannel setName(String name) {
log.error("name (%s) cannot be set in a fork-channel", name);
return this;
}
@Override
public JChannel name(String name) {
log.error("name (%s) cannot be set in a fork-channel", name);
return this;
}
@Override public void channelConnected(JChannel channel) {
copyFields();
if(local_addr == null) return;
if(up_handler != null)
up_handler.setLocalAddress(local_addr);
}
@Override public void channelDisconnected(JChannel channel) {
copyFields();
}
@Override public void channelClosed(JChannel channel) {
copyFields();
}
/**
* Connects the fork-channel, which will be operational after this. Note that the fork-channel will
* have the same state as the main-channel, ie. if the main-channel is disconnected, so will the fork-channel be,
* even if connect() was called. This connect() method essentially adds the fork-channel to the fork-stack's hashmap,
* ready to send/receive messages as soon as the main-channel has been connected.
* This method does not affect the main-channel.
* @param cluster_name Ignored, will be the same as the main-channel's cluster name
* @throws Exception
*/
@Override
public ForkChannel connect(String cluster_name) throws Exception {
if(!this.main_channel.isConnected())
throw new IllegalStateException("main channel is not connected");
if(state == State.CONNECTED)
return this;
if(state == State.CLOSED)
throw new IllegalStateException("a closed fork channel cannot reconnect");
state=State.CONNECTING;
this.main_channel.addChannelListener(this);
copyFields();
JChannel existing_ch=((ForkProtocolStack)prot_stack).putIfAbsent(fork_channel_id,this);
if(existing_ch != null && existing_ch != this)
throw new IllegalArgumentException("fork-channel with id=" + fork_channel_id + " is already present");
setLocalAddress(local_addr);
prot_stack.startStack();
prot_stack.down(new Event(Event.CONNECT, cluster_name));
View current_view=main_channel.getView();
if(current_view != null) {
up(new Event(Event.VIEW_CHANGE, current_view));
prot_stack.down(new Event(Event.VIEW_CHANGE, current_view));
}
state=State.CONNECTED;
notifyChannelConnected(this);
return this;
}
@Override
public ForkChannel connect(String cluster_name, Address target, long timeout) throws Exception {
connect(cluster_name);
main_channel.getState(target, timeout);
return this;
}
/** Removes the fork-channel from the fork-stack's hashmap and resets its state. Does not affect the
* main-channel */
@Override
public ForkChannel disconnect() {
if(state != State.CONNECTED)
return this;
prot_stack.down(new Event(Event.DISCONNECT, local_addr)); // will be discarded by ForkProtocol
prot_stack.stopStack(cluster_name);
((ForkProtocolStack)prot_stack).remove(fork_channel_id);
nullFields();
state=State.OPEN;
notifyChannelDisconnected(this);
return this;
}
/** Closes the fork-channel, essentially setting its state to CLOSED. Note that - contrary to a regular channel -
* a closed fork-channel can be connected again: this means re-attaching the fork-channel to the main-channel*/
@Override
public void close() {
((ForkProtocolStack)prot_stack).remove(fork_channel_id);
if(state == State.CLOSED)
return;
disconnect(); // leave cluster if connected
prot_stack.destroy();
state=State.CLOSED;
notifyChannelClosed(this);
}
@Override
public Object down(Message msg) {
setHeader(msg);
return super.down(msg);
}
@Override
public ForkChannel send(Message msg) throws Exception {
checkClosedOrNotConnected();
FORK.ForkHeader hdr=msg.getHeader(FORK.ID);
if(hdr != null)
hdr.setForkChannelId(fork_channel_id);
else {
hdr=new FORK.ForkHeader(null, fork_channel_id);
msg.putHeader(FORK.ID, hdr);
}
prot_stack.down(msg);
return this;
}
@Override
public ForkChannel startFlush(List flushParticipants, boolean automatic_resume) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public ForkChannel startFlush(boolean automatic_resume) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public ForkChannel stopFlush() {
throw new UnsupportedOperationException();
}
@Override
public ForkChannel stopFlush(List flushParticipants) {
throw new UnsupportedOperationException();
}
@Override
public ForkChannel getState(Address target, long timeout) throws Exception {
main_channel.getState(target, timeout);
return this;
}
@Override
public ForkChannel addAddressGenerator(AddressGenerator address_generator) {
main_channel.addAddressGenerator(address_generator);
return this;
}
protected ForkChannel setLocalAddress(Address local_addr) {
if(local_addr != null) {
((ForkProtocolStack)prot_stack).setLocalAddress(local_addr); // sets the address only in the protocols managed by the fork-prot-stack
if(up_handler != null)
up_handler.setLocalAddress(local_addr);
}
return this;
}
/**
* Creates a new FORK protocol, or returns the existing one, or throws an exception. Never returns null.
*/
protected static FORK getFORK(JChannel ch, ProtocolStack.Position position, Class extends Protocol> neighbor,
boolean create_fork_if_absent) throws Exception {
ProtocolStack stack=ch.getProtocolStack();
FORK fork=stack.findProtocol(FORK.class);
if(fork == null) {
if(!create_fork_if_absent)
throw new IllegalArgumentException("FORK not found in main stack");
fork = new FORK();
fork.setProtocolStack(stack);
stack.insertProtocol(fork, position, neighbor);
}
return fork;
}
protected void setHeader(Message msg) {
FORK.ForkHeader hdr=msg.getHeader(FORK.ID);
if(hdr != null)
hdr.setForkChannelId(fork_channel_id);
else
msg.putHeader(FORK.ID, new FORK.ForkHeader(null, fork_channel_id));
}
/** Copies state from main-channel to this fork-channel */
protected void copyFields() {
for(Field field: copied_fields) {
Object value=Util.getField(field,main_channel);
Util.setField(field, this, value);
}
}
protected void nullFields() {
for(Field field: copied_fields)
Util.setField(field, this, null);
}
}