diff --git a/html/relnotes.html b/html/relnotes.html index 16ed1367c96eb412f66ab1c0d660e47db66d5f15..eeef101576b5d24f443961fb52288aa5e359c381 100644 --- a/html/relnotes.html +++ b/html/relnotes.html @@ -79,6 +79,7 @@ with a warning: <li>Fixed a bug in <tt>QueryMediator</tt> which doubled '#' (serv)</li> <li>Fixed a few mistakes in <tt>SilkRendererVisitor</tt> (impl)</li> <li>Implemented <tt>translateMessage()</tt> and <tt>rewriteQuery()</tt> in <tt>BasicAlignment</tt> (impl)</li> +<li>Reorganised AlignmentServices into transport (HTTP) and service (HTML/Web service) (serv)</li> <li>Enabled query translation in interface (serv)</li> <li>Added a command-line utility for query translation (cli)</li> <li>Added a guard forbiding to create a <tt>BasicCell</tt> with null objects (impl)</li> diff --git a/src/fr/inrialpes/exmo/align/service/AServProtocolManager.java b/src/fr/inrialpes/exmo/align/service/AServProtocolManager.java index 75a33ce33908cfc6b3f3314f2f6a3c17cef53cd2..354d5c4ede7f6315c439e2c391f5ebcf5a8c8b8e 100644 --- a/src/fr/inrialpes/exmo/align/service/AServProtocolManager.java +++ b/src/fr/inrialpes/exmo/align/service/AServProtocolManager.java @@ -482,12 +482,10 @@ public class AServProtocolManager implements Service { //logger.trace( "Resulting rendering : {}", result.toString() ); logger.error( "Cannot render alignment", e ); return new Message(newId(),mess,serverId,mess.getSender(),"Failed to render alignment",(Properties)null); - } finally { writer.flush(); writer.close(); } - return new RenderedAlignment(newId(),mess,serverId,mess.getSender(),result.toString(),(Properties)null); } diff --git a/src/fr/inrialpes/exmo/align/service/AlignmentService.java b/src/fr/inrialpes/exmo/align/service/AlignmentService.java index 4d6c641f94c9c8708183b47597370a080dc7f541..93059aa1994dac1ef23199513fd36402e3d17276 100644 --- a/src/fr/inrialpes/exmo/align/service/AlignmentService.java +++ b/src/fr/inrialpes/exmo/align/service/AlignmentService.java @@ -35,6 +35,7 @@ import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.ParseException; import java.util.Hashtable; +import java.util.Vector; import java.util.Enumeration; import java.util.Properties; import java.io.PrintStream; @@ -87,8 +88,9 @@ public class AlignmentService extends CommonCLI { private String filename = null; private String outfile = null; private String paramfile = null; - private Hashtable<String,AlignmentServiceProfile> services = null; + private Vector<AlignmentServiceProfile> services = null; private Hashtable<String,Directory> directories = null; + private HTTPTransport transport = null; private AServProtocolManager manager; private DBService connection; @@ -124,7 +126,7 @@ public class AlignmentService extends CommonCLI { } public void run(String[] args) throws Exception { - services = new Hashtable<String,AlignmentServiceProfile>(); + services = new Vector<AlignmentServiceProfile>(); directories = new Hashtable<String,Directory>(); // Read parameters @@ -173,11 +175,11 @@ public class AlignmentService extends CommonCLI { logger.debug("Manager created"); // Launch services - for ( AlignmentServiceProfile serv : services.values() ) { + for ( AlignmentServiceProfile serv : services ) { try { serv.init( parameters, manager ); } catch ( AServException ex ) { // This should rather be the job of the caller - logger.warn( "Cannot start {} server on {}:{}", serv, parameters.getProperty( "host" ), parameters.getProperty( "http" ) ); + logger.warn( "Cannot start {} service on {}:{}", serv ); } } logger.debug("Services launched"); @@ -196,6 +198,17 @@ public class AlignmentService extends CommonCLI { } logger.debug("Directories registered"); + // Enables transports (here only HTTP) + try { + transport = new HTTPTransport(); + transport.init( parameters, manager, services ); + } catch ( AServException ex ) { + logger.error( "Cannot start HTTP transport on {}:{}", parameters.getProperty( "host" ), parameters.getProperty( "http" ) ); + usage(); + System.exit(-1); + } + logger.debug("Transport enabled"); + // Wait loop logger.info("Alignment server running"); while ( true ) { @@ -206,6 +219,8 @@ public class AlignmentService extends CommonCLI { protected void close(){ logger.debug("Shuting down server"); + // Disable transport + if ( transport != null ) transport.close(); // [Directory]: unregister to directories for ( Directory dir : directories.values() ) { try { dir.close(); } @@ -215,7 +230,7 @@ public class AlignmentService extends CommonCLI { } } // Close services - for ( AlignmentServiceProfile serv : services.values() ) { + for ( AlignmentServiceProfile serv : services ) { try { serv.close(); } catch ( AServException ex ) { logger.debug("Cannot close {}", serv); @@ -258,7 +273,7 @@ public class AlignmentService extends CommonCLI { if ( line.hasOption( 'i' ) ) { /* external service */ String arg = line.getOptionValue( 'i' ); try { - services.put( arg, (AlignmentServiceProfile)loadInstance( arg ) ); + services.add( (AlignmentServiceProfile)loadInstance( arg ) ); } catch (Exception ex) { logger.warn( "Cannot create service for {}", arg ); logger.debug( "IGNORED Exception", ex ); @@ -269,7 +284,7 @@ public class AlignmentService extends CommonCLI { parameters.setProperty( "http", line.getOptionValue( 'H', HTML ) ); // This shows that it does not work try { - services.put( "fr.inrialpes.exmo.align.service.HTMLAServProfile", (AlignmentServiceProfile)loadInstance( "fr.inrialpes.exmo.align.service.HTMLAServProfile" ) ); + services.add( (AlignmentServiceProfile)loadInstance( "fr.inrialpes.exmo.align.service.HTMLAServProfile" ) ); } catch (Exception ex) { logger.warn( "Cannot create service for HTMLAServProfile", ex ); } @@ -277,19 +292,19 @@ public class AlignmentService extends CommonCLI { if ( line.hasOption( 'A' ) ) { parameters.setProperty( "jade", line.getOptionValue( 'A', JADE ) ); try { - services.put( "fr.inrialpes.exmo.align.service.jade.JadeFIPAAServProfile", (AlignmentServiceProfile)loadInstance( "fr.inrialpes.exmo.align.service.jade.JadeFIPAAServProfile" ) ); + services.add( (AlignmentServiceProfile)loadInstance( "fr.inrialpes.exmo.align.service.jade.JadeFIPAAServProfile" ) ); } catch ( Exception ex ) { logger.warn("Cannot create service for JadeFIPAAServProfile", ex); } } if ( line.hasOption( 'W' ) ) { parameters.setProperty( "wsdl", line.getOptionValue( 'W', WSDL ) ); - // The WSDL extension requires HTTP server (and the same one). + // The Web service extension requires HTTP server (and the same one). // Put the default port, may be overriden if ( parameters.getProperty( "http" ) == null ) parameters.setProperty( "http", HTML ); try { - services.put( "fr.inrialpes.exmo.align.service.HTMLAServProfile", (AlignmentServiceProfile)loadInstance( "fr.inrialpes.exmo.align.service.HTMLAServProfile" ) ); + services.add( (AlignmentServiceProfile)loadInstance( "fr.inrialpes.exmo.align.service.WSAServProfile" ) ); } catch ( Exception ex ) { logger.warn( "Cannot create service for Web services", ex ); } diff --git a/src/fr/inrialpes/exmo/align/service/AlignmentServiceProfile.java b/src/fr/inrialpes/exmo/align/service/AlignmentServiceProfile.java index bb67a374166c5389ba281d26426dee3ba32391fc..14cff0ebf11f66078cd60066912c408200c381cd 100644 --- a/src/fr/inrialpes/exmo/align/service/AlignmentServiceProfile.java +++ b/src/fr/inrialpes/exmo/align/service/AlignmentServiceProfile.java @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (C) INRIA Rhône-Alpes, 2006 + * Copyright (C) INRIA Rhône-Alpes, 2006, 2014 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -30,6 +30,16 @@ public interface AlignmentServiceProfile { */ public void init( Properties p, AServProtocolManager m ) throws AServException; + /** + * Tells if it accept requests with this prefix + */ + public boolean accept( String prefix ); + + /** + * Process a particular request + */ + public String process( String uri, String prefix, String perf, Properties header, Properties params ); + /** * Shutdown the Service and undeclare it from any registery */ diff --git a/src/fr/inrialpes/exmo/align/service/HTMLAServProfile.java b/src/fr/inrialpes/exmo/align/service/HTMLAServProfile.java index a8f1bccf65e7b032e2b7a64ffff5166cbaa58f28..a2881f74a048c8d8341d4f43028e9590be49bd2d 100644 --- a/src/fr/inrialpes/exmo/align/service/HTMLAServProfile.java +++ b/src/fr/inrialpes/exmo/align/service/HTMLAServProfile.java @@ -55,349 +55,56 @@ import java.util.Map; import java.net.URI; import java.net.URISyntaxException; -import java.net.Socket; -import java.net.ServerSocket; -import java.net.URLEncoder; -import java.net.URLDecoder; import java.lang.Integer; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.ServletResponseWrapper; -import javax.servlet.ServletRequestWrapper; -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.mortbay.jetty.Handler; -import org.mortbay.jetty.handler.AbstractHandler; -import org.mortbay.jetty.Server; -import org.mortbay.jetty.Request; -import org.mortbay.servlet.MultiPartFilter; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * HTMLAServProfile: an HTML provile for the Alignment server - * It embeds an HTTP server. */ public class HTMLAServProfile implements AlignmentServiceProfile { final static Logger logger = LoggerFactory.getLogger( HTMLAServProfile.class ); - private int tcpPort; - private String tcpHost; - private Server server; private AServProtocolManager manager; - private WSAServProfile wsmanager; private String myId; private String serverId; private int localId = 0; - /** - * Some HTTP response status codes - */ - public static final String - HTTP_OK = "200 OK", - HTTP_REDIRECT = "301 Moved Permanently", - HTTP_FORBIDDEN = "403 Forbidden", - HTTP_NOTFOUND = "404 Not Found", - HTTP_BADREQUEST = "400 Bad Request", - HTTP_INTERNALERROR = "500 Internal Server Error", - HTTP_NOTIMPLEMENTED = "501 Not Implemented"; - - /** - * Common mime types for dynamic content - */ - public static final String - MIME_PLAINTEXT = "text/plain", - MIME_HTML = "text/html", - MIME_XML = "text/xml", - MIME_JSON = "application/json", - MIME_RDFXML = "application/rdf+xml", - MIME_DEFAULT_BINARY = "application/octet-stream"; - - private String returnType = MIME_HTML; + private int newId() { return localId++; } public static final int MAX_FILE_SIZE = 10000; public static final String HEADER = "<style type=\"text/css\">body { font-family: sans-serif } button {background-color: #DDEEFF; margin-left: 1%; border: #CCC 1px solid;}</style>"; - // ================================================== - // Socket & server code - // ================================================== - /** - * Starts a HTTP server to given port.<p> - * Throws an IOException if the socket is already in use - */ public void init( Properties params, AServProtocolManager manager ) throws AServException { this.manager = manager; - tcpPort = Integer.parseInt( params.getProperty( "http" ) ); - tcpHost = params.getProperty( "host" ) ; - - /* - try { - final ServerSocket ss = new ServerSocket( tcpPort ); - Thread t = new Thread( new Runnable() { - public void run() { - try { while( true ) new HTTPSession( ss.accept()); - } catch ( IOException ioe ) { logger.debug( "IGNORED Exception", ioe ); } - } - }); - t.setDaemon( true ); - t.start(); - } catch (Exception e) { - throw new AServException ( "Cannot launch HTTP Server" , e ); - } - */ - - // ******************************************************************** - // JE: Jetty implementation - server = new Server(tcpPort); - - // The handler deals with the request - // most of its work is to deal with large content sent in specific ways - Handler handler = new AbstractHandler(){ - public void handle( String target, HttpServletRequest request, HttpServletResponse response, int dispatch ) throws IOException, ServletException { - String method = request.getMethod(); - //uri = URLDecoder.decode( request.getURI(), "iso-8859-1" ); - // Should be decoded? - String uri = request.getPathInfo(); - Properties params = new Properties(); - try { decodeParams( request.getQueryString(), params ); } - catch ( Exception e) {}; - // I do not decode them here because it is useless - // See below how it is done. - Properties header = new Properties(); - Enumeration headerNames = request.getHeaderNames(); - while( headerNames.hasMoreElements() ) { - String headerName = (String)headerNames.nextElement(); - header.setProperty( headerName, request.getHeader(headerName) ); - } - - // Get the content if any - // This is supposed to be only an uploaded file - // We use jetty MultiPartFilter to decode this file. - // Note that this could be made more uniform - // with the text/xml part stored in a file as well. - String mimetype = request.getContentType(); - // Multi part: the content provided by an upload HTML form - if ( mimetype != null && mimetype.startsWith("multipart/form-data") ) { - MultiPartFilter filter = new MultiPartFilter(); - // This is in fact useless - ParameterServletResponseWrapper dummyResponse = - new ParameterServletResponseWrapper( response ); - // In theory, the filter must be inited with a FilterConfig - // filter.init( new FilterConfig); - // This filter config must have a javax.servlet.context.tempdir attribute - // and a ServletConxtext with parameter "deleteFiles" - // Apparently the Jetty implementation uses System defaults - // if no FilterConfig - // e.g., it uses /tmp and keeps the files - filter.doFilter( request, dummyResponse, new Chain() ); - // Apparently a bug from Jetty prevents from retrieving this - if ( request.getParameter("pretty") != null ) - params.setProperty( "pretty", request.getParameter("pretty").toString() ); - if ( request.getAttribute("content") != null ) - params.setProperty( "filename", request.getAttribute("content").toString() ); - filter.destroy(); - } else if ( mimetype != null && mimetype.startsWith("text/xml") ) { - // Most likely Web service request (REST through POST) - int length = request.getContentLength(); - if ( length > 0 ) { - char [] mess = new char[length+1]; - try { - new BufferedReader(new InputStreamReader(request.getInputStream())).read( mess, 0, length); - } catch ( Exception e ) { - logger.debug( "IGNORED Exception", e ); - } - params.setProperty( "content", new String( mess ) ); - } - // File attached to SOAP messages - } else if ( mimetype != null && mimetype.startsWith("application/octet-stream") ) { - File alignFile = new File(File.separator + "tmp" + File.separator + newId() +"XXX.rdf"); - // check if file already exists - and overwrite if necessary. - if (alignFile.exists()) alignFile.delete(); - FileOutputStream fos = new FileOutputStream(alignFile); - InputStream is = request.getInputStream(); - - try { - byte[] buffer = new byte[4096]; - int bytes=0; - while (true) { - bytes = is.read(buffer); - if (bytes < 0) break; - fos.write(buffer, 0, bytes); - } - } catch (Exception e) { - } finally { - fos.flush(); - fos.close(); - } - is.close(); - params.setProperty( "content", "" ); - params.setProperty( "filename" , alignFile.getAbsolutePath() ); - } - - // Get the answer (HTTP) - Response r = serve( uri, method, header, params ); - - // Return it - response.setContentType( r.getContentType() ); - response.setStatus( HttpServletResponse.SC_OK ); - response.getWriter().println( r.getData() ); - ((Request)request).setHandled( true ); - } - }; - server.setHandler(handler); + } - // Common part - try { server.start(); } - catch (Exception e) { - throw new AServException( "Cannot launch HTTP Server" , e ); - } - //server.join(); + public boolean accept( String prefix ) { + if ( prefix.equals("admin") || prefix.equals("html") ) return true; + else return false; + } - // ******************************************************************** - if ( params.getProperty( "wsdl" ) != null ){ - wsmanager = new WSAServProfile(); - if ( wsmanager != null ) wsmanager.init( params, manager ); - } - myId = "LocalHTMLInterface"; - serverId = manager.serverURL(); - logger.info( "Launched on {}/html/", serverId ); - localId = 0; + public String process( String uri, String prefix, String perf, Properties header, Properties params ) { + if ( prefix.equals("html") ) { + return htmlAnswer( uri, perf, header, params ); + } else if ( prefix.equals("admin") ) { + return adminAnswer( uri, perf, header, params ); + } else return about(); } public void close(){ - if ( wsmanager != null ) wsmanager.close(); - if ( server != null ) { - try { server.stop(); } - catch (Exception e) { logger.debug( "IGNORED Exception on close", e ); } - } } // ================================================== // API parts // ================================================== - /** - * Override this to customize the server.<p> - * - * (By default, this delegates to serveFile() and allows directory listing.) - * - * @param uri Percent-decoded URI without parameters, for example "/index.cgi" - * @param method "GET", "POST" etc. - * @param parms Parsed, percent decoded parameters from URI and, in case of POST, data. - * @param header Header entries, percent decoded - * @return HTTP response, see class Response for details - */ - public Response serve( String uri, String method, Properties header, Properties parms ) { - logger.debug( "{} '{}'", method, uri ); - - // Convert parms to parameters - Properties params = new Properties(); - for ( String key : parms.stringPropertyNames() ) { - //logger.trace( " PRM: '{}' = '{}'", key, parms.getProperty( key ) ); - if ( key.startsWith( "paramn" ) ){ - params.setProperty( parms.getProperty( key ), - parms.getProperty( "paramv"+key.substring( 6 ) ) ); - } else if ( !key.startsWith( "paramv" ) ) { - params.setProperty( key, parms.getProperty( key ) ); - } - } - - int start = 0; - while ( start < uri.length() && uri.charAt(start) == '/' ) start++; - int end = uri.indexOf( '/', start+1 ); - String oper = ""; - if ( end != -1 ) { - oper = uri.substring( start, end ); - start = end+1; - } else { - oper = uri.substring( start ); - start = uri.length(); - } - - // Content negotiation first - String accept = header.getProperty( "Accept" ); - returnType = MIME_HTML; - if ( accept == null ) accept = header.getProperty( "accept" ); - //logger.trace( "Accept header: {}", accept ); - if ( accept != null && !accept.equals("") ) { - int indexRXML = accept.indexOf( MIME_RDFXML ); - if ( indexRXML == -1 ) indexRXML = accept.indexOf( MIME_XML ); - int indexJSON = accept.indexOf( MIME_JSON ); - if ( indexRXML != -1 ) { - if ( indexJSON > indexRXML || indexJSON == -1 ) { - returnType = MIME_RDFXML; - } else { - returnType = MIME_JSON; - } - } else if ( indexJSON != -1 ) { - returnType = MIME_JSON; - } - } - //logger.trace( "Return MIME Type: {}", returnType ); - - // Serve this content - if ( oper.equals( "aserv" ) ){ // Classical web service SOAP/HTTP - if ( wsmanager != null ) { - return new Response( HTTP_OK, MIME_HTML, wsmanager.protocolAnswer( uri, uri.substring(start), header, params ) ); - } else { - // This is not correct: I shoud return an error - // Especially in WSDL, but we are not supposed to be a Web service server at that point - return new Response( HTTP_OK, MIME_HTML, "<html><head>"+HEADER+"</head><body>"+about()+"</body></html>" ); - } - } else if ( oper.equals( "admin" ) ){ // HTML/HTTP administration - return adminAnswer( uri, uri.substring(start), header, params ); - } else if ( oper.equals( "alid" ) ){ // Asks for an alignment by URI - if ( returnType == MIME_JSON ) { // YES string compared by ==. - return returnAlignment( uri, MIME_JSON, "fr.inrialpes.exmo.align.impl.renderer.JSONRendererVisitor" ); - } else if ( returnType == MIME_RDFXML ) { - return returnAlignment( uri, MIME_RDFXML, "fr.inrialpes.exmo.align.impl.renderer.RDFRendererVisitor" ); - } else { - return returnAlignment( uri, MIME_HTML, "fr.inrialpes.exmo.align.impl.renderer.HTMLRendererVisitor" ); - } - } else if ( oper.equals( "html" ) ){ // HTML/HTTP interface - return htmlAnswer( uri, uri.substring(start), header, params ); - } else if ( oper.equals( "rest" ) ){ // REST/HTTP - params.setProperty( "restful", "true" ); - params.setProperty( "returnType", returnType ); - if ( wsmanager != null ) { - if ( returnType == MIME_RDFXML ) { - params.setProperty( "renderer", "XML" ); - return new Response( HTTP_OK, MIME_XML, wsmanager.protocolAnswer( uri, uri.substring(start), header, params ) ); - } else if ( returnType == MIME_JSON ) { - params.setProperty( "renderer", "JSON" ); - return new Response( HTTP_OK, MIME_JSON, wsmanager.protocolAnswer( uri, uri.substring(start), header, params ) ); - } else { // HTML still default! - params.setProperty( "renderer", "HTML" ); - return htmlAnswer( uri, uri.substring(start), header, params ); - } - } else { - //Message err = new ErrorMsg(int surr, Message rep, String from, String to, String cont, params ); - if ( returnType == MIME_JSON ) { - return new Response( HTTP_OK, MIME_JSON, "{ \"type\" : \"SystemErrorMsg\",\n \"content\" : \"No service launched\"\n}" ); - } else if ( returnType == MIME_RDFXML ) { - return new Response( HTTP_OK, MIME_RDFXML, "<SystemErrorMsg>No service launched</SystemErrorMsg>" ); - } else { - return new Response( HTTP_OK, MIME_HTML, "<html><head>"+HEADER+"</head><body>"+"<ErrMsg>No service launched</ErrMsg>"+"<hr /><center><small><a href=\".\">Alignment server</a></small></center></body></html>" ); - } - } - } else if ( oper.equals( "wsdl" ) ){ - return wsdlAnswer(uri, uri.substring(start), header, params); - } else { - //return serveFile( uri, header, new File("."), true ); - return new Response( HTTP_OK, MIME_HTML, "<html><head>"+HEADER+"</head><body>"+about()+"</body></html>" ); - } - } - - protected String about() { + protected static String about() { return "<h1>Alignment server</h1><center>"+AlignmentService.class.getPackage().getImplementationTitle()+" "+AlignmentService.class.getPackage().getImplementationVersion()+"<br />" + "<center><a href=\"html/\">Access</a></center>" + "(C) INRIA, 2006-2014<br />" @@ -409,7 +116,7 @@ public class HTMLAServProfile implements AlignmentServiceProfile { * HTTP administration interface * Allows some limited administration of the server through HTTP */ - public Response adminAnswer( String uri, String perf, Properties header, Properties params ) { + public String adminAnswer( String uri, String perf, Properties header, Properties params ) { //logger.trace( "ADMIN[{}]", perf); String msg = ""; if ( perf.equals("listmethods") ){ @@ -436,11 +143,7 @@ public class HTMLAServProfile implements AlignmentServiceProfile { // JE: This is unused because the menu below directly refers to /wsdl // This does not really work because the wsdl is hidden in the HTML } else if ( perf.equals("wsdl") ){ - if ( wsmanager != null ){ - msg = "<pre>"+WSAServProfile.wsdlAnswer( false )+"</pre>"; - } else { - msg = "Error: the server does not have Web service capabilities (use -W switch)"; - } + msg = "<pre>"+WSAServProfile.wsdlAnswer( false )+"</pre>"; } else if ( perf.equals("argline") ){ msg = "<h1>Command line arguments</h1>\n<pre>\n"+manager.argline()+"\n<pre>\n"; } else if ( perf.equals("prmsqlquery") ){ @@ -468,8 +171,7 @@ public class HTMLAServProfile implements AlignmentServiceProfile { } else if ( perf.equals("") ) { msg = "<h1>Alignment server administration</h1>"; msg += "<form action=\"listmethods\"><button title=\"List embedded plug-ins\" type=\"submit\">Embedded classes</button></form>"; - if ( wsmanager != null ) - msg += "<form action=\"/wsdl\"><button title=\"WSDL Description\" type=\"submit\">WSDL Description</button></form>"; + msg += "<form action=\"/wsdl\"><button title=\"WSDL Description\" type=\"submit\">WSDL Description</button></form>"; msg += "<form action=\"prmsqlquery\"><button title=\"Query the SQL database (unavailable)\" type=\"submit\">SQL Query</button></form>"; msg += "<form action=\"prmflush\"><button title=\"Free memory by unloading correspondences\" type=\"submit\">Flush caches</button></form>"; msg += "<form action=\"prmreset\"><button title=\"Restore launching state (reload from database)\" type=\"submit\">Reset server</button></form>"; @@ -479,30 +181,14 @@ public class HTMLAServProfile implements AlignmentServiceProfile { } else { msg = "Cannot understand: "+perf; } - return new Response( HTTP_OK, MIME_HTML, "<html><head>"+HEADER+"</head><body>"+msg+"<hr /><center><small><a href=\".\">Alignment server administration</a></small></center></body></html>" ); - } - - /** - * Returns the alignment in negociated format - */ - public Response returnAlignment( String uri, String mime, String method ) { - Properties params = new Properties(); - params.setProperty( "id", manager.serverURL()+uri ); - params.setProperty( "method", method ); - logger.trace( "Bloody URI : {}", manager.serverURL()+uri); - Message answer = manager.render( new Message(newId(),(Message)null,myId,serverId,"", params) ); - if ( answer instanceof ErrorMsg ) { - return new Response( HTTP_NOTFOUND, MIME_PLAINTEXT, "Alignment server: unknown alignment : "+answer.getContent() ); - } else { - return new Response( HTTP_OK, mime, answer.getContent() ); - } + return "<html><head>"+HEADER+"</head><body>"+msg+"<hr /><center><small><a href=\".\">Alignment server administration</a></small></center></body></html>"; } /** * User friendly HTTP interface * uses the protocol but offers user-targeted interaction */ - public Response htmlAnswer( String uri, String perf, Properties header, Properties params ) { + public String htmlAnswer( String uri, String perf, Properties header, Properties params ) { //logger.trace("HTML[{}]", perf ); // REST get String msg = ""; @@ -747,7 +433,7 @@ public class HTMLAServProfile implements AlignmentServiceProfile { } else { // Depending on the type we should change the MIME type // This should be returned in answer.getParameters() - return new Response( HTTP_OK, MIME_HTML, answer.getContent() ); + return answer.getContent(); } // Metadata not done yet } else if ( perf.equals("prmmetadata") ) { @@ -771,7 +457,7 @@ public class HTMLAServProfile implements AlignmentServiceProfile { msg = testErrorMessages( answer, params ); } else { // Depending on the type we should change the MIME type - return new Response( HTTP_OK, MIME_HTML, answer.getContent() ); + return answer.getContent(); } // render // Alignment in HTML can be rendre or metadata+tuples @@ -905,29 +591,30 @@ public class HTMLAServProfile implements AlignmentServiceProfile { } else { msg = "Cannot understand command "+perf; } - return new Response( HTTP_OK, MIME_HTML, "<html><head>"+HEADER+"</head><body>"+msg+"<hr /><center><small><a href=\".\">Alignment server</a></small></center></body></html>" ); + return "<html><head>"+HEADER+"</head><body>"+msg+"<hr /><center><small><a href=\".\">Alignment server</a></small></center></body></html>"; } // =============================================== // Util - public Response wsdlAnswer(String uri, String perf, Properties header, Properties params ) { - return new Response( HTTP_OK, MIME_XML, WSAServProfile.wsdlAnswer( false ) ); - } - private String testErrorMessages( Message answer, Properties param ) { - if ( returnType == MIME_RDFXML ) { + /* + if ( returnType == HTTPResponse.MIME_RDFXML ) { return answer.RESTString(); - } else if ( returnType == MIME_JSON ) { + } else if ( returnType == HTTPResponse.MIME_JSON ) { return answer.JSONString(); - } else { + } else {*/ return "<h1>Alignment error</h1>"+answer.HTMLString(); - } + /*}*/ } private String displayAnswer( Message answer, Properties param ) { + return displayAnswer( answer, param, null ); + } + + private String displayAnswer( Message answer, Properties param, String returnType ) { String result = null; - if ( returnType == MIME_RDFXML ) { + if ( returnType == HTTPResponse.MIME_RDFXML ) { if( param.getProperty("return").equals("HTML") ) { // RESTFUL but in HTML ?? result = answer.HTMLRESTString(); if ( answer instanceof AlignmentId && ( answer.getParameters() == null || answer.getParameters().getProperty("async") == null ) ) { @@ -941,7 +628,7 @@ public class HTMLAServProfile implements AlignmentServiceProfile { } else { result = answer.RESTString(); } - } else if ( returnType == MIME_JSON ) { + } else if ( returnType == HTTPResponse.MIME_JSON ) { result = answer.JSONString(); } else { result = answer.HTMLString(); @@ -969,121 +656,5 @@ public class HTMLAServProfile implements AlignmentServiceProfile { return result; } - private int newId() { return localId++; } - - private void decodeParams( String params, Properties p ) throws InterruptedException { - if ( params == null ) return; - - for ( String next : params.split("&") ) { - int sep = next.indexOf( '=' ); - if ( sep >= 0 ){ - try { - p.put( URLDecoder.decode( next.substring( 0, sep ), "iso-8859-1" ).trim(), - // JE: URLDecoder allows for : and / but not # - URLDecoder.decode( next.substring( sep+1 ), "iso-8859-1" )); - } catch (Exception e) {}; //never thrown - } - } - } - - // ================================================== - // HTTP Machinery - - /** - * HTTP response. - * Return one of these from serve(). - */ - public class Response { - /** - * Default constructor: response = HTTP_OK, data = mime = 'null' - */ - public Response() { - this.status = HTTP_OK; - } - - /** - * Basic constructor. - */ - public Response( String status, String mimeType, InputStream data ) { - this.status = status; - this.mimeType = mimeType; - this.data = data; - } - - /** - * Convenience method that makes an InputStream out of - * given text. - */ - public Response( String status, String mimeType, String txt ) { - this.status = status; - this.mimeType = mimeType; - this.data = new ByteArrayInputStream( txt.getBytes()); - // JE: Added - this.msg = txt; - } - - /** - * Adds given line to the header. - */ - public void addHeader( String name, String value ) { - header.put( name, value ); - } - - - /** - * HTTP status code after processing, e.g. "200 OK", HTTP_OK - */ - public String status; - - /** - * MIME type of content, e.g. "text/html" - */ - public String mimeType; - - /** - * Data of the response, may be null. - */ - public InputStream data; - - /** - * Headers for the HTTP response. Use addHeader() - * to add lines. - */ - public Properties header = new Properties(); - // JE: Added for testing Jetty - public String msg; - public String getStatus() { return status; }; - public String getContentType() { return mimeType; } - public String getData() { return msg; } - - } - - /** - * Two private cclasses for retrieving parameters - */ - private class ParameterServletResponseWrapper extends ServletResponseWrapper { - private Map parameters; - - public ParameterServletResponseWrapper( ServletResponse r ){ - super(r); - }; - - public Map getParameterMap(){ return parameters; } - - public void setParameterMap( Map m ){ parameters = m; } - - } - - private class Chain implements FilterChain { - - public void doFilter( ServletRequest request, ServletResponse response) - throws IOException, ServletException { - if ( response instanceof ParameterServletResponseWrapper && - request instanceof ServletRequestWrapper ) { - ((ParameterServletResponseWrapper)response).setParameterMap( ((ServletRequestWrapper)request).getParameterMap() ); - } - } - - } } diff --git a/src/fr/inrialpes/exmo/align/service/HTTPResponse.java b/src/fr/inrialpes/exmo/align/service/HTTPResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..dee4374b50468cf2e08cd18109b9a857079801d7 --- /dev/null +++ b/src/fr/inrialpes/exmo/align/service/HTTPResponse.java @@ -0,0 +1,118 @@ +/* + * $Id$ + * + * Copyright (C) INRIA, 2014 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package fr.inrialpes.exmo.align.service; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Properties; + +/** + * HTTP response. + * Return one of these from serve(). + */ +public class HTTPResponse { + + /** + * Some HTTP response status codes + */ + public static final String + HTTP_OK = "200 OK", + HTTP_REDIRECT = "301 Moved Permanently", + HTTP_FORBIDDEN = "403 Forbidden", + HTTP_NOTFOUND = "404 Not Found", + HTTP_BADREQUEST = "400 Bad Request", + HTTP_INTERNALERROR = "500 Internal Server Error", + HTTP_NOTIMPLEMENTED = "501 Not Implemented"; + + /** + * Common mime types for dynamic content + */ + public static final String + MIME_PLAINTEXT = "text/plain", + MIME_HTML = "text/html", + MIME_XML = "text/xml", + MIME_JSON = "application/json", + MIME_RDFXML = "application/rdf+xml", + MIME_DEFAULT_BINARY = "application/octet-stream"; + + /** + * Default constructor: response = HTTP_OK, data = mime = 'null' + */ + public HTTPResponse() { + this.status = HTTP_OK; + } + + /** + * Basic constructor. + */ + public HTTPResponse( String status, String mimeType, InputStream data ) { + this.status = status; + this.mimeType = mimeType; + this.data = data; + } + + /** + * Convenience method that makes an InputStream out of + * given text. + */ + public HTTPResponse( String status, String mimeType, String txt ) { + this.status = status; + this.mimeType = mimeType; + this.data = new ByteArrayInputStream( txt.getBytes()); + // JE: Added + this.msg = txt; + } + + /** + * Adds given line to the header. + */ + public void addHeader( String name, String value ) { + header.put( name, value ); + } + + /** + * HTTP status code after processing, e.g. "200 OK", HTTP_OK + */ + public String status; + + /** + * MIME type of content, e.g. "text/html" + */ + public String mimeType; + + /** + * Data of the response, may be null. + */ + public InputStream data; + + /** + * Headers for the HTTP response. Use addHeader() + * to add lines. + */ + public Properties header = new Properties(); + // JE: Added for testing Jetty + public String msg; + public String getStatus() { return status; }; + public String getContentType() { return mimeType; } + public String getData() { return msg; } + +} + diff --git a/src/fr/inrialpes/exmo/align/service/HTTPTransport.java b/src/fr/inrialpes/exmo/align/service/HTTPTransport.java new file mode 100644 index 0000000000000000000000000000000000000000..caceb554ea5aaede7b4205e9b50e092ef017fa5c --- /dev/null +++ b/src/fr/inrialpes/exmo/align/service/HTTPTransport.java @@ -0,0 +1,416 @@ +/* + * $Id$ + * + * Copyright (C) INRIA, 2006-2014 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package fr.inrialpes.exmo.align.service; + +import fr.inrialpes.exmo.align.service.msg.Message; +import fr.inrialpes.exmo.align.service.msg.ErrorMsg; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.FileInputStream; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.BufferedInputStream; +import java.io.FileOutputStream; + +import java.util.Locale; +import java.util.TimeZone; +import java.util.Hashtable; +import java.util.Vector; +import java.util.Properties; +import java.util.Collection; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.Socket; +import java.net.ServerSocket; +import java.net.URLEncoder; +import java.net.URLDecoder; + +import java.lang.Integer; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.ServletResponseWrapper; +import javax.servlet.ServletRequestWrapper; +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.mortbay.jetty.Handler; +import org.mortbay.jetty.handler.AbstractHandler; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.Request; +import org.mortbay.servlet.MultiPartFilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * HTTPTransport: implements the HTTP connection of the server + * Dispatch messages to the various services + */ + +public class HTTPTransport { + final static Logger logger = LoggerFactory.getLogger( HTTPTransport.class ); + + private int tcpPort; + private String tcpHost; + private Server server; + private AServProtocolManager manager; + private Vector<AlignmentServiceProfile> services; + + private String myId; + private String serverId; + private int localId = 0; + + private String returnType = HTTPResponse.MIME_HTML; + + // ================================================== + // Socket & server code + // ================================================== + + /** + * Starts a HTTP server to given port.<p> + * Throws an exception if the socket is already in use + */ + public void init( Properties params, AServProtocolManager manager, Vector<AlignmentServiceProfile> serv ) throws AServException { + this.manager = manager; + services = serv; + tcpPort = Integer.parseInt( params.getProperty( "http" ) ); + tcpHost = params.getProperty( "host" ) ; + + /* + try { + final ServerSocket ss = new ServerSocket( tcpPort ); + Thread t = new Thread( new Runnable() { + public void run() { + try { while( true ) new HTTPSession( ss.accept()); + } catch ( IOException ioe ) { logger.debug( "IGNORED Exception", ioe ); } + } + }); + t.setDaemon( true ); + t.start(); + } catch (Exception e) { + throw new AServException ( "Cannot launch HTTP Server" , e ); + } + */ + + // ******************************************************************** + // JE: Jetty implementation + server = new Server(tcpPort); + + // The handler deals with the request + // most of its work is to deal with large content sent in specific ways + Handler handler = new AbstractHandler(){ + public void handle( String target, HttpServletRequest request, HttpServletResponse response, int dispatch ) throws IOException, ServletException { + String method = request.getMethod(); + //uri = URLDecoder.decode( request.getURI(), "iso-8859-1" ); + // Should be decoded? + String uri = request.getPathInfo(); + Properties params = new Properties(); + try { decodeParams( request.getQueryString(), params ); } + catch ( Exception e) {}; + // I do not decode them here because it is useless + // See below how it is done. + Properties header = new Properties(); + Enumeration headerNames = request.getHeaderNames(); + while( headerNames.hasMoreElements() ) { + String headerName = (String)headerNames.nextElement(); + header.setProperty( headerName, request.getHeader(headerName) ); + } + + // Get the content if any + // This is supposed to be only an uploaded file + // We use jetty MultiPartFilter to decode this file. + // Note that this could be made more uniform + // with the text/xml part stored in a file as well. + String mimetype = request.getContentType(); + // Multi part: the content provided by an upload HTML form + if ( mimetype != null && mimetype.startsWith("multipart/form-data") ) { + MultiPartFilter filter = new MultiPartFilter(); + // This is in fact useless + ParameterServletResponseWrapper dummyResponse = + new ParameterServletResponseWrapper( response ); + // In theory, the filter must be inited with a FilterConfig + // filter.init( new FilterConfig); + // This filter config must have a javax.servlet.context.tempdir attribute + // and a ServletConxtext with parameter "deleteFiles" + // Apparently the Jetty implementation uses System defaults + // if no FilterConfig + // e.g., it uses /tmp and keeps the files + filter.doFilter( request, dummyResponse, new Chain() ); + // Apparently a bug from Jetty prevents from retrieving this + if ( request.getParameter("pretty") != null ) + params.setProperty( "pretty", request.getParameter("pretty").toString() ); + if ( request.getAttribute("content") != null ) + params.setProperty( "filename", request.getAttribute("content").toString() ); + filter.destroy(); + } else if ( mimetype != null && mimetype.startsWith("text/xml") ) { + // Most likely Web service request (REST through POST) + int length = request.getContentLength(); + if ( length > 0 ) { + char [] mess = new char[length+1]; + try { + new BufferedReader(new InputStreamReader(request.getInputStream())).read( mess, 0, length); + } catch ( Exception e ) { + logger.debug( "IGNORED Exception", e ); + } + params.setProperty( "content", new String( mess ) ); + } + // File attached to SOAP messages + } else if ( mimetype != null && mimetype.startsWith("application/octet-stream") ) { + File alignFile = new File(File.separator + "tmp" + File.separator + newId() +"XXX.rdf"); + // check if file already exists - and overwrite if necessary. + if (alignFile.exists()) alignFile.delete(); + FileOutputStream fos = new FileOutputStream(alignFile); + InputStream is = request.getInputStream(); + + try { + byte[] buffer = new byte[4096]; + int bytes=0; + while (true) { + bytes = is.read(buffer); + if (bytes < 0) break; + fos.write(buffer, 0, bytes); + } + } catch (Exception e) { + } finally { + fos.flush(); + fos.close(); + } + is.close(); + params.setProperty( "content", "" ); + params.setProperty( "filename" , alignFile.getAbsolutePath() ); + } + + // Get the answer (HTTP) + HTTPResponse r = serve( uri, method, header, params ); + + // Return it + response.setContentType( r.getContentType() ); + response.setStatus( HttpServletResponse.SC_OK ); + response.getWriter().println( r.getData() ); + ((Request)request).setHandled( true ); + } + }; + server.setHandler(handler); + + // Common part + try { server.start(); } + catch (Exception e) { + throw new AServException( "Cannot launch HTTP Server" , e ); + } + //server.join(); + + // ******************************************************************** + //if ( params.getProperty( "wsdl" ) != null ){ + // wsmanager = new WSAServProfile(); + // if ( wsmanager != null ) wsmanager.init( params, manager ); + //} + //if ( params.getProperty( "http" ) != null ){ + // htmanager = new HTMLAServProfile(); + // if ( htmanager != null ) htmanager.init( params, manager ); + //} + myId = "LocalHTMLInterface"; + serverId = manager.serverURL(); + logger.info( "Launched on {}/html/", serverId ); + localId = 0; + } + + public void close(){ + if ( server != null ) { + try { server.stop(); } + catch (Exception e) { logger.debug( "IGNORED Exception on close", e ); } + } + } + + // ================================================== + // API parts + // ================================================== + + /** + * Override this to customize the server.<p> + * + * (By default, this delegates to serveFile() and allows directory listing.) + * + * @param uri Percent-decoded URI without parameters, for example "/index.cgi" + * @param method "GET", "POST" etc. + * @param parms Parsed, percent decoded parameters from URI and, in case of POST, data. + * @param header Header entries, percent decoded + * @return HTTP response, see class Response for details + */ + public HTTPResponse serve( String uri, String method, Properties header, Properties parms ) { + logger.debug( "{} '{}'", method, uri ); + + // Convert parms to parameters + Properties params = new Properties(); + for ( String key : parms.stringPropertyNames() ) { + //logger.trace( " PRM: '{}' = '{}'", key, parms.getProperty( key ) ); + if ( key.startsWith( "paramn" ) ){ + params.setProperty( parms.getProperty( key ), + parms.getProperty( "paramv"+key.substring( 6 ) ) ); + } else if ( !key.startsWith( "paramv" ) ) { + params.setProperty( key, parms.getProperty( key ) ); + } + } + + int start = 0; + while ( start < uri.length() && uri.charAt(start) == '/' ) start++; + int end = uri.indexOf( '/', start+1 ); + String oper = ""; + if ( end != -1 ) { + oper = uri.substring( start, end ); + start = end+1; + } else { + oper = uri.substring( start ); + start = uri.length(); + } + logger.trace( "Oper: {}", oper ); + + // Content negotiation first + String accept = header.getProperty( "Accept" ); + returnType = HTTPResponse.MIME_HTML; + if ( accept == null ) accept = header.getProperty( "accept" ); + logger.trace( "Accept header: {}", accept ); + if ( accept != null && !accept.equals("") ) { + int indexRXML = accept.indexOf( HTTPResponse.MIME_RDFXML ); + if ( indexRXML == -1 ) indexRXML = accept.indexOf( HTTPResponse.MIME_XML ); + int indexJSON = accept.indexOf( HTTPResponse.MIME_JSON ); + if ( indexRXML != -1 ) { + if ( indexJSON > indexRXML || indexJSON == -1 ) { + returnType = HTTPResponse.MIME_RDFXML; + } else { + returnType = HTTPResponse.MIME_JSON; + } + } else if ( indexJSON != -1 ) { + returnType = HTTPResponse.MIME_JSON; + } + } + logger.trace( "Return MIME Type: {}", returnType ); + params.setProperty( "returnType", returnType ); + + if ( oper.equals( "alid" ) ){ // Asks for an alignment by URI + return returnAlignment( uri, returnType ); + } else if ( oper.equals( "" ) ) { + // SHOULD BE ASSIGNED TO CONTENT NEGOCIATION AS WELL... (DEFAULT IN SERVERS) + //return serveFile( uri, header, new File("."), true ); + return new HTTPResponse( HTTPResponse.HTTP_OK, HTTPResponse.MIME_HTML, "<html><head>"+HTMLAServProfile.HEADER+"</head><body>"+HTMLAServProfile.about()+"</body></html>" ); + } else { + // Selects the relevant service for the request + for ( AlignmentServiceProfile serv : services ) { + if ( serv.accept( oper ) ) { + return new HTTPResponse( HTTPResponse.HTTP_OK, returnType, serv.process( uri, oper, uri.substring(start), header, params ) ); + } + } + return noManager( oper ); + } + } + + protected HTTPResponse noManager( String type ) { + if ( returnType == HTTPResponse.MIME_JSON ) { + return new HTTPResponse( HTTPResponse.HTTP_OK, HTTPResponse.MIME_JSON, "{ \"type\" : \"AServErrorMsg\",\n \"content\" : \"No "+type+" service launched\"\n}" ); + } else if ( returnType == HTTPResponse.MIME_RDFXML ) { + return new HTTPResponse( HTTPResponse.HTTP_OK, HTTPResponse.MIME_RDFXML, "<AServErrorMsg>No "+type+" service launched</AServErrorMsg>" ); + } else { + return new HTTPResponse( HTTPResponse.HTTP_OK, HTTPResponse.MIME_HTML, "<html><head>"+HTMLAServProfile.HEADER+"</head><body>"+"<ErrMsg>No "+type+" service launched</ErrMsg>"+"<hr /><center><small><a href=\".\">Alignment server</a></small></center></body></html>" ); + } + } + + /** + * Returns the alignment in negociated format + */ + public HTTPResponse returnAlignment( String uri, String mimeType ) { + Properties params = new Properties(); + params.setProperty( "id", manager.serverURL()+uri ); + if ( returnType == HTTPResponse.MIME_JSON ) { // YES string compared by ==. + params.setProperty( "method", "fr.inrialpes.exmo.align.impl.renderer.JSONRendererVisitor" ); + } else if ( returnType == HTTPResponse.MIME_RDFXML ) { + params.setProperty( "method", "fr.inrialpes.exmo.align.impl.renderer.RDFRendererVisitor" ); + } else { + params.setProperty( "method", "fr.inrialpes.exmo.align.impl.renderer.HTMLRendererVisitor" ); + } + logger.trace( "Bloody URI : {}", manager.serverURL()+uri); + Message answer = manager.render( new Message( newId(), (Message)null, myId, serverId, "", params) ); + if ( answer instanceof ErrorMsg ) { + return new HTTPResponse( HTTPResponse.HTTP_NOTFOUND, HTTPResponse.MIME_PLAINTEXT, "Alignment server: unknown alignment : "+answer.getContent() ); + } else { + return new HTTPResponse( HTTPResponse.HTTP_OK, mimeType, answer.getContent() ); + } + } + + // =============================================== + // Util + + private int newId() { return localId++; } + + private void decodeParams( String params, Properties p ) throws InterruptedException { + if ( params == null ) return; + + for ( String next : params.split("&") ) { + int sep = next.indexOf( '=' ); + if ( sep >= 0 ){ + try { + p.put( URLDecoder.decode( next.substring( 0, sep ), "iso-8859-1" ).trim(), + // JE: URLDecoder allows for : and / but not # + URLDecoder.decode( next.substring( sep+1 ), "iso-8859-1" )); + } catch (Exception e) {}; //never thrown + } + } + } + /** + * Two private cclasses for retrieving parameters + */ + private class ParameterServletResponseWrapper extends ServletResponseWrapper { + private Map parameters; + + public ParameterServletResponseWrapper( ServletResponse r ){ + super(r); + }; + + public Map getParameterMap(){ return parameters; } + + public void setParameterMap( Map m ){ parameters = m; } + + } + + private class Chain implements FilterChain { + + public void doFilter( ServletRequest request, ServletResponse response) + throws IOException, ServletException { + if ( response instanceof ParameterServletResponseWrapper && + request instanceof ServletRequestWrapper ) { + ((ParameterServletResponseWrapper)response).setParameterMap( ((ServletRequestWrapper)request).getParameterMap() ); + } + } + + } +} + diff --git a/src/fr/inrialpes/exmo/align/service/WSAServProfile.java b/src/fr/inrialpes/exmo/align/service/WSAServProfile.java index 8043f5550364652b87a7073e84b18614a52d6ce8..6625a82225476556acd5843a8990595260bb091a 100644 --- a/src/fr/inrialpes/exmo/align/service/WSAServProfile.java +++ b/src/fr/inrialpes/exmo/align/service/WSAServProfile.java @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (C) INRIA, 2007-2013 + * Copyright (C) INRIA, 2007-2014 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -90,6 +90,8 @@ public class WSAServProfile implements AlignmentServiceProfile { private AServProtocolManager manager; private static String wsdlSpec = ""; + private boolean restful = false; + private String myId; private String serverURL; private int localId = 0; @@ -184,6 +186,23 @@ public class WSAServProfile implements AlignmentServiceProfile { } } + public boolean accept( String prefix ) { + if ( prefix.equals("aserv") || prefix.equals("rest") || prefix.equals("wsdl") ) return true; + else return false; + } + + public String process( String uri, String prefix, String perf, Properties header, Properties params ) { + restful = false; + if ( prefix.equals("rest") ) { + restful = true; + return protocolAnswer( uri, perf, header, params ); + } else if ( prefix.equals("aserv") ) { + return protocolAnswer( uri, perf, header, params ); + } else if ( prefix.equals("wsdl") ) { + return wsdlAnswer( false ); + } else return "";//about(); + } + public void close(){ // This may unregister the WSDL file to some directory } @@ -204,7 +223,6 @@ public class WSAServProfile implements AlignmentServiceProfile { String message = null; Properties newparameters = null; Message answer = null; - boolean restful = (param.getProperty("restful")==null)?false:true; String svcNS = "\n xml:base='"+Namespace.ALIGNSVC.prefix+"'"+ "\n xmlns='"+Namespace.ALIGNSVC.prefix+"'"; String msg = ""; @@ -221,16 +239,19 @@ public class WSAServProfile implements AlignmentServiceProfile { // However, there is a way to pass SOAP messages with attachments // It would be better to implement this. See: // http://www.oracle.com/technology/sample_code/tech/java/codesnippet/webservices/attachment/index.html - message = param.getProperty("content").trim(); + message = param.getProperty("content"); // Create the DOM tree for the SOAP message Document domMessage = null; try { - domMessage = BUILDER.parse( new ByteArrayInputStream( message.getBytes()) ); + domMessage = BUILDER.parse( new ByteArrayInputStream( message.trim().getBytes() ) ); } catch ( IOException ioex ) { - logger.debug( "IGNORED Exception", ioex ); + logger.debug( "IGNORED Exception {}", ioex ); answer = new NonConformParameters(0,(Message)null,myId,"Cannot Parse SOAP message",message,(Properties)null); } catch ( SAXException saxex ) { - logger.debug( "IGNORED Exception", saxex ); + logger.debug( "IGNORED Exception {}", saxex ); + answer = new NonConformParameters(0,(Message)null,myId,"Cannot Parse SOAP message",message,(Properties)null); + } catch ( NullPointerException npex ) { + logger.debug( "IGNORED Exception {}", npex ); answer = new NonConformParameters(0,(Message)null,myId,"Cannot Parse SOAP message",message,(Properties)null); } newparameters = getParameters( domMessage ); @@ -238,6 +259,7 @@ public class WSAServProfile implements AlignmentServiceProfile { newparameters = new Properties(); } } + //logger.debug( "Analised header" ); // Process the action if ( perf.equals("WSDL") || method.equals("wsdl") || method.equals("wsdlRequest") ) { diff --git a/src/fr/inrialpes/exmo/align/service/jade/JadeFIPAAServProfile.java b/src/fr/inrialpes/exmo/align/service/jade/JadeFIPAAServProfile.java index ba25babfc9fd4a636579db1db983b8fbad96dc9e..3c3578360ff8fbc70af1f777ce873969f7d563c6 100755 --- a/src/fr/inrialpes/exmo/align/service/jade/JadeFIPAAServProfile.java +++ b/src/fr/inrialpes/exmo/align/service/jade/JadeFIPAAServProfile.java @@ -2,7 +2,7 @@ * $Id$ * * Copyright (C) Orange R&D, 2006-2007 - * Copyright (C) INRIA, 2006-2007, 2009-2010, 2013 + * Copyright (C) INRIA, 2006-2007, 2009-2010, 2013-2014 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -41,63 +41,69 @@ import org.slf4j.LoggerFactory; public class JadeFIPAAServProfile implements AlignmentServiceProfile { final static Logger logger = LoggerFactory.getLogger( JadeFIPAAServProfile.class ); - private AgentContainer mc; - private AgentController algagentcontroller; + private AgentContainer mc; + private AgentController algagentcontroller; - public void init( Properties params, AServProtocolManager manager ) throws AServException { - int port = 8888; - Object args[] = new Object[2]; - - //set up the manager as an argument to pass to the JADEFIPAAServiceAgent - args[0]=manager; + public void init( Properties params, AServProtocolManager manager ) throws AServException { + int port = 8888; + Object args[] = new Object[2]; + + //set up the manager as an argument to pass to the JADEFIPAAServiceAgent + args[0]=manager; - // set up the Parameters as an argument to pass to the JADEFIPAServiceAgent - args[1]=params; + // set up the Parameters as an argument to pass to the JADEFIPAServiceAgent + args[1]=params; - if ( params.getProperty( "jade" ) != null ) - port = Integer.parseInt( params.getProperty( "jade" ) ); + if ( params.getProperty( "jade" ) != null ) + port = Integer.parseInt( params.getProperty( "jade" ) ); - /** - Properties props = new Properties(); - **/ - try { - // Get a hold on JADE runtime - Runtime rt = Runtime.instance(); + // Properties props = new Properties(); + try { + // Get a hold on JADE runtime + Runtime rt = Runtime.instance(); - // Exit the JVM when there are no more containers around - rt.setCloseVM(true); + // Exit the JVM when there are no more containers around + rt.setCloseVM(true); - /** Profile with no MTP( Message Transfer Protocol + /** Profile with no MTP( Message Transfer Protocol props.setProperty("nomtp", "true"); Profile pMain = new ProfileImpl(props); - **/ - // create a default Profile - Profile pMain = new ProfileImpl(null, port, null); - - //logger.trace( "Launching a whole in-process platform... {}", pMain ); - mc = rt.createMainContainer(pMain); - algagentcontroller = mc.createNewAgent("JadeFIPAAServiceAgent", JadeFIPAAServiceAgent.class.getName(), args); - algagentcontroller.start(); - } - catch(Exception e) { - throw new AServException ( "Cannot launch Jade Server" , e ); - } + **/ + // create a default Profile + Profile pMain = new ProfileImpl(null, port, null); + + //logger.trace( "Launching a whole in-process platform... {}", pMain ); + mc = rt.createMainContainer(pMain); + algagentcontroller = mc.createNewAgent("JadeFIPAAServiceAgent", JadeFIPAAServiceAgent.class.getName(), args); + algagentcontroller.start(); + } catch(Exception ex) { + throw new AServException ( "Cannot launch Jade Server" , ex ); } + } + - public void close(){ - try{ - algagentcontroller.kill(); - mc.kill(); - logger.info( "Agent Alignement closed" ); - } catch (ControllerException e) { - logger.warn( "Error killing the alignment agent." ); } - try { - // Destroy the files please (JE) - new File("APDescription.txt").delete(); - new File("MTPs-Main-Container.txt").delete(); - } catch (Exception e) { - logger.debug( "IGNORED Exception", e ); - } + public boolean accept( String prefix ) { + return false; + } + + public String process( String uri, String prefix, String perf, Properties header, Properties params ) { + return "JADE Cannot be invoked this way through HTTP service"; + } + + public void close(){ + try{ + algagentcontroller.kill(); + mc.kill(); + logger.info( "Agent Alignement closed" ); + } catch (ControllerException cex) { + logger.warn( "Error killing the alignment agent." ); } - + try { + // Destroy the files please (JE) + new File("APDescription.txt").delete(); + new File("MTPs-Main-Container.txt").delete(); + } catch (Exception ex) { + logger.debug( "IGNORED Exception", ex ); + } + } } diff --git a/src/fr/inrialpes/exmo/align/service/osgi/OSGIAServProfile.java b/src/fr/inrialpes/exmo/align/service/osgi/OSGIAServProfile.java index 2ccd804faeee117ca012b06ccc5af4976ae7080c..5103615d5b6cbf8f1892062b28b2c1574d160392 100644 --- a/src/fr/inrialpes/exmo/align/service/osgi/OSGIAServProfile.java +++ b/src/fr/inrialpes/exmo/align/service/osgi/OSGIAServProfile.java @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (C) INRIA, 2010, 2013 + * Copyright (C) INRIA, 2010, 2013-2014 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -56,6 +56,14 @@ public class OSGIAServProfile implements AlignmentServiceProfile, BundleActivato this.manager = manager; } + public boolean accept( String prefix ) { + return false; + } + + public String process( String uri, String prefix, String perf, Properties header, Properties params ) { + return "OSGI Cannot be invoked this way through HTTP service"; + } + public void close(){ // This may unregister the WSDL file to some directory stop( ctxt );