fr.inrialpes.exmo.align.service.HTTPTransport.java Source code

Java tutorial

Introduction

Here is the source code for fr.inrialpes.exmo.align.service.HTTPTransport.java

Source

/*
 * $Id: HTTPTransport.java 2102 2015-11-29 10:29:56Z euzenat $
 *
 * Copyright (C) INRIA, 2006-2015
 *
 * 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.IOException;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileOutputStream;

import java.util.Vector;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Enumeration;

import java.net.URLDecoder;

import java.lang.Integer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.Request;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.io.FilenameUtils;

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.
     *
     * @param params: the parameters of the connection, including HTTP port and host
     * @param manager: the manager which will deal with connections
     * @param serv: the set of services to be listening on this connection
     * @throws AServException when something goes wrong (e.g., socket 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");

        // ********************************************************************
        // 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 String, Request baseRequest, HttpServletRequest request,
                    HttpServletResponse response) throws IOException, ServletException {
                String method = request.getMethod();
                String uri = request.getPathInfo();
                Properties params = new Properties();
                try {
                    decodeParams(request.getQueryString(), params);
                } catch (Exception ex) {
                    logger.debug("IGNORED EXCEPTION: {}", ex);
                }
                ;
                // I do not decode them here because it is useless
                // See below how it is done.
                Properties header = new Properties();
                Enumeration<String> headerNames = request.getHeaderNames();
                while (headerNames.hasMoreElements()) {
                    String headerName = headerNames.nextElement();
                    header.setProperty(headerName, request.getHeader(headerName));
                }

                // Get the content if any
                // This is supposed to be only an uploaded 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")) {
                    try {
                        //if ( !ServletFileUpload.isMultipartContent( request ) ) {
                        //   logger.debug( "Does not detect multipart" );
                        //}
                        DiskFileItemFactory factory = new DiskFileItemFactory();
                        File tempDir = new File(System.getProperty("java.io.tmpdir"));
                        factory.setRepository(tempDir);
                        ServletFileUpload upload = new ServletFileUpload(factory);
                        List<FileItem> items = upload.parseRequest(request);
                        for (FileItem fi : items) {
                            if (fi.isFormField()) {
                                logger.trace("  >> {} = {}", fi.getFieldName(), fi.getString());
                                params.setProperty(fi.getFieldName(), fi.getString());
                            } else {
                                logger.trace("  >> {} : {}", fi.getName(), fi.getSize());
                                logger.trace("  Stored at {}", fi.getName(), fi.getSize());
                                try {
                                    // FilenameUtils.getName() needed for Internet Explorer problem
                                    File uploadedFile = new File(tempDir, FilenameUtils.getName(fi.getName()));
                                    fi.write(uploadedFile);
                                    params.setProperty("filename", uploadedFile.toString());
                                    params.setProperty("todiscard", "true");
                                } catch (Exception ex) {
                                    logger.warn("Cannot load file", ex);
                                }
                                // Another solution is to run this in 
                                /*
                                  InputStream uploadedStream = item.getInputStream();
                                  ...
                                  uploadedStream.close();
                                */
                            }
                        }
                        ;
                    } catch (FileUploadException fuex) {
                        logger.trace("Upload Error", fuex);
                    }
                } 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.
     *
     * (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 an HTTP response made of these elements (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 (Entry<Object, Object> e : parms.entrySet()) {
            //logger.trace( "  PRM: '{}' = '{}'", e.getKey(), e.getValue() );
            String key = (String) e.getKey();
            if (key.startsWith("paramn")) {
                params.setProperty((String) e.getValue(), parms.getProperty("paramv" + key.substring(6)));
            } else if (!key.startsWith("paramv")) {
                params.setProperty(key, (String) e.getValue());
            }
        }
        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("onid")) { // Asks for a network by URI
            return returnNetwork(uri, returnType);
        } else if (oper.equals("")) {
            // SHOULD BE ASSIGNED TO CONTENT NEGOCIATION AS WELL... (DEFAULT IN SERVERS)
            //return serveFile( uri, header, new File("."), true );
            HTMLAServProfile profile = null;
            for (AlignmentServiceProfile serv : services) {
                if (serv instanceof HTMLAServProfile)
                    profile = (HTMLAServProfile) serv;
                break;
            }
            if (profile == null)
                profile = new HTMLAServProfile();
            return new HTTPResponse(HTTPResponse.HTTP_OK, HTTPResponse.MIME_HTML, "<html><head>"
                    + HTMLAServProfile.HEADER + "</head><body>" + profile.about() + "</body></html>");
        } else {
            // Selects the relevant service for the request
            for (AlignmentServiceProfile serv : services) {
                // JE2014: oper is not anymore an operation
                // It is one of: aserv. rest. wsdl. html.
                // wsdl could be served here
                // the others would be better ( man + noo + align ) / oper
                // That should be a large change
                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
     *
     * @param uri: the URI of an alignment
     * @param mimeType: the MIME type in which the alignment is requested
     * @return an HTTP response corresponding to the alignment in the requested MIME type
     */
    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("Alignment URI : {}", manager.serverURL() + uri);
        Message answer = manager.render(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());
        }
    }

    /**
     * Returns the network in HTML or RDF
     *
     * @param uri: the URI of a network of ontologies
     * @param mimeType: the MIME type in which the network of ontologies is requested
     * @return an HTTP response corresponding to the network of ontologies in the requested MIME type
     */
    public HTTPResponse returnNetwork(String uri, String mimeType) {
        Properties params = new Properties();
        params.setProperty("id", manager.serverURL() + uri);
        logger.trace("Network URI : {}", manager.serverURL() + uri);
        if (returnType == HTTPResponse.MIME_HTML) { // YES string compared by ==.
            Message answer = manager.renderHTMLNetwork(params);
            if (answer instanceof ErrorMsg) {
                return new HTTPResponse(HTTPResponse.HTTP_NOTFOUND, HTTPResponse.MIME_PLAINTEXT,
                        "Alignment server: unknown network : " + answer.getContent());
            } else {
                return new HTTPResponse(HTTPResponse.HTTP_OK, mimeType, "<html><head>" + HTMLAServProfile.HEADER
                        + "</head><body>" + answer.getContent() + "</body></html>");
            }
        } else {
            Message answer = manager.renderOntologyNetwork(params);
            if (answer instanceof ErrorMsg) {
                return new HTTPResponse(HTTPResponse.HTTP_NOTFOUND, HTTPResponse.MIME_PLAINTEXT,
                        "Alignment server: unknown network : " + 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 ex) { //never thrown
                    logger.debug("IGNORED (SHOULD NEVER BEEN TROWN: {}", ex);
                }
                ;
            }
        }
    }
}