Package lupos.endpoint.server

Source Code of lupos.endpoint.server.Endpoint$OutputStreamSizeLogger

/**
* Copyright (c) 2013, Institute of Information Systems (Sven Groppe and contributors of LUPOSDATE), University of Luebeck
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
*   - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
*     disclaimer.
*   - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
*     following disclaimer in the documentation and/or other materials provided with the distribution.
*   - Neither the name of the University of Luebeck nor the names of its contributors may be used to endorse or promote
*     products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lupos.endpoint.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import lupos.datastructures.bindings.Bindings;
import lupos.datastructures.bindings.BindingsArrayReadTriples;
import lupos.datastructures.queryresult.QueryResult;
import lupos.endpoint.server.format.CSVFormatter;
import lupos.endpoint.server.format.Formatter;
import lupos.endpoint.server.format.HTMLFormatter;
import lupos.endpoint.server.format.JSONFormatter;
import lupos.endpoint.server.format.PlainFormatter;
import lupos.endpoint.server.format.QueryTriplesFormatter;
import lupos.endpoint.server.format.TSVFormatter;
import lupos.endpoint.server.format.XMLFormatter;
import lupos.engine.evaluators.BasicIndexQueryEvaluator;
import lupos.engine.evaluators.CommonCoreQueryEvaluator;
import lupos.engine.evaluators.RDF3XQueryEvaluator;
import lupos.engine.operators.singleinput.federated.BitVectorFilterFunction;

import com.google.common.collect.Sets;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

@SuppressWarnings("restriction")
public class Endpoint {

  // enable or disable logging into console
  public static boolean log = false;
  // enable or disable logging the sizes of the query and its response into console
  public static boolean sizelog = false;

  private final static Map<String, Formatter> registeredFormatter = Collections.synchronizedMap(new HashMap<String, Formatter>());

  private final static Map<String, HttpHandler> registeredhandler = Collections.synchronizedMap(new HashMap<String, HttpHandler>());

  private static HTMLForm htmlForm = new StandardHTMLForm();

  public static Class<? extends Bindings> defaultBindingsClass = Bindings.instanceClass;

  public static void registerFormatter(final Formatter formatter){
    Endpoint.registeredFormatter.put(formatter.getKey().toLowerCase(), formatter);
    Endpoint.registeredFormatter.put(formatter.getName().toLowerCase(), formatter);
  }

  public static Map<String, Formatter> getRegisteredFormatters(){
    return Endpoint.registeredFormatter;
  }

  public static void registerHandler(final String context, final HttpHandler httpHandler){
    Endpoint.registeredhandler.put(context, httpHandler);
  }

  public static HTMLForm getHTMLForm(){
    return Endpoint.htmlForm;
  }

  public static void setHTMLForm(final HTMLForm htmlForm){
    Endpoint.htmlForm = htmlForm;
  }

  public static Class<? extends Bindings> getDefaultBindingsClass() {
    return defaultBindingsClass;
  }

  /**
   * HTTP Request Example
   * http://localhost:8080/sparql?query=PREFIX+rdf%3A%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E+SELECT+*+WHERE%7B+%3Fs+rdf%3Atype+%3Fo.+%7D&format=application%2Fsparql-results%2Bxml
   * for query
   * PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> SELECT * WHERE{?s rdf:type ?o. }
   */
  public static void main(final String[] args) throws Exception {
    final int port = Endpoint.init(args);
    Endpoint.registerStandardFormattersAndContexts(args[0]);
    Endpoint.initAndStartServer(port);
  }

  public static int init(final String[] args){
    if (args.length < 1) {
      System.err.println("Usage:\njava -Xmx768M lupos.endpoint.server.Endpoint <directory for indices> [portX] [output] [size]");
      System.err.println("(The indices can be constructed using lupos.engine.indexconstruction.FastRDF3XIndexConstruction)");
      System.err.println("If \"portX\" is given, the port X (default 8080) is used, X must be a non-negative number.");
      System.err.println("If \"output\" is given, the response is written to console.");
      System.err.println("If \"size\" is given, the size of the received query and the size of the response is written to console.");
      System.exit(0);
    }
    int port = 8080;
    for(int i=1; i<args.length; i++){
      if(args[i].compareTo("output")==0){
        Endpoint.log = true;
      } else if(args[i].compareTo("size")==0){
        Endpoint.sizelog = true;
      } else if(args[i].startsWith("port")){
        port = Integer.parseInt(args[i].substring("port".length()));
      }
    }
    return port;
  }

  public static void initAndStartServer(final int port){
    try {
      final String localHost = InetAddress.getLocalHost().getHostName();
      System.out.println("Starting LUPOSDATE Endpoint on host: "+localHost);
      System.out.println("Canonical host name: "+InetAddress.getLocalHost().getCanonicalHostName());
      for (final InetAddress ia : InetAddress.getAllByName(localHost)){
        System.out.println("IP: "+ia);
      }

      Endpoint.startServer(port);
      System.out.println("Endpoint ready to receive requests...");
      System.out.println("_____________________________________");
    } catch (final Exception e) {
      System.err.println(e);
      e.printStackTrace();
    }
  }

  public static BasicIndexQueryEvaluator createQueryEvaluator(final String directory){
    try {
      final RDF3XQueryEvaluator evaluator = new RDF3XQueryEvaluator();
      evaluator.loadLargeScaleIndices(directory);
      defaultBindingsClass = Bindings.instanceClass;
      return evaluator;
    } catch (final Exception e) {
      System.err.println(e);
      e.printStackTrace();
      return null;
    }
  }

  /**
   * register the standard formatters and contexts of the server...
   */
  public static void registerStandardFormattersAndContexts(final String directory) {
    Endpoint.registerStandardFormatter();
    Endpoint.registerStandardContexts(directory);
  }

  public static void registerStandardFormatter(){
    Endpoint.registerFormatter(new XMLFormatter());
    Endpoint.registerFormatter(new XMLFormatter(true));
    Endpoint.registerFormatter(new PlainFormatter());
    Endpoint.registerFormatter(new JSONFormatter());
    Endpoint.registerFormatter(new JSONFormatter(true));
    Endpoint.registerFormatter(new CSVFormatter());
    Endpoint.registerFormatter(new TSVFormatter());
    Endpoint.registerFormatter(new HTMLFormatter(false));
    Endpoint.registerFormatter(new HTMLFormatter(true));
    Endpoint.registerFormatter(new HTMLFormatter(false, true));
    Endpoint.registerFormatter(new HTMLFormatter(true, true));
    Endpoint.registerFormatter(new QueryTriplesFormatter());
  }

  public static void registerStandardContexts(final String directory){
    Endpoint.registerHandler("/sparql", new SPARQLHandler(new SPARQLExecutionImplementation(Endpoint.createQueryEvaluator(directory), directory)));
    Endpoint.registerHandler("/", new HTMLFormHandler());
  }

  public static void startServer(final int port){
    try {
      final HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

      for(final Entry<String, HttpHandler> entry: Endpoint.registeredhandler.entrySet()){
        server.createContext(entry.getKey(), entry.getValue());
      }

      server.setExecutor(null);
      server.start();
    } catch (final Exception e) {
      System.err.println(e);
      e.printStackTrace();
    }
  }

  public static String getResponse(final HttpExchange t) throws IOException {
    final String requestMethod = t.getRequestMethod();
    String response;
    if (requestMethod.equalsIgnoreCase("POST")) {
      final InputStream bodyStream = t.getRequestBody();
      final StringBuilder builder = new StringBuilder();
      final BufferedReader reader = new BufferedReader(new InputStreamReader(bodyStream, "UTF-8"));
      String readLine;
      boolean firstTime=true;
      while ((readLine = reader.readLine()) != null) {
        if(firstTime){
          firstTime=false;
        } else {
          builder.append("\n");
        }
        builder.append(readLine);
      }
      response=builder.toString();
    } else if (requestMethod.equalsIgnoreCase("GET")) {
      response = t.getRequestURI().getRawQuery();
    } else {
      response = "";
    }
    return response;
  }

  public static interface SPARQLExecution {
    public void execute(final String queryParameter, final Formatter formatter, final HttpExchange t) throws IOException;
  }

  public static class SPARQLExecutionImplementation implements SPARQLExecution {

    protected final BasicIndexQueryEvaluator evaluator;
    protected final String dir;

    public SPARQLExecutionImplementation(final BasicIndexQueryEvaluator evaluator, final String dir){
      this.evaluator = evaluator;
      this.dir = dir;
    }

    @Override
    public void execute(final String queryParameter, final Formatter formatter, final HttpExchange t) throws IOException {
      if(Endpoint.sizelog){
        System.out.println("Size of the received query (number of characters): "+queryParameter.length());
      }
      try {
        synchronized(this.evaluator){ // avoid any inference of several queries in parallel!
          System.out.println("Evaluating query:\n"+queryParameter);
          if((this.evaluator instanceof CommonCoreQueryEvaluator) && formatter.isWriteQueryTriples()){
            // log query-triples by using BindingsArrayReadTriples as class for storing the query solutions!
            Bindings.instanceClass = BindingsArrayReadTriples.class;
          } else {
            Bindings.instanceClass = Endpoint.defaultBindingsClass;
          }
          final QueryResult queryResult = (this.evaluator instanceof CommonCoreQueryEvaluator)?((CommonCoreQueryEvaluator)this.evaluator).getResult(queryParameter, true):this.evaluator.getResult(queryParameter);
          final String mimeType = formatter.getMIMEType(queryResult);
          System.out.println("Done, sending response using MIME type "+mimeType);
          t.getResponseHeaders().add("Content-type", mimeType);
          t.getResponseHeaders().add("Transfer-encoding", "chunked");
          t.sendResponseHeaders(200, 0);
          OutputStream os = t.getResponseBody();
          if(Endpoint.log){
            os = new OutputStreamLogger(os);
          }
          if(Endpoint.sizelog){
            os = new OutputStreamSizeLogger(os);
          }
          formatter.writeResult(os, this.evaluator.getVariablesOfQuery(), queryResult);
          os.close();
          if(this.evaluator instanceof RDF3XQueryEvaluator){
            ((RDF3XQueryEvaluator)this.evaluator).writeOutIndexFileAndModifiedPages(this.dir);
          }
        }
        return;
      } catch (final Error e) {
        System.err.println(e);
        e.printStackTrace();
        t.getResponseHeaders().add("Content-type", "text/plain");
        final String answer = "Error:\n"+e.getMessage();
        System.out.println(answer);
        Endpoint.sendString(t, answer);
        return;
      } catch (final Exception e){
        System.err.println(e);
        e.printStackTrace();
        t.getResponseHeaders().add("Content-type", "text/plain");
        final String answer = "Error:\n"+e.getMessage();
        System.out.println(answer);
        Endpoint.sendString(t, answer);
        return;
      }
    }
  }

  public static class SPARQLHandler implements HttpHandler {

    private final SPARQLExecution sparqlExecution;

    public SPARQLHandler(final SPARQLExecution sparqlExecution){
      super();
      this.sparqlExecution = sparqlExecution;
      BitVectorFilterFunction.register();
    }

    private final static String format = "format=";
    private final static String query = "query=";

    @Override
    public void handle(final HttpExchange t) throws IOException {
      System.out.println("\n-> Receiving request from: "+t.getRequestHeaders().get("Host"));
      final String response = Endpoint.getResponse(t);
      final String[] responseParts = response.split("[&]");
      if(responseParts.length>0){
        // first check whether or not a format is given (default is XML as defined by W3C)
        final String formatParameter = Endpoint.getParameter(responseParts, format, "XML");
        final Formatter formatter = registeredFormatter.get(formatParameter.toLowerCase());
        if(formatter == null){
          t.getResponseHeaders().add("Content-type", "text/plain");
          final String answer = "Bad Request: format " + formatParameter + " not supported";
          System.out.println(answer);
          Endpoint.sendString(t, answer);
          return;
        }
        // now look for a query parameter
        final String queryParameter = getParameter(responseParts, query);
        if(queryParameter!=null){
          this.sparqlExecution.execute(queryParameter, formatter, t);
        } else {
          t.getResponseHeaders().add("Content-type", "text/plain");
          final String answer = "Bad Request: query parameter missing";
          System.out.println(answer);
          Endpoint.sendString(t, answer);
          return;
        }
      }
      Endpoint.htmlForm.sendHTMLForm(t);
    }
  }

  public static class HTMLFormHandler implements HttpHandler {
    @Override
    public void handle(final HttpExchange t) throws IOException {
      Endpoint.htmlForm.sendHTMLForm(t);
    }
  }

  protected static String getParameter(final String[] responseParts, final String parameter) throws UnsupportedEncodingException{
    for(final String item: responseParts){
      if(item.startsWith(parameter)){
        return URLDecoder.decode(item.substring(parameter.length()), "UTF-8");
      }
    }
    return null;
  }

  protected static String getParameter(final String[] responseParts, final String parameter, final String defaultValue) throws UnsupportedEncodingException{
    final String result = Endpoint.getParameter(responseParts, parameter);
    if(result!=null){
      return result;
    } else {
      return defaultValue;
    }
  }

  public static void sendString(final HttpExchange t, final String toSend) throws IOException{
    t.sendResponseHeaders(200, toSend.length());
    final OutputStream os = t.getResponseBody();
    os.write(toSend.getBytes());
    os.close();
  }

  public static interface HTMLForm{
    public void sendHTMLForm(final HttpExchange t) throws IOException;
  }

  public static class StandardHTMLForm implements HTMLForm {

    private static String HTML_FORM_1 = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"+
                      "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" +
                      "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"+
                      "<head>\n <title>LUPOSDATE SPARQL Endpoint</title>\n</head>\n"+
                      "<body>\n <h1>LUPOSDATE SPARQL Endpoint</h1>\n\n"+
                      " <form method=\"get\" action=\"sparql\">\n  <p>Type in your SPARQL query:<br/>\n   "+
                      "<textarea name=\"query\" cols=\"50\" rows=\"10\">SELECT * WHERE { ?s ?p ?o. } LIMIT 10</textarea>\n  </p>\n" +
                      "  <p>\n   Result Format:<br/>\n   <select name=\"format\" size=\"1\">\n   ";
    private static String HTML_FORM_2 = "</select>\n  </p>\n  <p>\n   <input type=\"submit\" value=\" Submit Query \"/>\n  </p>\n "+
                      "</form>\n</body>\n</html>";

    private static String HTML_OPTION_1 = " <option value=\"";
    private static String HTML_OPTION_2 = "\">";
    private static String HTML_OPTION_3 = "</option>\n   ";


    @Override
    public void sendHTMLForm(final HttpExchange t) throws IOException {
      final StringBuilder toSend = new StringBuilder(StandardHTMLForm.HTML_FORM_1);
      // do duplicate elimination!
      final Formatter[] formatters=Sets.newHashSet(Endpoint.getRegisteredFormatters().values()).toArray(new Formatter[0]);
      // sort according to the name of the formatter
      Arrays.sort(formatters, new Comparator<Formatter>(){
        @Override
        public int compare(final Formatter o1, final Formatter o2) {
          return o1.getName().compareTo(o2.getName());
        }
      });
      for(final Formatter formatter: formatters){
        toSend.append(StandardHTMLForm.HTML_OPTION_1);
        toSend.append(formatter.getKey());
        toSend.append(StandardHTMLForm.HTML_OPTION_2);
        toSend.append(formatter.getName());
        toSend.append(StandardHTMLForm.HTML_OPTION_3);
      }
      toSend.append(StandardHTMLForm.HTML_FORM_2);
      Endpoint.sendString(t, toSend.toString());
    }
  }

  public static class OutputStreamLogger extends OutputStream {

    private final OutputStream piped;

    public OutputStreamLogger(final OutputStream piped){
      this.piped = piped;
    }

    @Override
    public void write(final int b) throws IOException {
      if(b>=0){
        for(final char c: Character.toChars(b)){
          System.out.print(c);
        }
      }
      this.piped.write(b);
    }

    @Override
    public void close() throws IOException{
      this.piped.close();
    }
  }

  public static class OutputStreamSizeLogger extends OutputStream {

    private final OutputStream piped;
    private int size = 0;

    public OutputStreamSizeLogger(final OutputStream piped){
      this.piped = piped;
    }

    @Override
    public void write(final int b) throws IOException {
      if(b>=0){
        this.size++;
      }
      this.piped.write(b);
    }

    @Override
    public void close() throws IOException{
      System.out.println("Size of response in bytes: "+this.size);
      this.piped.close();
    }
  }
}
TOP

Related Classes of lupos.endpoint.server.Endpoint$OutputStreamSizeLogger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.