/***************************************************************** * JADE - Java Agent DEvelopment Framework is a framework to develop * multi-agent systems in compliance with the FIPA specifications. * Copyright (C) 2000 CSELT S.p.A. * * The updating of this file to JADE 2.0 has been partially supported by the * IST-1999-10211 LEAP Project * * This file refers to parts of the FIPA 99/00 Agent Message Transport * Implementation Copyright (C) 2000, Laboratoire d'Intelligence * Artificielle, Ecole Polytechnique Federale de Lausanne * * GNU Lesser General Public License * * This library is free software; you can redistribute it sand/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *****************************************************************/ /** * MessageTransportProtocol.java * * * @author Jose Antonio Exposito * @author MARISM-A Development group ( marisma-info@ccd.uab.es ) * @version 0.1 * @author Nicolas Lhuillier (Motorola Labs) * @version 1.0 */ package jade.mtp.http; import java.net.*; import java.io.*; import java.util.Hashtable; import java.util.Enumeration; import jade.mtp.InChannel; import jade.mtp.MTP; import jade.mtp.MTPException; import jade.mtp.TransportAddress; import jade.domain.FIPAAgentManagement.Envelope; import jade.core.Profile; import jade.core.ProfileImpl; import jade.util.ExtendedProperties; public class MessageTransportProtocol implements MTP { // DEFAULT VALUES private static final int IN_PORT = 7778; private static final String OUT_PORT = "-1"; private static final String PROXY_PORT = "1080"; private static final String MAX_KA = "10"; private static final String POLICY = "conservative"; //conservative or aggressive private static final String PREFIX = "jade_mtp_http_"; private static final String TIMEOUT = "60000"; // 60 seconds private int numKA; private String proxyHost; private int proxyPort; private int timeout; //private int proxyKATimeout; private boolean policy; private boolean keepAlive = false; private boolean useProxy = false; private String[] protocols = {}; private String FIPA_NAME = "fipa.mts.mtp.http.std"; private Hashtable addr2srv = new Hashtable(); //Object Keep-Alive connections private KeepAlive ka; /** MTP Interface Methods */ public TransportAddress strToAddr(String rep) throws MTPException { try { return new HTTPAddress(rep); } catch(MalformedURLException mue) { throw new MTPException("Address mismatch: this is not a valid HTTP address."); } } public String addrToStr(TransportAddress ta) throws MTPException { try { return ((HTTPAddress) ta).toString(); } catch(Exception e) { throw new MTPException(e.toString()); } } public String getName() { return FIPA_NAME; } public String[] getSupportedProtocols() { return protocols; } /******************************** * InChannel Interface Methods * *********************************/ /** * Old method, only for compliance with former versions (prior 3.0) */ public TransportAddress activate(InChannel.Dispatcher disp) throws MTPException { try { return activate(disp,new ProfileImpl(new ExtendedProperties())); } catch(Exception e) { throw new MTPException(e.getMessage()); } } public void activate(InChannel.Dispatcher disp, TransportAddress ta) throws MTPException { try { activate(disp,ta,new ProfileImpl(new ExtendedProperties())); } catch(Exception e) { throw new MTPException(e.getMessage()); } } public TransportAddress activate(InChannel.Dispatcher disp, Profile p) throws MTPException { //Active the new HTTPAddress return activateServer(disp,null,p); } /** * Actual method to activate the HTTP MTP. * * Customizable parameters read from profile:<UL> * <LI><B>port</B>: the port this HTTP server listens to.</LI> * <LI><B>numKeepAlive</B>: Maximum number of keep-alive connections. * Default value is 10. Set to 0 to disable keep-alive coonections (possible performance impact).</LI> * <LI><B>proxyHost</B>: Proxy host name or IP-address. No default value.</LI> * <LI><B>proxyPort</B>: Default value is 1080.</LI> * <LI><B>outPort</B>: Fix port to be used by HTTP client (for firewall configuration). * Default value is freely chosen by Java</LI> * <LI><B>parser</B>: XML SAX2 parser implementation to use. * Default value is JDK 1.4 default parser.</LI> * <LI><B>policy</B>: "conservative" (default value) or "aggressive". * (see documentation for details).</LI> * <LI><B>timeout</B>: Timeout for keep-alive connections. Default value is 1 min. * 0 means infinite.</LI> * </UL> * Note that all these parameters must be prefixed with "jade_mtp_http_". */ public void activate(InChannel.Dispatcher disp, TransportAddress ta, Profile p) throws MTPException { activateServer(disp, ta, p); } private TransportAddress activateServer(InChannel.Dispatcher disp, TransportAddress ta, Profile p) throws MTPException { //Comprobation of correct HTTPAddress int port = -1; boolean changePortIfBusy = false; String saxClass = null; HTTPAddress hta = null; try { if (ta != null) { hta = (HTTPAddress)ta; } else { try { // Create default HTTPAddress String tmp; if ((tmp = p.getParameter(PREFIX+"port",null)) != null) { port = Integer.parseInt(tmp); } else { // Use default port port = IN_PORT; changePortIfBusy = true; } //#DOTNET_EXCLUDE_BEGIN hta = new HTTPAddress(InetAddress.getLocalHost().getCanonicalHostName(),port, false); //#DOTNET_EXCLUDE_END /*#DOTNET_INCLUDE_BEGIN hta = new HTTPAddress(InetAddress.getLocalHost().getHostName(),port, false); #DOTNET_INCLUDE_END*/ } catch( UnknownHostException ukhexc ) { throw new MTPException("Cannot activate MTP on default address: Unknown Host"); } catch( MalformedURLException mexc ) { throw new MTPException("Cannot activate MTP on default address: Malformed URL"); } catch( NumberFormatException nfexc ) { throw new MTPException("Cannot activate MTP on default address: Invalid port"); } } port = hta.getPortNo(); if((port <= 0) || (port > 65535)) { throw new MTPException("Invalid port number "+ta.getPort()); } // Parse other profile parameters numKA = Integer.parseInt(p.getParameter(PREFIX+"numKeepAlive",MAX_KA)); policy = (p.getParameter(PREFIX+"policy",POLICY).equals("aggressive"))?true:false; int outPort = Integer.parseInt(p.getParameter(PREFIX+"outPort",OUT_PORT)); ka = new KeepAlive(numKA, outPort, policy); keepAlive = numKA > 0; proxyHost = p.getParameter(PREFIX+"proxyHost",null); if (proxyHost != null) { useProxy = true; proxyPort = Integer.parseInt(p.getParameter(PREFIX+"proxyPort",PROXY_PORT)); } saxClass = p.getParameter(PREFIX+"parser",null); timeout = Integer.parseInt(p.getParameter(PREFIX+"timeout",TIMEOUT)); //#PJAVA_EXCLUDE_BEGIN try{ HTTPSocketFactory.getInstance().configure(p, hta); } catch(Exception e){ throw new MTPException("Error configuring Socket Factory", e); } //#PJAVA_EXCLUDE_END // System.out.println("Parameters set:"); // System.out.println("- KA "+numKA); // System.out.println("- Policy "+policy); // System.out.println("- Out port "+outPort); // System.out.println("- Proxy host "+proxyHost); // System.out.println("- Proxy port "+proxyPort); // System.out.println("- Parser "+saxClass); // System.out.println("- Timeout "+timeout); } catch (ClassCastException cce) { throw new MTPException("User supplied transport address not supported."); } catch( NumberFormatException nexc ) { throw new MTPException(nexc.getMessage()); } //Creation of the Server try { //Create object server HTTPServer srv = new HTTPServer(port,disp,numKA,saxClass,timeout, changePortIfBusy); int actualPort = srv.getLocalPort(); if (actualPort != port) { // The selected port is busy and a new one was selected --> Update the transport address boolean useHttps = "https".equalsIgnoreCase(hta.getProto()); //#DOTNET_EXCLUDE_BEGIN hta = new HTTPAddress(InetAddress.getLocalHost().getCanonicalHostName(),actualPort, useHttps); //#DOTNET_EXCLUDE_END /*#DOTNET_INCLUDE_BEGIN hta = new HTTPAddress(InetAddress.getLocalHost().getHostName(),actualPort, useHttps); #DOTNET_INCLUDE_END*/ } //Save the reference to HTTPServer addr2srv.put(hta.toString(),srv); //Execute server srv.start(); protocols = new String[]{hta.getProto()}; return hta; } catch( Exception e ) { throw new MTPException("While activating MTP got exception "+e); } } public void deactivate(TransportAddress ta) throws MTPException { // Shutdown HTTP Server HTTPServer srv = (HTTPServer)addr2srv.get(ta.toString()); if( srv != null ) { addr2srv.remove(ta.toString()); srv.desactivate(); //srv.interrupt(); } else throw new MTPException("No server on address "+ta); } public void deactivate() throws MTPException { for(Enumeration it=addr2srv.keys(); it.hasMoreElements() ; ) { TransportAddress ta=(TransportAddress)it.nextElement(); deactivate(ta); } } /******************************** * OutChannel Interface Methods * *********************************/ public void deliver(String addr, Envelope env, byte[] payload) throws MTPException { HTTPAddress url; byte[] request; try { HTTPAddress host = new HTTPAddress(addr); if (useProxy) { url = new HTTPAddress(proxyHost,proxyPort,false); //false=>do_not_use HTTPS with the proxy } else { url = host; } String connPol = (keepAlive)?HTTPIO.KA:HTTPIO.CLOSE; // Prepare the HTTP request // ------------------------ //Calculate de value of boundary StringBuffer boundary = new StringBuffer(); for( int i=0 ; i < 31 ; i++ ) { boundary.append(Integer.toString((int)Math.round(Math.random()*15),16)); } //Request body //String body = HTTPIO.createHTTPBody(env,boundary.toString(),new String(payload)); byte[] boundaryBytes = boundary.toString().getBytes("ISO-8859-1"); byte[] body = HTTPIO.createHTTPBody(env,boundaryBytes,payload); //HTTP header //StringBuffer req = HTTPIO.createHTTPHeader(host,body.length(),connPol,boundary.toString(),useProxy); byte[] header = HTTPIO.createHTTPHeader(host,body.length,connPol,boundaryBytes,useProxy); // Concatenate header + body //req.append(body); ByteArrayOutputStream requestStream = new ByteArrayOutputStream(header.length + body.length); requestStream.write(header); requestStream.write(body); requestStream.flush(); requestStream.close(); request = requestStream.toByteArray(); } catch (Exception e) { throw new MTPException(e.getMessage()); } ka.send(url, request); } } // End of MessageTransportProtocol class