org.jgroups.protocols.pbcast.STATE_SOCK Maven / Gradle / Ivy
package org.jgroups.protocols.pbcast;
import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Global;
import org.jgroups.View;
import org.jgroups.annotations.LocalAddress;
import org.jgroups.annotations.MBean;
import org.jgroups.annotations.Property;
import org.jgroups.conf.PropertyConverters;
import org.jgroups.stack.IpAddress;
import org.jgroups.util.StateTransferResult;
import org.jgroups.util.Tuple;
import org.jgroups.util.Util;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
/**
* STATE_SOCK
has the state provider create a server socket to which the state
* requester connects and from which the latter reads the state.
*
* When implementing {@link org.jgroups.MessageListener#getState(java.io.OutputStream)}, the state should be written in
* sizeable chunks, because the underlying output stream sends 1 message / write over the socket. So if there are 1000
* writes of 1 byte each, this would generate 1000 messages ! We suggest using a {@link java.io.BufferedOutputStream}
* over the output stream handed to the application as argument of the callback.
*
* When implementing the {@link org.jgroups.MessageListener#setState(java.io.InputStream)} callback, there is no need to use a
* {@link java.io.BufferedOutputStream}, as the input stream handed to the application already buffers incoming data
* internally.
* @author Vladimir Blagojevic
* @author Bela Ban
* @see STATE_TRANSFER
* @since 3.0
*/
@MBean(description="State trasnfer protocol based on streaming state transfer")
public class STATE_SOCK extends StreamingStateTransfer {
/*
* ----------------------------------------------Properties -----------------------------------
*/
@LocalAddress
@Property(description="The interface (NIC) used to accept state requests. " +
"The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK",
systemProperty={Global.BIND_ADDR},writable=false)
protected InetAddress bind_addr;
@Property(description="Use \"external_addr\" if you have hosts on different networks, behind " +
"firewalls. On each firewall, set up a port forwarding rule (sometimes called \"virtual server\") to " +
"the local IP (e.g. 192.168.1.100) of the host then on each host, set \"external_addr\" TCP transport " +
"parameter to the external (public IP) address of the firewall.",
systemProperty=Global.EXTERNAL_ADDR,writable=false)
protected InetAddress external_addr=null ;
@Property(description="Used to map the internal port (bind_port) to an external port. Only used if > 0",
systemProperty=Global.EXTERNAL_PORT,writable=false)
protected int external_port=0;
@Property(name="bind_interface", converter=PropertyConverters.BindInterface.class,
description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr")
protected String bind_interface_str=null;
@Property(description="The port listening for state requests. Default value of 0 binds to any (ephemeral) port")
protected int bind_port=0;
/*
* --------------------------------------------- Fields ---------------------------------------
*/
/**
* Runnable that listens for state requests and spawns threads to serve those requests if socket transport is used
*/
protected volatile StateProviderAcceptor spawner;
public STATE_SOCK() {
super();
}
public void stop() {
super.stop();
if(spawner != null)
spawner.stop();
}
/*
* --------------------------- Private Methods ------------------------------------------------
*/
protected StateProviderAcceptor createAcceptor() {
StateProviderAcceptor retval=new StateProviderAcceptor(thread_pool,
Util.createServerSocket(getSocketFactory(),
"jgroups.streaming_state_transfer.srv_sock",
bind_addr, bind_port));
Thread t=getThreadFactory().newThread(retval, "STATE server socket acceptor");
t.start();
return retval;
}
protected void modifyStateResponseHeader(StateHeader hdr) {
if(spawner != null)
hdr.bind_addr=spawner.getServerSocketAddress();
}
protected Tuple createStreamToProvider(Address provider, StateHeader hdr) throws Exception {
IpAddress address=hdr.bind_addr;
Tuple retval=new Tuple(null,null);
Socket socket=null;
try {
socket=getSocketFactory().createSocket("jgroups.state_sock.sock");
retval.setVal2(socket);
socket.bind(new InetSocketAddress(bind_addr, 0));
socket.setReceiveBufferSize(buffer_size);
Util.connect(socket, new InetSocketAddress(address.getIpAddress(), address.getPort()), 0);
log.debug("%s: connected to state provider %s:%d", local_addr, address.getIpAddress(), address.getPort());
DataOutputStream out=new DataOutputStream(socket.getOutputStream());
Util.writeAddress(local_addr, out);
retval.setVal1(new BufferedInputStream(socket.getInputStream(), buffer_size));
return retval;
}
catch(Throwable t) {
Util.close(socket);
if(t instanceof Exception)
throw (Exception)t;
throw new Exception("failed creating socket", t);
}
}
protected void close(Object resource) {
if(resource instanceof Socket)
Util.close((Socket)resource);
}
protected void handleStateReq(Address requester) {
if(spawner == null || !spawner.isRunning())
spawner=createAcceptor();
super.handleStateReq(requester);
}
protected void handleViewChange(View v) {
super.handleViewChange(v);
if(state_provider != null && !v.getMembers().contains(state_provider)) {
openBarrierAndResumeStable();
Exception ex=new EOFException("state provider " + state_provider + " left");
up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED, new StateTransferResult(ex)));
}
}
protected void handleConfig(Map config) {
super.handleConfig(config);
if(bind_addr == null)
bind_addr=(InetAddress)config.get("bind_addr");
if(external_addr == null)
external_addr=(InetAddress)config.get("external_addr");
if(external_port <= 0) {
Object val=config.get("external_port");
if(val != null)
external_port=(Integer)val;
}
}
/*
* ------------------------ End of Private Methods --------------------------------------------
*/
protected class StateProviderAcceptor implements Runnable {
protected final ExecutorService pool;
protected final ServerSocket serverSocket;
protected final IpAddress address;
protected volatile boolean running=true;
public StateProviderAcceptor(ExecutorService pool, ServerSocket stateServingSocket) {
super();
this.pool=pool;
this.serverSocket=stateServingSocket;
if(external_addr != null)
this.address=new IpAddress(external_addr, external_port > 0? external_port : serverSocket.getLocalPort());
else
this.address=new IpAddress(bind_addr, serverSocket.getLocalPort());
}
public IpAddress getServerSocketAddress() {return address;}
public boolean isRunning() {return running;}
public void run() {
if(log.isDebugEnabled())
log.debug(local_addr + ": StateProviderAcceptor listening at " + getServerSocketAddress());
while(running) {
try {
final Socket socket=serverSocket.accept();
try {
pool.execute(new Runnable() {
public void run() {
process(socket);
}
});
}
catch(RejectedExecutionException rejected) {
Util.close(socket);
}
}
catch(Throwable e) {
if(serverSocket.isClosed())
running=false;
}
}
}
protected void process(Socket socket) {
OutputStream output=null;
try {
socket.setSendBufferSize(buffer_size);
if(log.isDebugEnabled())
log.debug(local_addr + ": accepted request for state transfer from " + socket.getInetAddress() + ":" + socket.getPort());
DataInput in=new DataInputStream(socket.getInputStream());
Address stateRequester=Util.readAddress(in);
output=new BufferedOutputStream(socket.getOutputStream(), buffer_size);
getStateFromApplication(stateRequester, output, false);
}
catch(Throwable e) {
if(log.isWarnEnabled())
log.warn(local_addr + ": failed handling request from requester", e);
}
// getStateFromApplication() is run in the same thread; it closes the output stream, and we close the socket
finally {
Util.close(socket);
}
}
public void stop() {
running=false;
try {
getSocketFactory().close(serverSocket);
}
catch(Exception ignored) {
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy