package org.jboss.fresh.shell.commands;
import org.jboss.fresh.io.BufferObjectReader;
import org.jboss.fresh.io.BufferObjectWriter;
import org.jboss.fresh.io.BufferWriter;
import org.jboss.fresh.io.PrintWriter2;
import org.jboss.fresh.shell.AbstractExecutable;
import org.jboss.fresh.events.Event;
import org.jboss.fresh.events.EventCentral;
import org.jboss.fresh.events.EventListener;
import org.jboss.fresh.events.net.EventNetRouter;
import org.jboss.fresh.events.net.EventNetConnector;
import org.jboss.fresh.events.net.EventNetRouterImpl;
import org.jboss.fresh.events.net.TakenOverException;
import org.jboss.fresh.ctx.Context;
import java.io.IOException;
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
//import javax.naming.Context;
/**
* This class is a server side part of RemoteShellEventNetConnector client side class.
*
* The two classes together form a communication layer thar maintains an open channel
* between two hosts.
*
* This class runs inside a shell on what we'll refer to as server side. The other side is initiator side.
* Initiator establishes a connection to the server side, starts a shell and invokes this executable.
*
* Once this executable is running - bothways data pipe is established and simultaneous reads and writes
* of data can begin.
*
* This infrastructure strives to connect servers into an events network (EventNet). Servers do get restarted and connections
* do drop. For that reason every initiator in the EventNet must have a permanent unique name assigned and
* it communicates this name to the server during channel establishing phase.
*
* For each initiator there exists an EventNetRouter on the server. If it does not exist yet it is created. If it exists already
* that means that it hasn't been destroyed since initiator's last disconnect. Initiator can continue using the same EventNetRouter.
* This also means it can catch up on all the events it has missed. In this case initiator must choose whether he wants to receive
* all the saved events or will he discard them and tap into realtime event stream right away.
*
* EventNetAgentExe only provides IN/OUT channel. All the logic is performed by EventNetRouter. That means that once reference to
* the EventNetRouter is available BufferObjectReader around StdIn and BufferObjectWriter around StdOut are passed to it and
* EventNetRouter takes over on executable process' thread.
*
* If we let execute() method exit then StdOut and StdIn will get closed, so we must use the invocation thread for EventNetRouter's
* processing. Processing will fail if any of the buffers fails.
*
* To assure consistency EventNetRouter must disable any internal buffering inside output buffers. If the chunk can not be sent over then
* if must be backed up to secondary it must not get lost in the buffer that will eventually just be garbage-collected.
*
* Invocation thread polls a local outgoing events buffer on a regular periodic basis and writes complete buffers to out in blocking
* write operations. Either a complete buffer write succedes or it fails. It never gets partially tranferred.
*
* Server-side has it's own connector called EventNetAgentConnector that knows how to handle Buffers. On the initiator side RemoteShellEventNetConnector
* doesn't work with buffers but with RemoteShell writeBuffer readBuffer methods.
*
* API allows RemoteShell client to request a buffer that is smaller than server's buffer. That would cause partial buffer being transferred so we must
* not use that feature. Initiator and server-side should negotiate buffer size.
*
* There is a tradeoff between buffer size and speed. In best-case scenario data never comes fast enough to fill up buffer size so you can always do a data
* transfer in one remote call.
*
*
* eventnetagent -host nasser_1 -name nasser1_hirohito_remoter -keepeventsoncondrop
*
*/
//
// They handshake. They exchange info on registered broadcasters/listeners - so they are in sync with regards to that. If registrations happen during handshake they are communikated to the other side after the handshake via ECentral event.
//
// After the handshake and ECentral events that occured during hand shake events start to go bothways - depending on registration and filtering.
//
// Kako povemo katerega EC uporabiti? Normalno je EC bound v shell contextu. Po mojem zado��a �e to implementiramo.
//
public class EventNetAgentExe extends AbstractExecutable {
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(EventNetAgentExe.class);
public void process(String exename, String[] params) throws Exception {
try {
getStdIn().setMaxSize(1000);
getStdOut().setMaxSize(1000);
// this could as well be part of EventRouter configuration on the server-side
boolean keepEvents = false;
// process events
String [] invalid = getInvalidSwitches(params, "haick", new String[] {"host", "app", "id", "conid", "cchk"});
if(invalid!=null && invalid.length > 0) {
error("Unrecoginzed switches: " + java.util.Arrays.asList(invalid));
return;
}
// client tells what host he is
String hostLabel = getSwitchValue(params, "h", "host");
// client tells who exactly he is - agent name specified as param
String app = getSwitchValue(params, "a", "app");
// application if specified
String initorID = getSwitchValue(params, "i", "id");
// application if specified
String conID = getSwitchValue(params, "c", "conid");
boolean checkCon = isSwitchActive(params, "k", "cchk");
if(conID == null) {
error("conID not specified. You must use conid parameter");
return;
}
//__hackBindEventCentral();
/*
for(int i=0; i<params.length; i++) {
String tmp = params[i];
switch(i) {
case 0:
hostLabel = tmp;
break;
case 1:
app = tmp;
break;
case 2:
initorID = tmp;
break;
default:
usage();
if(canThrowEx()) {
throw new RuntimeException("Too many parameters. Use --help for usage information.");
}
return;
}
}
*/
// if(hostLabel==null || app==null || initorID==null) {
// error("Parameter(s) missing. Use --help for usage information.");
// return;
// }
// get ref to EventCentral
Context ctx = getShell().getContext();
// we could also specify lookup name on cmdline or as ENV variable
EventCentral ec = (EventCentral) ctx.get("EventCentral");
if(ec == null) {
error(new org.jboss.fresh.shell.UninitializedEnvironmentException("EventCentral not bound in context"));
return;
}
log.info("EC1 Listeners: " + ec.getListeners());
log.info("EC1 Broadcasters: " + ec.getBroadcasters());
// lookup EventNetRouter for our name
EventNetRouter router = ec.getEventNetRouter(initorID);
// Thread t = null;
log.info("=== processid: " + getProcess().getID());
// if exists take over : tell it to stop, get Connector from it and set Buffers on it, then tell EventNetRouter to start
if(router!=null) {
log.info("router != null checkCon: " + checkCon + ", conID: " + conID + ", existing conID: " + router.getConID());
if(checkCon && !conID.equals(router.getConID())) {
try {
error("Agent (" + conID + ") has been taken over by another agent (" + router.getConID() + ")");
} catch(Exception e) {
throw new TakenOverException(e.getMessage());
}
return;
}
router.setConID(conID);
log.info("Stopping current router ...");
router.stop(true);
log.info("ok. getting connector and replacing buffers ...");
EventNetShellAgentConnector con = (EventNetShellAgentConnector) router.getConnector();
con.setOutput(new BufferObjectWriter(getStdOut()));
con.setInput(new BufferObjectReader(getStdIn()));
log.info("ok. Creating new ProcessorThread");
router.start();
log.info("### YAK! This shouldn'0t happen :(.");
return;
// (this will take over our thread and our job is done)
} else {
// if it doesn't exist then we need to instanciate a new EventNetRouter and register it with EventCentral
// Then do same things as with taking over which eventually takes executable's thread inside the last method we call.
if(hostLabel == null) {
error("Host not specified");
return;
}
if(app==null) app = shell.getEnvProperty("PROJECT");
router = new EventNetRouterImpl(ec, initorID, hostLabel, app, keepEvents);
router.setConID(conID);
//ec.registerEventNetRouter(initorID, router);
EventNetShellAgentConnector con = new EventNetShellAgentConnector();
con.setOutput(new BufferObjectWriter(getStdOut()));
con.setInput(new BufferObjectReader(getStdIn()));
router.setConnector(con);
router.start();
// t = new ProcessorThread(router);
// t.start();
}
} catch(Exception e) {
try {
Thread.sleep(10000);
} catch(Exception ex) {
}
throw e;
}
// Thread.sleep(10000);
// org.jboss.fresh.events.EventBroadcaster eb = new org.jboss.fresh.events.EventBroadcaster(ec, "CMSPersistence");
// while(true) {
// Event ev = new Event("CMS", "onAfterCreate");
//log.info("dispatchEvent: " + ev);
// eb.dispatchEvent(ev);
// Thread.sleep(10000);
// }
/*
log.info(" * * * * * * * * * * * * * * * ");
log.info(" EC2 Broadcasters: " + ec2.getBroadcasters());
log.info(" EC2 Listeners: " + ec2.getListeners());
log.info(" * *");
log.info(" EC1 Broadcasters: " + ec.getBroadcasters());
log.info(" EC1 Listeners: " + ec.getListeners());
t.join();
*/
}
/*
private void __hackBindEventCentral() throws NamingException {
RegistryContext ctx = new RegistryContext();
Context c = null;
try {
c = (Context) ctx.lookup("java:");
} catch(NameNotFoundException ex) {
c = ctx.createSubcontext("java:");
}
try {
c = (Context) ctx.lookup("java:/CP2");
} catch(NameNotFoundException ex) {
c = c.createSubcontext("CP2");
}
try {
ctx.lookup("java:/CP2/EventCentral");
} catch(NameNotFoundException ex) {
EventCentral ec = new EventCentralImpl("nasser_0", null);
c.bind("EventCentral", ec);
}
try {
ctx.lookup("java:/CP2/proj/EventCentral");
} catch(NameNotFoundException ex) {
try {
c = c.createSubcontext("proj");
} catch(Exception e) {
log.error("Failed to create subcontext proj: ", e);
}
try {
EventCentral ec2 = new EventCentralImpl("nasser_0", "proj");
EventCentral ec = (EventCentral) ctx.lookup("java:/CP2/EventCentral");
//ec.registerEventCentral(ec2, "nasser_0/proj/EventCentral(" + ec2.generateID() + ")", new ECListener(ec2));
ctx.bind("java:/CP2/proj/EventCentral", ec2);
} catch(Exception e) {
log.error("Failed to bind proj EventCentral: ", e);
}
}
}
*/
private void usage() {
PrintWriter2 out = new PrintWriter2(new BufferWriter(getStdOut()));
out.println("Usage: eventnet [-h <host_label>] [-a <application>] -i <agentid>");
out.println(" -h, --host : host label");
out.println(" -a, --app : application (this parameter is optional)");
out.println(" -i, --id : agent id");
out.println(" --help : this help");
out.println();
out.println(" Agent id is required every time. It uniquely identifies remote agent which");
out.println(" represents connection with remote EventCentral.");
out.println(" Host label is required the first time remote agent establishes a connection.");
out.println(" To continue an already established eventnet connection you only specify id.");
out.println();
out.close();
}
/*
class ProcessorThread extends Thread {
EventNetRouter router;
ProcessorThread(EventNetRouter router) {
this.router = router;
}
public void run() {
try {
router.start();
} catch(Exception ex) {
log.error("Exception occured inside EventNetRouter start(): ", ex);
}
}
}
*/
}
class ECListener implements EventListener {
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(ECListener.class);
EventCentral ec;
ECListener(EventCentral ec) {
this.ec = ec;
}
public void event(Event ev) {
// we are interested in registration events
try {
if("ECentral".equals(ev.getEventClass())) {
if("RegisterListener".equals(ev.getEventName())) {
Object [] dat = (Object []) ev.getValueObject();
String id = (String) dat[0];
EventCentral.ListenerData ld = (EventCentral.ListenerData) dat[1];
ec.registerEventListener(id, ld.getListener(), ld.getFilter());
}
}
} catch(Exception ex) {
log.error("Exception occured while processing event: " + ev, ex);
}
}
}
class EventNetShellAgentConnector implements EventNetConnector {
private BufferObjectReader shin;
private BufferObjectWriter shout;
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(EventNetShellAgentConnector.class);
public void setOutput(BufferObjectWriter out) {
shout = out;
}
public void setInput(BufferObjectReader in) {
shin = in;
}
public int getPingTime() {
return Integer.MAX_VALUE;
}
public void ping() {
}
public void reinit() {
}
public void reset() {
try {
Thread.sleep(5000);
} catch(Exception ex) {}
}
public void sendBuffer(List ls, int timeout) {
log.info("Sending buffer to out: " + shout + " :: " + ls);
try {
shout.setTimeout(timeout);
Iterator it = ls.iterator();
while(it.hasNext()) {
shout.writeObject(it.next());
}
shout.flush();
} catch(IOException ex) {
throw new RuntimeException(ex);
}
log.info(" done sending.");
}
public List receiveBuffer(int timeout) {
log.info("Receiving buffer from in: " + shin);
try {
shin.setTimeout(timeout);
//List ls = in.getBuffer(timeout, 500);
LinkedList ls = new LinkedList();
ls.add(shin.readObject());
int len = shin.available();
for(int i=0; i<len; i++) {
ls.add(shin.readObject());
}
log.info(" received: " + ls);
return ls;
} catch(IOException ex) {
throw new RuntimeException(ex);
}
}
}