org.jgroups.protocols.FORK Maven / Gradle / Ivy
package org.jgroups.protocols;
import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.conf.ConfiguratorFactory;
import org.jgroups.conf.ProtocolConfiguration;
import org.jgroups.fork.ForkConfig;
import org.jgroups.fork.ForkProtocol;
import org.jgroups.fork.ForkProtocolStack;
import org.jgroups.fork.UnknownForkHandler;
import org.jgroups.stack.Configurator;
import org.jgroups.stack.Protocol;
import org.jgroups.stack.ProtocolStack;
import org.jgroups.util.Bits;
import org.jgroups.util.*;
import org.w3c.dom.Node;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* The FORK protocol; multiplexes messages to different forks in a stack (https://issues.jboss.org/browse/JGRP-1613).
* See doc/design/FORK.txt for details
* @author Bela Ban
* @since 3.4
*/
@XmlInclude(schema="fork-stacks.xsd",type=XmlInclude.Type.IMPORT,namespace="fork",alias="fork")
@XmlElement(name="fork-stacks",type="fork:ForkStacksType")
@MBean(description="Implementation of FORK protocol")
public class FORK extends Protocol {
public static short ID=ClassConfigurator.getProtocolId(FORK.class);
@Property(description="Points to an XML file defining the fork-stacks, which will be created at initialization. " +
"Ignored if null")
protected String config;
@Property(description="If enabled, state transfer events will be processed, else they will be passed up")
protected boolean process_state_events=true;
private UnknownForkHandler unknownForkHandler = new UnknownForkHandler() {
@Override
public Object handleUnknownForkStack(Message message, String forkStackId) {
log.warn("%s: fork-stack for id=%s not found; discarding message", local_addr, forkStackId);
return null;
}
@Override
public Object handleUnknownForkChannel(Message message, String forkChannelId) {
log.warn("%s: fork-channel for id=%s not found; discarding message", local_addr, forkChannelId);
return null;
}
};
// mappings between fork-stack-ids and fork-stacks (bottom-most protocol)
protected final ConcurrentMap fork_stacks=new ConcurrentHashMap<>();
protected Address local_addr;
public void setUnknownForkHandler(UnknownForkHandler unknownForkHandler) {
this.unknownForkHandler = unknownForkHandler;
}
public UnknownForkHandler getUnknownForkHandler() {
return this.unknownForkHandler;
}
public Protocol get(String fork_stack_id) {return fork_stacks.get(fork_stack_id);}
public Protocol putIfAbsent(String fork_stack_id, Protocol prot) {return fork_stacks.put(fork_stack_id, prot);}
public void remove(String fork_stack_id) {fork_stacks.remove(fork_stack_id);}
@ManagedAttribute(description="Number of fork-stacks")
public int getForkStacks() {return fork_stacks.size();}
public static ForkProtocolStack getForkStack(Protocol prot) {
while(prot != null && !(prot instanceof ForkProtocolStack))
prot=prot.getUpProtocol();
return prot instanceof ForkProtocolStack? (ForkProtocolStack)prot : null;
}
public void init() throws Exception {
super.init();
if(config != null)
createForkStacks(config);
}
public Object down(Event evt) {
switch(evt.getType()) {
case Event.SET_LOCAL_ADDRESS:
local_addr=(Address)evt.getArg();
break;
}
return down_prot.down(evt);
}
public Object up(Event evt) {
switch(evt.getType()) {
case Event.MSG:
Message msg=(Message)evt.getArg();
ForkHeader hdr=(ForkHeader)msg.getHeader(id);
if(hdr == null)
break;
if(hdr.fork_stack_id == null)
throw new IllegalArgumentException("header has a null fork_stack_id");
Protocol bottom_prot=get(hdr.fork_stack_id);
return bottom_prot != null? bottom_prot.up(evt) : this.unknownForkHandler.handleUnknownForkStack(msg, hdr.fork_stack_id);
case Event.VIEW_CHANGE:
for(Protocol bottom: fork_stacks.values())
bottom.up(evt);
break;
case Event.STATE_TRANSFER_OUTPUTSTREAM:
if(!process_state_events)
break;
getStateFromMainAndForkChannels(evt);
return null;
case Event.STATE_TRANSFER_INPUTSTREAM:
if(!process_state_events)
break;
setStateInMainAndForkChannels((InputStream)evt.getArg());
return null;
}
return up_prot.up(evt);
}
public void up(MessageBatch batch) {
// Sort fork messages by fork-stack-id
Map> map=new HashMap<>();
for(Message msg: batch) {
ForkHeader hdr=(ForkHeader)msg.getHeader(id);
if(hdr != null) {
batch.remove(msg);
List list=map.get(hdr.fork_stack_id);
if(list == null) {
list=new ArrayList<>();
map.put(hdr.fork_stack_id, list);
}
list.add(msg);
}
}
// Now pass fork messages up, batched by fork-stack-id
for(Map.Entry> entry: map.entrySet()) {
String fork_stack_id=entry.getKey();
List list=entry.getValue();
Protocol bottom_prot=get(fork_stack_id);
if(bottom_prot == null)
continue;
MessageBatch mb=new MessageBatch(batch.dest(), batch.sender(), batch.clusterName(), batch.multicast(), list);
try {
bottom_prot.up(mb);
}
catch(Throwable t) {
log.error(Util.getMessage("FailedPassingUpBatch"), t);
}
}
if(!batch.isEmpty())
up_prot.up(batch);
}
protected void getStateFromMainAndForkChannels(Event evt) {
final OutputStream out=(OutputStream)evt.getArg();
try(DataOutputStream dos=new DataOutputStream(out)) {
getStateFrom(null, up_prot, null, null, dos);
// now fetch state from all fork channels
for(Map.Entry entry: fork_stacks.entrySet()) {
String stack_name=entry.getKey();
Protocol prot=entry.getValue();
ForkProtocolStack fork_stack=getForkStack(prot);
for(Map.Entry en: fork_stack.getForkChannels().entrySet()) {
String fc_name=en.getKey();
JChannel fc=en.getValue();
getStateFrom(fc, null, stack_name, fc_name, dos);
}
}
}
catch(Throwable ex) {
log.error("%s: failed fetching state from main channel", local_addr, ex);
}
}
protected void getStateFrom(JChannel channel, Protocol prot, String stack, String ch, DataOutputStream out) throws Exception {
ByteArrayDataOutputStream output=new ByteArrayDataOutputStream(1024);
OutputStreamAdapter out_ad=new OutputStreamAdapter(output);
Event evt=new Event(Event.STATE_TRANSFER_OUTPUTSTREAM, out_ad);
if(channel != null)
channel.up(evt);
else
prot.up(evt);
int len=output.position();
if(len > 0) {
Bits.writeString(stack, out);
Bits.writeString(ch, out);
out.writeInt(len);
out.write(output.buffer(), 0, len);
log.trace("%s: fetched %d bytes from %s:%s", local_addr, len, stack, ch);
}
}
protected void setStateInMainAndForkChannels(InputStream in) {
try(DataInputStream input=new DataInputStream(in)) {
for(;;) {
String stack_name=Bits.readString(input);
String ch_name=Bits.readString(input);
int len=input.readInt();
if(len > 0) {
byte[] data=new byte[len];
in.read(data, 0, len);
ByteArrayInputStream tmp=new ByteArrayInputStream(data, 0, len);
if(stack_name == null && ch_name == null)
up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, tmp));
else {
Protocol prot=fork_stacks.get(stack_name);
if(prot == null) {
log.warn("%s: fork stack %s not found, dropping state for %s:%s", local_addr, stack_name, stack_name, ch_name);
continue;
}
ForkProtocolStack fork_stack=getForkStack(prot);
JChannel fork_ch=fork_stack.get(ch_name);
if(fork_ch == null) {
log.warn("%s: fork channel %s not found, dropping state for %s:%s", local_addr, ch_name, stack_name, ch_name);
continue;
}
fork_ch.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, tmp));
}
}
}
}
catch(EOFException eof) {
}
catch(Throwable ex) {
log.error("%s: failed setting state in main channel", local_addr, ex);
}
}
protected void createForkStacks(String config) throws Exception {
InputStream in=getForkStream(config);
if(in == null)
throw new FileNotFoundException("fork stacks config " + config + " not found");
Map> protocols=ForkConfig.parse(in);
createForkStacks(protocols);
}
protected void createForkStacks(Map> protocols) throws Exception {
for(Map.Entry> entry: protocols.entrySet()) {
String fork_stack_id=entry.getKey();
if(get(fork_stack_id) != null)
continue;
List prots=createProtocols(null,entry.getValue());
createForkStack(fork_stack_id, prots, false);
}
}
public void parse(Node node) throws Exception {
Map> protocols=ForkConfig.parse(node);
createForkStacks(protocols);
}
/**
* Returns the fork stack for fork_stack_id (if exitstent), or creates a new fork-stack from protocols and adds it
* into the hashmap of fork-stack (key is fork_stack_id).
* Method init() will be called on each protocol, from bottom to top.
* @param fork_stack_id The key under which the new fork-stack should be added to the fork-stacks hashmap
* @param protocols A list of protocols from bottom to top to be inserted. They will be sandwiched
* between ForkProtocolStack (top) and ForkProtocol (bottom). The list can be empty (or null) in
* which case we won't create any protocols, but still have a separate fork-stack inserted.
* @param initialize If false, the ref count 'inits' will not get incremented and init() won't be called. This is
* needed when creating a fork stack from an XML config inside of the FORK protocol. The protocols
* in the fork stack will only get initialized on the first ForkChannel creation
* @return The new {@link ForkProtocolStack}, or the existing stack (if present)
*/
public synchronized ProtocolStack createForkStack(String fork_stack_id, List protocols, boolean initialize) throws Exception {
Protocol bottom;
if((bottom=get(fork_stack_id)) != null) {
ForkProtocolStack retval=getForkStack(bottom);
return initialize? retval.incrInits() : retval;
}
List prots=new ArrayList<>();
prots.add(bottom=new ForkProtocol(fork_stack_id).setDownProtocol(this)); // add a ForkProtocol as bottom protocol
if(protocols != null)
prots.addAll(protocols);
ForkProtocolStack fork_stack=(ForkProtocolStack)new ForkProtocolStack(getUnknownForkHandler(), prots, fork_stack_id).setChannel(this.stack.getChannel());
fork_stack.init();
if(initialize)
fork_stack.incrInits();
fork_stacks.put(fork_stack_id, bottom);
return fork_stack;
}
/** Creates a fork-stack from the configuration, initializes all protocols (setting values),
* sets the protocol stack as top protocol, connects the protocols and calls init() on them. Returns
* the protocols in a list, from bottom to top */
protected static List createProtocols(ProtocolStack stack, List protocol_configs) throws Exception {
return Configurator.createProtocols(protocol_configs,stack);
}
public static InputStream getForkStream(String config) throws IOException {
InputStream configStream = null;
try {
configStream=new FileInputStream(config);
}
catch(FileNotFoundException | AccessControlException fnfe) { // catching ACE fixes http://jira.jboss.com/jira/browse/JGRP-94
}
// Check to see if the properties string is a URL.
if(configStream == null) {
try {
configStream=new URL(config).openStream();
}
catch (MalformedURLException mre) {
}
}
// Check to see if the properties string is the name of a resource, e.g. udp.xml.
if(configStream == null)
configStream=Util.getResourceAsStream(config, ConfiguratorFactory.class);
return configStream;
}
public static class ForkHeader extends Header {
protected String fork_stack_id, fork_channel_id;
public ForkHeader() {
}
public ForkHeader(String fork_stack_id, String fork_channel_id) {
this.fork_stack_id=fork_stack_id;
this.fork_channel_id=fork_channel_id;
}
public String getForkStackId() {
return fork_stack_id;
}
public void setForkStackId(String fork_stack_id) {
this.fork_stack_id=fork_stack_id;
}
public String getForkChannelId() {
return fork_channel_id;
}
public void setForkChannelId(String fork_channel_id) {
this.fork_channel_id=fork_channel_id;
}
public int size() {return Util.size(fork_stack_id) + Util.size(fork_channel_id);}
public void writeTo(DataOutput out) throws Exception {
Bits.writeString(fork_stack_id,out);
Bits.writeString(fork_channel_id,out);
}
public void readFrom(DataInput in) throws Exception {
fork_stack_id=Bits.readString(in);
fork_channel_id=Bits.readString(in);
}
public String toString() {return fork_stack_id + ":" + fork_channel_id;}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy