/***************************************************************** 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. *****************************************************************/ /** * HTTPServer.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.*; import jade.mtp.InChannel; import jade.mtp.InChannel.Dispatcher; import jade.mtp.MTPException; import jade.domain.FIPAAgentManagement.Envelope; import jade.util.Logger; //#DOTNET_EXCLUDE_BEGIN import javax.xml.parsers.SAXParserFactory; //#DOTNET_EXCLUDE_END public class HTTPServer extends Thread { // Codec class //static String CODEC = "org.apache.xerces.parsers.SAXParser"; static String CODEC = "org.apache.crimson.parser.XMLReaderImpl"; private int port; private InChannel.Dispatcher dispatcher; private int maxKA; private int timeout; private ServerSocket server; //logging private static Logger logger = Logger.getMyLogger(HTTPServer.class.getName()); private Vector threads; // for keep alive connections //attribute for synchronized private static Object lock = new Object(); // the flag that shows if the server is active or not boolean active = true; /** Constructor: Store the information*/ public HTTPServer(int p, InChannel.Dispatcher d, int m, String s, int t, boolean changePortIfBusy) throws IOException { port = p; dispatcher = d; maxKA = m; threads = new Vector(maxKA); //#DOTNET_EXCLUDE_BEGIN CODEC = getSaxParserName(s); if (CODEC == null) { throw new IOException("NO XML Parser specified"); } //#DOTNET_EXCLUDE_END logger.log(Logger.INFO, "HTTP-MTP Using XML parser "+CODEC); timeout = t; try { //#PJAVA_EXCLUDE_BEGIN server = HTTPSocketFactory.getInstance().createServerSocket(port); //#PJAVA_EXCLUDE_END /*#PJAVA_INCLUDE_BEGIN server = new ServerSocket(port); #PJAVA_INCLUDE_END*/ } catch (IOException ioe) { if (changePortIfBusy) { // The specified port is busy. Let the system find a free one //#PJAVA_EXCLUDE_BEGIN server = HTTPSocketFactory.getInstance().createServerSocket(0); //#PJAVA_EXCLUDE_END /*#PJAVA_INCLUDE_BEGIN server = new ServerSocket(0); #PJAVA_INCLUDE_END*/ if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"Port "+p+" is already in used, selected another one"); } else { throw ioe; } } } /** * Desactivate The HTTPServerThread and all other sub-threads **/ public synchronized void desactivate() { //Stop keep-alive Threads for(int i=0 ; i < threads.size(); i++) { ((ServerThread) threads.elementAt(i)).shutdown(); } // The non-keep-alive will close themselves after a while active = false; try { server.close(); } catch(Exception e) { // Does nothing as we asked to close } } //#DOTNET_EXCLUDE_BEGIN private String getSaxParserName(String s) { if (s != null) { // SAXParser specified by means of the jade_mtp_http_parser JADE option return s; } else { String saxFactory = System.getProperty( "org.xml.sax.driver" ); if( saxFactory != null ) { // SAXParser specified by means of the org.xml.sax.driver Java option return saxFactory; } else { // Use the JVM default SAX Parser try { return SAXParserFactory.newInstance().newSAXParser().getXMLReader().getClass().getName(); } catch (Throwable t) { } } } return null; } //#DOTNET_EXCLUDE_END int getLocalPort() { return server.getLocalPort(); } void addThread(ServerThread st) { synchronized(lock) { threads.addElement(st); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG," Added Ka threads: "+threads.size()+"/"+maxKA); } } void removeThread(ServerThread st) { synchronized(lock) { threads.removeElement(st); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG," Removed Ka threads: "+threads.size()+"/"+maxKA); } } boolean isSpaceLeft() { synchronized(lock) { return (threads.size() < maxKA); } } /** * Entry point for the master server thread */ public void run() { try { while(active) { //Accept the input connections Socket client = server.accept(); client.setSoTimeout(timeout); new ServerThread(this,client,dispatcher).start(); } } catch( Exception e ) { if (active) { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"HTTP Server closed on port "+port); } } } public static class ServerThread extends Thread { private HTTPServer father; private Socket client; private InputStream input; private OutputStream output; private InChannel.Dispatcher dispatcher; private XMLCodec codec; private boolean keepAlive = false; private boolean active = false; /** Constructor: Store client port*/ public ServerThread(HTTPServer f, Socket s, InChannel.Dispatcher d) { father = f; client = s; dispatcher = d; } /** * Entry point for the slave server thread */ public void run () { try { //#DOTNET_EXCLUDE_BEGIN codec = new XMLCodec(HTTPServer.CODEC); //#DOTNET_EXCLUDE_END /*#DOTNET_INCLUDE_BEGIN codec = new XMLCodec(); #DOTNET_INCLUDE_END*/ input = new BufferedInputStream(client.getInputStream()); output = new BufferedOutputStream(client.getOutputStream()); do { //Read the request from client StringBuffer envelope = new StringBuffer(40); ByteArrayOutputStream payload = new ByteArrayOutputStream(40); StringBuffer connection = new StringBuffer(); String responseMsg = HTTPIO.readAll(input,envelope,payload,connection); String type = connection.toString(); if (HTTPIO.OK.equals(responseMsg)) { // Extract the information from request //System.out.println("\n"+envelope.toString()); //System.out.println("--------------------------"); //System.out.println("--------------------------"); //System.out.println(payload.toString()+"\n"); //Execute parser to extract information from the Envelope //System.out.println("Envelope received:\n"+envelope.toString()); //#DOTNET_EXCLUDE_BEGIN StringReader sr = new StringReader(envelope.toString()); //#DOTNET_EXCLUDE_END /*#DOTNET_INCLUDE_BEGIN System.IO.StringReader sr = new System.IO.StringReader( envelope.toString() ); #DOTNET_INCLUDE_END*/ Envelope env = codec.parse(sr); /*#DOTNET_INCLUDE_BEGIN //There are problems if PayloadEncoding is set to US-ASCII if (env.getPayloadEncoding() == null) env.setPayloadEncoding(XMLCodec.CHARS_CODEC); #DOTNET_INCLUDE_END*/ //System.out.println("Envelope received:\n"+env); //Post the Message to Jade platform synchronized (dispatcher) { if(logger.isLoggable(Logger.WARNING)) { // check payload size if ((env.getPayloadLength() != null) && (env.getPayloadLength().intValue() >= 0) && (env.getPayloadLength().intValue() != payload.size())) logger.log(Logger.WARNING,"Payload size does not match envelope information"); } dispatcher.dispatchMessage(env,payload.toByteArray()); } if (HTTPIO.KA.equalsIgnoreCase(type)) { if (! keepAlive) { // This thread is not known yet if (father.isSpaceLeft()) { // There is space left for a new KA active = true; keepAlive = true; father.addThread(this); } else { // This is a to-be-closed thread type = HTTPIO.CLOSE; } } } else { active = false; } } HTTPIO.writeAll(output,HTTPIO.createHTTPResponse(responseMsg,type)); } while(active); } catch(SocketException se) { } catch(IOException ioe) { } catch(Exception e ) { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"HTTPServer error : "+e); } finally { //Close socket connection if (keepAlive) { father.removeThread(this); // shutdown(); } } shutdown(); } void shutdown() { active = false; keepAlive = false; try { client.close(); } catch(Exception e) { // Nothing important can happen here } } } //End of ServerThread class }//End of HTTPServer class