diff options
author | jestin | 2006-07-11 13:57:07 +0000 |
---|---|---|
committer | jestin | 2006-07-11 13:57:07 +0000 |
commit | c9dbee9c2fb98a3ca467ac82a97a9d6f3dba9737 (patch) | |
tree | 19df9d4d9a3874f0a96a9ae6cee6716fe48bd9fd | |
parent | 5acff6b70cee194fbb74df827618169d2e2a6499 (diff) | |
download | ivy-java-c9dbee9c2fb98a3ca467ac82a97a9d6f3dba9737.zip ivy-java-c9dbee9c2fb98a3ca467ac82a97a9d6f3dba9737.tar.gz ivy-java-c9dbee9c2fb98a3ca467ac82a97a9d6f3dba9737.tar.bz2 ivy-java-c9dbee9c2fb98a3ca467ac82a97a9d6f3dba9737.tar.xz |
voir chaque fichier pour les modifs.
Je rajoute les servlets dans la base, j'essayerai de fabriquer un ant file
pour les construire
-rwxr-xr-x | src/Ivy.java | 66 | ||||
-rwxr-xr-x | src/IvyClient.java | 26 | ||||
-rw-r--r-- | src/IvyHttpGatewayClient.java | 120 | ||||
-rw-r--r-- | src/IvyHttpGatewayServlet.java | 567 | ||||
-rw-r--r-- | src/IvyHttpGatewayTest.java | 42 | ||||
-rwxr-xr-x | src/IvyWatcher.java | 48 | ||||
-rw-r--r-- | src/Probe.java | 96 |
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> + * + * 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> + * + * 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> + * + * 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) { |