aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/Ivy.java66
-rwxr-xr-xsrc/IvyClient.java26
-rw-r--r--src/IvyHttpGatewayClient.java120
-rw-r--r--src/IvyHttpGatewayServlet.java567
-rw-r--r--src/IvyHttpGatewayTest.java42
-rwxr-xr-xsrc/IvyWatcher.java48
-rw-r--r--src/Probe.java96
7 files changed, 918 insertions, 47 deletions
diff --git a/src/Ivy.java b/src/Ivy.java
index e8ea0b2..4101931 100755
--- a/src/Ivy.java
+++ b/src/Ivy.java
@@ -13,6 +13,10 @@
*</pre>
*
* CHANGELOG:
+ * 1.2.9:
+ * - introducing setFilter()
+ * - introducing IVYRANGE in to allow the bus service socket to start on a
+ * specific port range ( think of firewalls ), using java -DIVYRANGE=4000-5000 e.g.
* 1.2.8:
* - addclient and removeclient going synchronized
* - domainaddr goes protected in Domain ( gij compatibility )
@@ -83,6 +87,7 @@ import java.net.*;
import java.io.*;
import java.util.*;
import gnu.getopt.Getopt;
+import org.apache.regexp.*;
public class Ivy implements Runnable {
@@ -107,7 +112,7 @@ public class Ivy implements Runnable {
* the library version, useful for development purposes only, when java is
* invoked with -DIVY_DEBUG
*/
- public static final String libVersion ="1.2.8";
+ public static final String libVersion ="1.2.9";
private boolean debug;
private ServerSocket app;
@@ -118,6 +123,7 @@ public class Ivy implements Runnable {
private Vector ivyApplicationListenerList = new Vector();
private Vector ivyBindListenerList = new Vector();
private Vector sendThreads = new Vector();
+ private String[] filter = null;
private boolean stopped=true;
protected int applicationPort; /* Application port number */
protected String ready_message = null;
@@ -129,6 +135,7 @@ public class Ivy implements Runnable {
private int myserial=serial++;
static long current = System.currentTimeMillis();
private static java.util.Random generator = new java.util.Random(current*(serial+1));
+ private String watcherId=null;
/**
* Readies the structures for the software bus connexion.
@@ -225,16 +232,38 @@ public class Ivy implements Runnable {
if (domainbus==null) domainbus=getDomain(null);
Properties sysProp = System.getProperties();
sysProp.put("IVYBUS",domainbus);
- try {
+ String range=(String)sysProp.get("IVYRANGE");
+ RE rangeRE = new RE("(\\d+)-(\\d+)"); // tcp range min and max
+ if ((range!=null)&&rangeRE.match(range)) {
+ int rangeMin=Integer.parseInt(rangeRE.getParen(1));
+ int rangeMax=Integer.parseInt(rangeRE.getParen(2));
+ int index=rangeMin;
+ traceDebug("trying to allocate a TCP port between "+rangeMin+" and "+rangeMax);
+ boolean allocated=false;
+ while (!allocated) try {
+ if (index>rangeMax) throw new IvyException("no available port in IVYRANGE" + range );
+ app = new ServerSocket(index);
+ app.setSoTimeout(TIMEOUTLENGTH);
+ applicationPort = app.getLocalPort();
+ allocated=true;
+ } catch (BindException e) {
+ index++;
+ } catch (IOException e) {
+ throw new IvyException("can't open TCP service socket " + e );
+ }
+ }
+ else try {
app = new ServerSocket(0);
app.setSoTimeout(TIMEOUTLENGTH);
applicationPort = app.getLocalPort();
} catch (IOException e) {
throw new IvyException("can't open TCP service socket " + e );
}
+ // app.getInetAddress().getHostName()) is always 0.0.0.0
traceDebug("lib: "+libVersion+" protocol: "+PROTOCOLVERSION+" TCP service open on port "+applicationPort);
Domain[] d = parseDomains(domainbus);
+ watcherId=getWBUId().replace(' ','*'); // no space in the watcherId
// readies the rendezvous : an IvyWatcher (thread) per domain bus
for (int index=0;index<d.length;index++)
watchers.addElement(new IvyWatcher(this,d[index].domainaddr,d[index].port));
@@ -525,6 +554,37 @@ public class Ivy implements Runnable {
}
}
+ /**
+ * sets the filter expression
+ * @param filter the extensive list of strings beginning the messages
+ * @since 1.2.9
+ *
+ * once this filter is set, when a client subscribes to a regexp of the
+ * form "^dummystring...", there is a check against the filter list. If no
+ * keyword is found to match, the binding is just ignored.
+ */
+ public void setFilter(String[] filter){ this.filter=filter; }
+
+ /**
+ * checks the "validity" of a regular expression if a filter has been set
+ * @since 1.2.9
+ * @param exp a string regular expression
+ * must be synchronized ( RE is not threadsafe )
+ */
+ private static final RE bounded = new RE("^\\^([a-zA-Z0-9_-]+).*");
+ public synchronized boolean CheckRegexp( String exp ) {
+ if (filter==null) return true; // there's no message filter
+ if (!bounded.match(exp)) return true; // the regexp is not bounded
+ //System.out.println("the regexp is bounded, "+bounded.getParen(1));
+ // else the regexp is bounded. The matching string *must* be in the filter
+ for (int i=0;i<filter.length;i++) {
+ String prems = bounded.getParen(1);
+ // traceDebug(" classFilter ["+filter[i]+"] vs regexp ["+prems+"]");
+ if (filter[i].compareTo(prems)==0) return true;
+ }
+ return false;
+ }
+
// a private class used by bindMsgOnce, to ensure that a callback will be
// executed once, and only once
private class Once implements IvyMessageListener {
@@ -772,6 +832,8 @@ public class Ivy implements Runnable {
traceDebug("service thread stopped"); // THREADDEBUG
}
+ protected String getWatcherId() { return watcherId ; }
+
protected int getSerial() { return myserial ; }
private void traceDebug(String s){
if (debug) System.out.println("-->Ivy["+myserial+"]<-- "+s);
diff --git a/src/IvyClient.java b/src/IvyClient.java
index 2c9b9ce..85edfc8 100755
--- a/src/IvyClient.java
+++ b/src/IvyClient.java
@@ -234,26 +234,6 @@ public class IvyClient implements Runnable {
sendString(Die,0,message);
}
- /**
- * checks the "validity" of a regular expression.
- * @param exp the string being a regular expression
- * @return true if the regexp is valid
- * @since 1.2.4
- */
- public boolean CheckRegexp( String exp ) {
- return true;
- /*
- boolean ok = true;
- if ( exp.startsWith( "^" )&&messages_classes!=null) {
- ok=false;
- for (int i=0 ; i < messages_classes.length;i++) {
- if (messages_classes[i].equals(exp.substring(1))) return true;
- }
- }
- return ok;
- */
- }
-
///////////////////////////////////////////////////
//
// PROTECTED METHODS
@@ -493,7 +473,7 @@ public class IvyClient implements Runnable {
break;
case AddRegexp:
String regexp=s.substring(from,b.length);
- if ( CheckRegexp(regexp) ) {
+ if ( bus.CheckRegexp(regexp) ) {
try {
regexps.put(msgId,new RE(regexp));
regexpsText.put(msgId,regexp);
@@ -504,7 +484,9 @@ public class IvyClient implements Runnable {
sendBuffer(Error+e.toString());
}
} else {
- throw new IvyException("regexp Warning exp='"+regexp+"' can't match removing from "+appName);
+ // throw new IvyException("regexp Warning exp='"+regexp+"' can't match removing from "+appName);
+ traceDebug("Warning "+appName+" subscribes to '"+regexp+"', it can't match our message filter");
+ bus.regexpReceived(this,msgId.intValue(),regexp);
}
break;
case DelRegexp:
diff --git a/src/IvyHttpGatewayClient.java b/src/IvyHttpGatewayClient.java
new file mode 100644
index 0000000..585e342
--- /dev/null
+++ b/src/IvyHttpGatewayClient.java
@@ -0,0 +1,120 @@
+/*
+ * IvyHttpGatewayClient.java
+ */
+
+package fr.dgac.ivy.tools;
+
+import java.lang.*;
+import java.io.*;
+import java.net.*;
+
+
+/**
+ * Light remote client for the Ivy HTTP Gateway.
+ * This code is given as example only: it is not compulsery
+ * to use this client with the IvyHttpGatewayServlet.
+ * <strong>Warning:</strong> the error management is rather poor!
+ * <br><br>
+ *
+ * <strong>License:</strong><br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ * See IvyHttpGatewayServlet
+ *
+ * @see IvyHttpGatewayServlet
+ * @see fr.dgac.ivy
+ * @author Francis JAMBON - CLIPS-IMAG/MultiCom
+ * @version 1.0.5
+ */
+public class IvyHttpGatewayClient {
+
+ private String name; // bus name
+ private String domain; // bus domain
+ private String url; // servlet URL
+ private boolean is_started; // bus state (supposed!)
+ private static final boolean debug=true; // debug option
+
+
+ /**
+ * Creates a new instance of IvyHttpGatewayClient.
+ * @param name the bus name
+ * @param url the servlet URL (protocol, host, port and complete servlet path)
+ */
+ public IvyHttpGatewayClient(String name, String url) {
+ this.name = name;
+ this.url = url;
+ this.domain = null;
+ this.is_started = false;
+ }
+
+
+ /**
+ * Starts the Ivy bus.
+ * @param domain the bus domain (address and port)
+ */
+ public void start(String domain) {
+ this.domain = domain;
+ if (!is_started) {
+ String request = this.url+"?"+"cmd=start"+"&name="+this.name+"&domain="+this.domain;
+ doHttpRequest(request);
+ this.is_started=true;
+ }
+ }
+
+
+ /**
+ * Stops the Ivy bus.
+ */
+ public void stop() {
+ if (is_started) {
+ String request = this.url+"?"+"cmd=stop"+"&name="+this.name+"&domain="+this.domain;
+ doHttpRequest(request);
+ this.is_started=false;
+ }
+ }
+
+
+ /**
+ * Sends a message to the Ivy bus.
+ * The bus must be started before sending a message.
+ * @param msg the message to be sent
+ */
+ public void sendMsg(String msg) {
+ if (is_started) {
+ String request = this.url+"?"+"cmd=sendmsg"+"&name="+this.name+"&domain="+this.domain+"&msg="+msg;
+ doHttpRequest(request);
+ }
+ }
+
+
+ /**
+ * Generic request to the servlet.
+ * @param request the URL (protocol, host, port, servlet path and parameters)
+ */
+ private static void doHttpRequest(String request) {
+ if (debug) System.out.println();
+ if (debug) System.out.println("-------------------- start of request ------------------");
+ if (debug) System.out.println("IvyHttpGatewayClient");
+ if (debug) System.out.println("REQUEST: "+request);
+ try {
+ URL servlet = new URL(request);
+ URLConnection connection = servlet.openConnection();
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(
+ servlet.openStream()));
+ String input_line;
+ if (debug) System.out.println("ANSWER:");
+ while ((input_line=in.readLine()) != null)
+ if (debug) System.out.println(input_line);
+ in.close();
+ } catch (MalformedURLException e) {
+ System.err.println("IvyHttpGatewayClient ERROR: URL Malformed");
+ if (debug) e.printStackTrace();
+ } catch (IOException e) {
+ System.err.println("IvyHttpGatewayClient ERROR: Open connection failed");
+ if (debug) e.printStackTrace();
+ }
+ if (debug) System.out.println("-------------------- end of request --------------------");
+ if (debug) System.out.println();
+ }
+
+}
diff --git a/src/IvyHttpGatewayServlet.java b/src/IvyHttpGatewayServlet.java
new file mode 100644
index 0000000..ca886b5
--- /dev/null
+++ b/src/IvyHttpGatewayServlet.java
@@ -0,0 +1,567 @@
+/*
+ * IvyHttpGatewayServlet.java
+ */
+
+package fr.dgac.ivy.tools;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import fr.dgac.ivy.*;
+
+
+/**
+ * Simple gateway between HTTP and Ivy software bus protocols.
+ * <br><br>
+ * The gateway enables remote clients to manage Ivy clients via
+ * HTTP protocol. It aims at solving Ivy limitations to local networks.
+ * Features are limited to Ivy bus start(), stop() and sendMsg() methods.
+ * The Ivy HTTP gateway considers two Ivy instances as identical if their
+ * buses names, buses domains and clients IP names equals pair-by-pair.
+ * <br><br>
+ * Ivy main web page:
+ * <a target="_blank" href="http://www.tls.cena.fr/products/ivy/">
+ * http://www.tls.cena.fr/products/ivy/</a>
+ * <br>
+ * Author home page:
+ * <a target="_blank" href="http://www-clips.imag.fr/multicom/User/francis.jambon/index-en.html">
+ * http://www-clips.imag.fr/multicom/User/francis.jambon/index-en.html</a>
+ * <br><br>
+ *
+ * <strong>Requests examples:</strong>
+ * <pre>
+ * - start bus: http://host/IvyHttpGatewayServlet?cmd=start&name=test&domain=228.1.2.4:5678
+ * - start bus (with message): http://host/IvyHttpGatewayServlet?cmd=start&name=test&domain=228.1.2.4:5678&msg=ready
+ * - stop bus: http://host/IvyHttpGatewayServlet?cmd=stop&name=test&domain=228.1.2.4:5678
+ * - send message: http://host/IvyHttpGatewayServlet?cmd=sendmsg&name=test&domain=228.1.2.4:5678&msg=hello
+ * - interactive webpage: http://host/IvyHttpGatewayServlet
+ * - status information: http://host/IvyHttpGatewayServlet?cmd=status
+ * - kill all buses managed by the servlet: http://host/IvyHttpGatewayServlet?cmd=killall
+ * </pre>
+ *
+ * <strong>Versions history:</strong>
+ * <pre>
+ * - 1.1: new package structure, default address and documentation
+ * non-regression tests with new Ivy 1.2.8-rc2 library
+ * - 1.0.5: correction of the form encoding attribute incompatibility with IE
+ * - 1.0.4: correction of the button element incompatibility with IE
+ * - 1.0.3: correction of the remote host identification bug
+ * - 1.0.2: start command can be invoked without message
+ * - 1.0.1: licence and some name modifications
+ * - 1.0: first working release
+ * </pre>
+ *
+ * <strong>Bugs:</strong>
+ * <pre>
+ * - the POST method does not work in the interactive webpage
+ * (the parameters are not transmitted)
+ * </pre>
+ *
+ * <strong>License:</strong>
+ * <br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ * GNU Lesser General Public License, see <a target="_blank" href="http://www.gnu.org/copyleft/lesser.html">http://www.gnu.org/copyleft/lesser.html</a>
+ *
+ * @see fr.dgac.ivy
+ * @author Francis JAMBON - CLIPS-IMAG/MultiCom
+ * @version 1.1
+ */
+public class IvyHttpGatewayServlet extends HttpServlet {
+
+ // Ivy bus(es) list
+ private Hashtable buses;
+
+ // Valid name, domain and msg regexps
+ private static final String NAME_REGEXP="[^(\\r)]+";
+ private static final String DOMAIN_REGEXP="(\\d){1,3}+(\\.)(\\d){1,3}+(\\.)(\\d){1,3}+(\\.)(\\d){1,3}+(\\:)(\\d){1,5}+";
+ private static final String MSG_REGEXP="[^(\\r)]*";
+
+ // Default values for the interactive webpage
+ private static final String DEFAULT_NAME="test";
+ private static final String DEFAULT_DOMAIN="228.1.2.4:5678";
+ private static final String DEFAULT_MSG="hello";
+
+
+ /**
+ * Initializes the servlet.
+ */
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ this.buses = new Hashtable();
+ }
+
+
+ /**
+ * Destroys the servlet.
+ */
+ public void destroy() {
+ Enumeration eb = this.buses.elements();
+ while (eb.hasMoreElements()) {
+ ((Ivy)eb.nextElement()).stop();
+ }
+ // wait 100ms to prevent ugly Ivy bus(es) disconnection
+ try { Thread.sleep(100); } catch (InterruptedException ie) {}
+ this.buses.clear();
+ this.buses=null;
+ }
+
+
+ /**
+ * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
+ * @param request servlet request
+ * @param response servlet response
+ */
+ protected synchronized void processRequest(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ if (request.getQueryString()!=null) {
+ if (request.getParameter("cmd").equals("start"))
+ processRequestStart(request,response);
+ else if (request.getParameter("cmd").equals("stop"))
+ processRequestStop(request,response);
+ else if (request.getParameter("cmd").equals("sendmsg"))
+ processRequestSendMsg(request,response);
+ else if (request.getParameter("cmd").equals("status"))
+ processRequestStatus(request,response);
+ else if (request.getParameter("cmd").equals("killall"))
+ processRequestKillAll(request,response);
+ else
+ processRequestError(request, response);
+ } else
+ processRequestWebpage(request,response);
+ }
+
+
+ /**
+ * Starts an Ivy bus and adds it to the list.
+ */
+ private void processRequestStart(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println("<head>");
+ out.println("<title>Ivy HTTP Gateway</title>");
+ out.println("</head>");
+ out.println("<body>");
+ out.println("<hr>");
+ out.println("<pre>");
+
+ String from = request.getRemoteHost();
+ String name = request.getParameter("name");
+ String domain = request.getParameter("domain");
+ String msg = request.getParameter("msg");
+
+ out.println("Ivy HTTP Gateway");
+ out.println("Request from ["+from+"]");
+ out.println();
+ out.println("START");
+ out.println("- name="+name);
+ out.println("- domain="+domain);
+ if (msg!=null) out.println("- msg="+msg);
+ out.println();
+
+ if ( domain!=null && domain.matches(DOMAIN_REGEXP) &&
+ name!=null && name.matches(NAME_REGEXP) &&
+ ( msg==null || (msg!=null && msg.matches(MSG_REGEXP)) ) ) {
+
+ IvyHashKey ihk = new IvyHashKey(from, name, domain);
+ if (!this.buses.containsKey(ihk)) {
+ try {
+ Ivy bus = new Ivy(name, msg, null);
+ bus.start(domain);
+ this.buses.put(ihk,bus);
+ out.println("Ivy bus started");
+ } catch (IvyException ie) {
+ out.println("ERROR while starting Ivy bus: "+ie.getMessage());
+ }
+ } else {
+ out.println("ERROR: Similar Ivy bus already started");
+ }
+ } else {
+ out.println("ERROR: Parameter(s) not valid");
+ }
+
+ out.println("</pre>");
+ out.println("<hr>");
+ out.println("</body>");
+ out.println("</html>");
+ out.close();
+ }
+
+
+ /**
+ * Stops an Ivy bus and removes it from the list.
+ */
+ private void processRequestStop(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println("<head>");
+ out.println("<title>Ivy HTTP Gateway</title>");
+ out.println("</head>");
+ out.println("<body>");
+ out.println("<hr>");
+ out.println("<pre>");
+
+ String from = request.getRemoteHost();
+ String name = request.getParameter("name");
+ String domain = request.getParameter("domain");
+
+ out.println("Ivy HTTP Gateway");
+ out.println("Request from ["+from+"]");
+ out.println();
+ out.println("STOP");
+ out.println("- name="+name);
+ out.println("- domain="+domain);
+ out.println();
+
+ if ( domain!=null && domain.matches(DOMAIN_REGEXP) &&
+ name!=null && name.matches(NAME_REGEXP) ) {
+
+ IvyHashKey ihk = new IvyHashKey(from, name, domain);
+ if (this.buses.containsKey(ihk)) {
+ Ivy bus = (Ivy)(this.buses.get(ihk));
+ bus.stop();
+ // wait 100ms to prevent ugly Ivy bus disconnection
+ try { Thread.sleep(100); } catch (InterruptedException ie) {}
+ this.buses.remove(ihk);
+ bus=null;
+ out.println("Ivy bus stopped");
+ } else {
+ out.println("ERROR: Ivy bus does not exist");
+ }
+ } else {
+ out.println("ERROR: Parameter(s) not valid");
+ }
+
+ out.println("</pre>");
+ out.println("<hr>");
+ out.println("</body>");
+ out.println("</html>");
+ out.close();
+ }
+
+
+ /**
+ * Sends a message to an Ivy bus.
+ */
+ private void processRequestSendMsg(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println("<head>");
+ out.println("<title>Ivy HTTP Gateway</title>");
+ out.println("</head>");
+ out.println("<body>");
+ out.println("<hr>");
+ out.println("<pre>");
+
+ String from = request.getRemoteHost();
+ String name = request.getParameter("name");
+ String domain = request.getParameter("domain");
+ String msg = request.getParameter("msg");
+
+ out.println("Ivy HTTP Gateway");
+ out.println("Request from ["+from+"]");
+ out.println();
+ out.println("SEND MESSAGE");
+ out.println("- name="+name);
+ out.println("- domain="+domain);
+ out.println("- msg="+msg);
+ out.println();
+
+ if ( domain!=null && domain.matches(DOMAIN_REGEXP) &&
+ name!=null && name.matches(NAME_REGEXP) &&
+ msg !=null && msg.matches(MSG_REGEXP) ) {
+
+ IvyHashKey ihk = new IvyHashKey(from, name, domain);
+ if (this.buses.containsKey(ihk)) {
+ try {
+ Ivy bus = (Ivy)(this.buses.get(ihk));
+ int count = bus.sendMsg(msg);
+ out.println("Message sent to "+count+" client(s)");
+ } catch (IvyException ie) {
+ out.println("ERROR sending message: "+ie.getMessage());
+ }
+ } else {
+ out.println("ERROR: Ivy bus does not exist");
+ }
+ } else {
+ out.println("ERROR: Parameter(s) not valid");
+ }
+
+ out.println("</pre>");
+ out.println("<hr>");
+ out.println("</body>");
+ out.println("</html>");
+ out.close();
+ }
+
+
+ /**
+ * Status page.
+ */
+ private void processRequestStatus(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println("<head>");
+ out.println("<title>Ivy HTTP Gateway</title>");
+ out.println("</head>");
+ out.println("<body>");
+ out.println("<hr>");
+ out.println("<pre>");
+
+ out.println("Ivy HTTP Gateway");
+ out.println("Request from ["+request.getRemoteHost()+"]");
+ out.println();
+ out.println("STATUS");
+ out.println();
+ out.println(this.buses.size()+" Ivy bus(es) alive");
+
+ Enumeration ek = this.buses.keys();
+ while (ek.hasMoreElements()) {
+ IvyHashKey ihk = (IvyHashKey)(ek.nextElement());
+ out.println("- Ivy bus ["+ihk.getName()+
+ "] on domain ["+ihk.getDomain()+
+ "] managed by ["+ihk.getFrom()+"]");
+ }
+
+ out.println("</pre>");
+ out.println("<hr>");
+ out.println("</body>");
+ out.println("</html>");
+ out.close();
+ }
+
+
+ /**
+ * Stops all Ivy buses.
+ */
+ private void processRequestKillAll(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println("<head>");
+ out.println("<title>Ivy HTTP Gateway</title>");
+ out.println("</head>");
+ out.println("<body>");
+ out.println("<hr>");
+ out.println("<pre>");
+
+ out.println("Ivy HTTP Gateway");
+ out.println("Request from ["+request.getRemoteHost()+"]");
+ out.println();
+ out.println("KILL ALL");
+ out.println();
+
+ if (this.buses.size()!=0) {
+
+ Enumeration eb = this.buses.elements();
+ while (eb.hasMoreElements()) {
+ ((Ivy)eb.nextElement()).stop();
+ }
+ Enumeration ek = this.buses.keys();
+ while (ek.hasMoreElements()) {
+ IvyHashKey ihk = (IvyHashKey)(ek.nextElement());
+ out.println("Ivy bus ["+ihk.getName()+
+ "] on domain ["+ihk.getDomain()+
+ "] managed by ["+ihk.getFrom()+
+ "] stopped");
+ }
+ // wait 100ms to prevent ugly Ivy bus(es) disconnection
+ try { Thread.sleep(100); } catch (InterruptedException ie) {}
+ this.buses.clear();
+ } else {
+ out.println("NOTHING TO DO: no alive Ivy bus");
+ }
+
+ out.println("</pre>");
+ out.println("<hr>");
+ out.println("</body>");
+ out.println("</html>");
+ out.close();
+ }
+
+
+ /**
+ * Error (request not understood).
+ */
+ private void processRequestError(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println("<head>");
+ out.println("<title>Ivy HTTP Gateway</title>");
+ out.println("</head>");
+ out.println("<body>");
+ out.println("<hr>");
+ out.println("<pre>");
+
+ out.println("Ivy HTTP Gateway");
+ out.println("Request from ["+request.getRemoteHost()+"]");
+ out.println();
+ out.println("ERROR: request not understood");
+ out.println();
+ out.println("Parameters string readed: "+request.getQueryString());
+
+ out.println("</pre>");
+ out.println("<hr>");
+ out.println("</body>");
+ out.println("</html>");
+ out.close();
+ }
+
+
+ /**
+ * Interactive webpage (request without parameter).
+ */
+ private void processRequestWebpage(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println(" <head>");
+ out.println(" <title>Ivy HTTP Gateway</title>");
+ out.println(" </head>");
+ out.println(" <body>");
+ out.println(" <h2>Ivy HTTP Gateway Servlet Interactive Webpage</h2>");
+ out.println(" <hr>");
+ out.println(" <h3>Global Commands</h3>");
+ out.println(" <form method=\"get\"");
+ out.println(" action=\""+request.getRequestURL()+"\"");
+ out.println(" name=\"Global Commands\">");
+ out.println(" <table style=\"text-align: left;\" border=\"0\" cellpadding=\"0\" cellspacing=\"6\">");
+ out.println(" <tbody>");
+ out.println(" <tr>");
+ out.println(" <td>Ivy bus(es) status: </td>");
+ out.println(" <td><input type=\"submit\" value=\"status\" name=\"cmd\"></td>");
+ out.println(" <tr>");
+ out.println(" </tr>");
+ out.println(" <td>Kill all (warning!): </td>");
+ out.println(" <td><input type=\"submit\" value=\"killall\" name=\"cmd\"></td>");
+ out.println(" </tr>");
+ out.println(" </tbody>");
+ out.println(" </table>");
+ out.println(" </form>");
+ out.println(" <hr>");
+ out.println(" <h3>Bus Commands</h3>");
+ out.println(" <form method=\"get\"");
+ out.println(" action=\""+request.getRequestURL()+"\"");
+ out.println(" name=\"Bus Commands\">");
+ out.println(" <table style=\"text-align: left;\" border=\"0\" cellpadding=\"0\" cellspacing=\"6\">");
+ out.println(" <tbody>");
+ out.println(" <tr>");
+ out.println(" <td><label>Name:</label> </td>");
+ out.println(" <td><input name=\"name\" value=\""+DEFAULT_NAME+"\"> </td>");
+ out.println(" <td>(name of the Ivy agent - <strong>required</strong>)</td>");
+ out.println(" </tr>");
+ out.println(" <tr>");
+ out.println(" <td><label>Domain:</label> </td>");
+ out.println(" <td><input value=\""+DEFAULT_DOMAIN+"\" name=\"domain\"> </td>");
+ out.println(" <td>(IP address and port - <strong>required</strong>) </td>");
+ out.println(" </tr>");
+ out.println(" <tr>");
+ out.println(" <td><label>Message:</label> </td>");
+ out.println(" <td><input name=\"msg\" value=\""+DEFAULT_MSG+"\"> </td>");
+ out.println(" <td>(message to be sent)</td>");
+ out.println(" </tr>");
+ out.println(" </tbody>");
+ out.println(" </table>");
+ out.println(" <table style=\"text-align: left;\" border=\"0\" cellpadding=\"0\" cellspacing=\"6\">");
+ out.println(" <tbody>");
+ out.println(" <tr>");
+ out.println(" <td>Commands: </td>");
+ out.println(" <td><input type=\"submit\" value=\"start\" name=\"cmd\"> </td>");
+ out.println(" <td><input type=\"submit\" value=\"stop\" name=\"cmd\"> </td>");
+ out.println(" <td><input type=\"submit\" value=\"sendmsg\" name=\"cmd\"> </td>");
+ out.println(" </tr>");
+ out.println(" </tbody>");
+ out.println(" </table>");
+ out.println(" </form>");
+ out.println(" <hr>");
+ out.println(" <code>Request from ["+request.getRemoteHost()+"]</code>");
+ out.println(" <br>");
+ out.println(" <code>CLIPS-IMAG/MultiCom</code>");
+ out.println(" </body>");
+ out.println("</html>");
+ out.close();
+ }
+
+
+ /**
+ * Handles the HTTP <code>GET</code> method.
+ * @param request servlet request
+ * @param response servlet response
+ */
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ processRequest(request, response);
+ }
+
+
+ /**
+ * Handles the HTTP <code>POST</code> method.
+ * @param request servlet request
+ * @param response servlet response
+ */
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ processRequest(request, response);
+ }
+
+
+ /**
+ * Returns a short description of the servlet.
+ * @return the short description
+ */
+ public String getServletInfo() {
+ return "Ivy HTTP Gateway";
+ }
+
+
+ /**
+ * Internal class for Ivy bus hashkey definition.
+ */
+ private class IvyHashKey {
+
+ private String from;
+ private String name;
+ private String domain;
+
+ public IvyHashKey(String from, String name, String domain) {
+ this.from=from;
+ this.name=name;
+ this.domain=domain;
+ }
+
+ public String getFrom() { return this.from; }
+
+ public String getName() { return this.name; }
+
+ public String getDomain() { return this.domain; }
+
+ public boolean equals(Object o) {
+ if (o!=null && o instanceof IvyHashKey) {
+ IvyHashKey ihk = (IvyHashKey)o;
+ return (this.from.equals(ihk.from) &&
+ this.name.equals(ihk.name) &&
+ this.domain.equals(ihk.domain) );
+ } else return false;
+ }
+
+ public int hashCode() {
+ return this.name.hashCode();
+ }
+
+ }
+
+
+}
diff --git a/src/IvyHttpGatewayTest.java b/src/IvyHttpGatewayTest.java
new file mode 100644
index 0000000..e8de0aa
--- /dev/null
+++ b/src/IvyHttpGatewayTest.java
@@ -0,0 +1,42 @@
+/*
+ * IvyHttpGatewayTest.java
+ * Created on 18 avril 2005, 20:25
+ */
+
+package fr.dgac.ivy.tools;
+
+
+/**
+ * Dynamic tests of the light remote client for the Ivy HTTP Gateway.
+ * <br><br>
+ *
+ * <strong>License:</strong><br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ * See IvyHttpGatewayServlet
+ *
+ * @see IvyHttpGatewayClient
+ * @see IvyHttpGatewayServlet
+ * @see fr.dgac.ivy
+ * @author Francis JAMBON - CLIPS-IMAG/MultiCom
+ * @version See IvyHttpGatewayClient
+ */
+public class IvyHttpGatewayTest {
+
+ /**
+ * Main method: for testing purpose.
+ * @param args command line arguments (not used)
+ */
+ public static void main(String[] args) {
+ System.out.println("### START OF TEST ###");
+ IvyHttpGatewayClient test = new IvyHttpGatewayClient(
+ "test",
+ "http://localhost:8084/Ivy-HttpGateway/IvyHttpGatewayServlet");
+ test.start(
+ "228.1.2.4:5678");
+ test.sendMsg(
+ "hello");
+ test.stop();
+ System.out.println("### END OF TEST ###");
+ }
+
+}
diff --git a/src/IvyWatcher.java b/src/IvyWatcher.java
index b37fa6b..4b06b1b 100755
--- a/src/IvyWatcher.java
+++ b/src/IvyWatcher.java
@@ -14,6 +14,11 @@
* thing.
*
* CHANGELOG:
+ * 1.2.9:
+ * - added an application Id in the UDP broadcast. It seems to be ok with
+ * most implementations ( VERSION PORT APPID APPNAME \n) is compatible with (VERSION
+ * APPID). If I receive a broadcast with with the same TCP port number,
+ * I ignore the first and accept the new ones
* 1.2.8:
* - alreadyBroadcasted was static, thus Ivy Agents within the same JVM used
* to share the list of agents already connected. A nasty bug.
@@ -71,6 +76,7 @@ import java.util.Hashtable;
class IvyWatcher implements Runnable {
private static boolean debug = (System.getProperty("IVY_DEBUG")!=null);
private boolean isMulticastAddress = false;
+ private boolean alreadyIgnored = false;
private Ivy bus; /* master bus controler */
private DatagramSocket broadcast; /* supervision socket */
private InetAddress localhost,loopback;
@@ -80,6 +86,7 @@ class IvyWatcher implements Runnable {
private InetAddress group;
private static int serial=0;
private int myserial=serial++;
+ private String busWatcherId = null;
/**
* creates an Ivy watcher
@@ -90,6 +97,7 @@ class IvyWatcher implements Runnable {
this.bus = bus;
this.domainaddr=domainaddr;
this.port=port;
+ busWatcherId=bus.getWatcherId();
listenThread = new Thread(this);
// create the MulticastSocket
try {
@@ -143,12 +151,37 @@ class IvyWatcher implements Runnable {
continue;
}
remotePort = Integer.parseInt(re.getParen(2));
- if (bus.applicationPort==remotePort) {
- traceDebug("ignoring a broadcast from "+ remotehostname+":"+packet.getPort()
- +" on my port number ("+remotePort+"), it's probably me");
- // TODO check this in a better way
- continue;
- }
+ if (bus.applicationPort==remotePort) { // if (same port number)
+ RE reId = new RE("([0-9]*) ([0-9]*) ([^ ]*) (.*)");
+ if (reId.match(msg)&&(busWatcherId!=null)) {
+ traceDebug("there's an appId: "+reId.getParen(3));
+ String otherId=reId.getParen(3);
+ String otherName=reId.getParen(4);
+ if (busWatcherId.compareTo(otherId)==0) {
+ // same port #, same bus Id, It's me, I'm outta here
+ traceDebug("ignoring my own broadcast");
+ continue;
+ } else {
+ // same port #, different bus Id, it's another agent
+ // implementing the Oh Soooo Cool watcherId undocumented
+ // unprotocolar Ivy add on
+ traceDebug("accepting a broadcast from a same port by "+otherName);
+ }
+ } else {
+ // there's no watcherId in the broacast. I fall back to a
+ // crude strategy: I ignore the first broadcast with the same
+ // port number, and accept the following ones
+ if (alreadyIgnored) {
+ traceDebug("received another broadcast from "+ remotehostname+":"+packet.getPort()
+ +" on my port number ("+remotePort+"), it's probably someone else");
+ } else {
+ alreadyIgnored=true;
+ traceDebug("ignoring a broadcast from "+ remotehostname+":"+packet.getPort()
+ +" on my port number ("+remotePort+"), it's probably me");
+ continue;
+ }
+ }
+ } // end if (same port #)
traceDebug("broadcast accepted from " +remotehostname
+":"+packet.getPort()+", port:"+remotePort+", protocol version:"+version);
if (!alreadyBroadcasted(remotehost.toString(),remotePort)) {
@@ -231,7 +264,8 @@ class IvyWatcher implements Runnable {
}
synchronized void start() throws IvyException {
- String hello = Ivy.PROTOCOLVERSION + " " + bus.applicationPort + "\n";
+ // String hello = Ivy.PROTOCOLVERSION + " " + bus.applicationPort + "\n";
+ String hello = Ivy.PROTOCOLVERSION + " " + bus.applicationPort + " "+busWatcherId+" "+bus.selfIvyClient.getApplicationName()+"\n";
if (broadcast==null) throw new IvyException("IvyWatcher PacketSender null broadcast address");
new PacketSender(hello); // notifies our arrival on each domain: protocol version + port
listenThread.start();
diff --git a/src/Probe.java b/src/Probe.java
index 7f5e7b5..ab40a11 100644
--- a/src/Probe.java
+++ b/src/Probe.java
@@ -7,6 +7,9 @@
* (c) CENA
*
* Changelog:
+ * 1.2.9
+ * - added the .time command
+ * - added the .bound *
* 1.2.8
* - added a fr.dgac.ivy.tools subsection
* 1.2.7
@@ -56,24 +59,54 @@ import org.apache.regexp.*;
public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBindListener, Runnable {
- public static final String helpCommands = "Available commands:\n.die CLIENT\t\t\t* sends a die message\n.dieall-yes-i-am-sure\t\t* sends a die to all clients\n.direct CLIENT ID MSG\t\t* sends the direct message to the client\n.bye\t\t\t\t* quits the probe\n.quit\t\t\t\t* quites the probe\n.list\t\t\t\t* lists the clients on the bus\n.bind REGEXP\t\t\t* subscribes to a regexp\n.unbind REGEXP\t\t\t* unsubscribes to a regexp\n.bound CLIENT\t\t\t* lists the subscriptions of a client\n.bound\t\t\t\t* lists the probe's subscriptions\n.where CLIENT\t\t\t* displays the host where a client runs";
+ public static final String helpCommands = "Available commands:\n"+
+ ".die CLIENT\t\t\t* sends a die message\n"+
+ ".dieall-yes-i-am-sure\t\t* sends a die to all clients\n"+
+ ".direct CLIENT ID MSG\t\t* sends the direct message to the client\n"+
+ ".bye\t\t\t\t* quits the probe\n"+
+ ".quit\t\t\t\t* quites the probe\n"+
+ ".list\t\t\t\t* lists the clients on the bus\n"+
+ ".bind REGEXP\t\t\t* subscribes to a regexp\n"+
+ ".unbind REGEXP\t\t\t* unsubscribes to a regexp\n"+
+ ".bound CLIENT\t\t\t* lists the subscriptions of a client, .bound * to get all\n"+
+ ".bound\t\t\t\t* lists the probe's subscriptions\n"+
+ ".where CLIENT\t\t\t* displays the host where a client runs\n"+
+ ".time COUNT MESSAGE\t\t\t* measures the time it takes to send COUNT messages\n"
+ ;
- public static final String helpmsg = "usage: java fr.dgac.ivy.Probe [options] [regexp]\n\t-b BUS\tspecifies the Ivy bus domain\n\t-n ivyname (default JPROBE)\n\t-q\tquiet, no tty output\n\t-d\tdebug\n\t-t\ttime stamp each message\n\t-s\tsends to self\n\t-h\thelp\n\n\t regexp is a Perl5 compatible regular expression";
+ public static final String helpmsg =
+ "usage: java fr.dgac.ivy.Probe [options] [regexp]\n"+
+ "\t-b BUS\tspecifies the Ivy bus domain\n"+
+ "\t-c CLASS\tuses a message class CLASS=Word1,Word2,Word3...\n"+
+ "\t-n ivyname (default JPROBE)\n"+
+ "\t-q\tquiet, no tty output\n"+
+ "\t-d\tdebug\n"+
+ "\t-t\ttime stamp each message\n"+
+ "\t-s\tsends to self\n"+
+ "\t-h\thelp\n"+
+ "\n\t regexp is a Perl5 compatible regular expression";
public static void main(String[] args) throws IvyException {
- Getopt opt = new Getopt("Probe",args,"n:b:dqsht");
+ Getopt opt = new Getopt("Probe",args,"n:b:c:dqsht");
int c;
boolean timestamp=false;
boolean quiet=false;
boolean sendsToSelf=false;
String domain=Ivy.getDomain(null);
String name="JPROBE";
+ String[] messageClass=null;
while ((c = opt.getopt()) != -1) switch (c) {
case 'd':
Properties sysProp = System.getProperties();
sysProp.put("IVY_DEBUG","yes");
break;
case 'b': domain=opt.getOptarg(); break;
+ case 'c':
+ java.util.StringTokenizer classTok = new java.util.StringTokenizer(opt.getOptarg(),",");
+ messageClass=new String[classTok.countTokens()];
+ System.out.println("YANNNN "+messageClass.length);
+ for (int i=0;classTok.hasMoreElements();) messageClass[i++]=new String((String)classTok.nextElement());
+ break;
case 'n': name=opt.getOptarg(); break;
case 'q': quiet=true; break;
case 't': timestamp=true; break;
@@ -86,6 +119,11 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin
Ivy bus=new Ivy(name,name+" ready",null);
bus.addBindListener(p);
bus.sendToSelf(sendsToSelf);
+ if (messageClass!=null) {
+ System.out.println("using a message class filter of "+messageClass.length+" elements");
+ for (int i=0;i<messageClass.length;i++) System.out.println(messageClass[i]);
+ bus.setFilter(messageClass);
+ }
for (int i=opt.getOptind();i<args.length;i++) {
try {
bus.bindMsg(args[i],p);
@@ -103,21 +141,15 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin
private volatile Thread looperThread;
private Ivy bus;
private boolean timestamp,quiet,debug,exitOnDie=false;
- private RE directMsgRE;
+ private static RE directMsgRE = new RE("^\\.direct ([^ ]*) ([0-9]+) (.*)");
+ private static RE timeCountRE = new RE("^\\.time (\\d+) (.*)");
public Probe(BufferedReader in, boolean timestamp,boolean quiet,boolean debug) {
this.in=in;
this.timestamp=timestamp;
this.quiet=quiet;
this.debug = debug;
- try {
- directMsgRE = new RE("^\\.direct ([^ ]*) ([0-9]+) (.*)");
- } catch (RESyntaxException ree) {
- System.err.println("Regexp fatal error in the Probe program.");
- ree.printStackTrace();
- System.exit(0);
- }
}
public void start(Ivy bus) throws IvyException {
@@ -131,10 +163,10 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin
public void setExitOnDie(boolean b) { exitOnDie=b; }
public void run() {
- traceDebug("Thread started");
+ traceDebug("Probe Thread started");
Thread thisThread=Thread.currentThread();
String s;
- println("probe ready, type .help and return to get help");
+ println(bus.getSelfIvyClient().getApplicationName()+ " ready, type .help and return to get help");
// "infinite" loop on keyboard input
while (looperThread==thisThread) {
try {
@@ -190,16 +222,35 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin
} else {
println("you can't unsubscribe to " + regexp + ", your're not subscribed to it");
}
+ } else if (s.lastIndexOf(".bound *")>=0){
+ Vector v=bus.getIvyClients();
+ int total=0;
+ int boundedtotal=0;
+ for (int i=0;i<v.size();i++) {
+ IvyClient ic=(IvyClient)v.elementAt(i);
+ for (Enumeration e = ic.getRegexps();e.hasMoreElements();) {
+ total++;
+ String r = (String)e.nextElement();
+ if (r.startsWith("^")) boundedtotal++;
+ println(ic.getApplicationName()+" has subscribed to: "+r);
+ }
+ System.out.println("total: "+total+", unbounded:"+(total-boundedtotal));
+ }
} else if (s.lastIndexOf(".bound ")>=0){
+ int total=0;
+ int boundedtotal=0;
String target=s.substring(7);
Vector v=bus.getIvyClientsByName(target);
if (v.size()==0) println("no Ivy client with the name \""+target+"\"");
for (int i=0;i<v.size();i++) {
IvyClient ic=(IvyClient)v.elementAt(i);
- println(target+" has subscribed to:");
for (Enumeration e = ic.getRegexps();e.hasMoreElements();) {
- println("\t"+(String)e.nextElement());
+ total++;
+ String r = (String)e.nextElement();
+ if (r.startsWith("^")) boundedtotal++;
+ println(target+" has subscribed to: "+(String)e.nextElement());
}
+ System.out.println("total: "+total+", unbounded:"+(total-boundedtotal));
}
} else if (s.lastIndexOf(".bound")>=0){
println("you have subscribed to:");
@@ -230,6 +281,17 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin
for (int i=0;i<v.size();i++) {
println(target+" runs on "+((IvyClient)v.elementAt(i)).getHostName());
}
+ } else if (timeCountRE.match(s)) {
+ long before = new java.util.Date().getTime();
+ int times=Integer.parseInt(timeCountRE.getParen(1));
+ try {
+ int n=0;
+ for (int i=0;i<times;i++) n=bus.sendMsg(timeCountRE.getParen(2));
+ long after = new java.util.Date().getTime();
+ println("-> it took "+(after-before)+"ms to send "+times+" to " +n+" peers");
+ } catch (IvyException ie) {
+ println("-> not sent, the line contains incorrect characters");
+ }
} else if ( s.lastIndexOf(".help")>=0) {
println(helpCommands);
} else if ( s.charAt(0)=='.') {
@@ -245,7 +307,9 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin
} // parseCommand
public void bindPerformed(IvyClient client,int id,String re) {
- println(client.getApplicationName() + " subscribes to " +re );
+ String s="";
+ if (!bus.CheckRegexp(re)) s=" WITH NO EFFECT";
+ println(client.getApplicationName() + " subscribes to " +re+s);
}
public void unbindPerformed(IvyClient client,int id,String re) {