Commit 1701709e authored by Michal Batko's avatar Michal Batko
Browse files

* Renamed and enhanced REST service utility

parent 254d5d1b
Loading
Loading
Loading
Loading
+213 −0
Original line number Diff line number Diff line
package messif.utility;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Random;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import messif.algorithms.Algorithm;
import messif.objects.LocalAbstractObject;
import messif.objects.NoDataObject;
import messif.operations.query.KNNQueryOperation;

/**
 * Simple class that can be used for testing HTTP REST services.
 * It also provides a MESSIF {@link Algorithm} that allows to execute 
 * {@link KNNQueryOperation}s via REST service.
 * 
 * <p>Example:
 * <pre>
 * System.out.println(RestServiceAlgorithm.processHttpRequest("localhost", 12345, "/insertImage?imageId=testImage", null, new File("myTestImage.jpg")));
 * System.out.println(RestServiceAlgorithm.processHttpRequest("localhost", 12345, "/search?k=2", "locator", "testImage"));
 * </pre>
 * 
 * @author Michal Batko, Masaryk University, Brno, Czech Republic, batko@fi.muni.cz
 */
public class TestHttpService {
    /** Random number generator */
    public static final Random randomGenerator = new Random();
public class RestServiceAlgorithm extends Algorithm {
    /** Class id for serialization */
    private static final long serialVersionUID = 1L;

    /** Host (IP address) where the service is running */
    private final String host;
    /** TCP port on which the service is running */
    private final int port;
    /** Name of the service to execute (including all necessary parameters)*/
    private final String service;
    /** Additional parameter where the number of results (k) is passed */
    private final String resultCountParamName;
    /** Additional parameter where the query object is passed */
    private final String queryObjectParamName;

    /**
     * @param host the host (IP address) where the service is running
     * @param port the TCP port on which the service is running
     * @param service the name of the service to execute (including all necessary parameters)
     * @param resultCountParamName the additional parameter where the number of results (k) is passed
     * @param queryObjectParamName the additional parameter where the query object is passed;
     *          if <tt>null</tt>, the query object is sent via POST method
     */
    public RestServiceAlgorithm(String host, int port, String service, String resultCountParamName, String queryObjectParamName) {
        super("HttpService on " + host + ":" + port);
        this.host = host;
        this.port = port;
        this.service = service;
        this.resultCountParamName = resultCountParamName;
        this.queryObjectParamName = queryObjectParamName;
    }

    /**
     * Create a float vector of arbitrary dimension.
     * @param dim the dimension of the vector to create
     * @return the coma separated vector of random float numbers of the specified dimension
     * Executes KNN query operation via the REST service.
     * @param operation the operation to execute
     * @throws IOException if there was a problem working with the REST service
     */
    public static String createLongFloatVector(int dim) {
        StringBuilder str = new StringBuilder();
        str.append(randomGenerator.nextFloat());
        for (int i = 0; i < dim; i++)
            str.append(", ").append(randomGenerator.nextFloat());
        return str.toString();
    public void executeSearch(KNNQueryOperation operation) throws IOException {
        StringBuilder searchService = new StringBuilder(service);
        if (resultCountParamName != null)
            searchService = appendQueryParameter(searchService, resultCountParamName, Integer.toString(operation.getK()));
        String result = processHttpRequest(host, port, searchService, queryObjectParamName, operation.getQueryObject());

        // Parse JSON output
        Pattern pattern = Pattern.compile("\\[([^\\[,]+),\"([^]]+)\"\\]");
        Matcher matcher = pattern.matcher(result);
        while (matcher.find()) {
            operation.addToAnswer(new NoDataObject(matcher.group(2)), Float.parseFloat(matcher.group(1)), null);
        }
        operation.endOperation();
    }

    /**
@@ -45,13 +97,28 @@ public class TestHttpService {
     * @return the REST service response
     * @throws IOException if the REST service returned an error return code
     */
    public static String processHttpRequest(String host, int port, String service, String dataParamName, LocalAbstractObject obj) throws IOException {
    public static String processHttpRequest(String host, int port, CharSequence service, String dataParamName, LocalAbstractObject obj) throws IOException {
        // Convert the object to string data
        ByteArrayOutputStream objData = new ByteArrayOutputStream();
        obj.write(objData);
        return processHttpRequest(host, port, service, dataParamName, new String(objData.toByteArray()));
    }

    /**
     * Process request on a HTTP REST service.
     * @param host the host (IP address) where the service is running
     * @param port the TCP port on which the service is running
     * @param service the name of the service to execute (including all necessary parameters)
     * @param dataParamName the additional parameter where the data are passed;
     *          if <tt>null</tt>, the data are sent via POST method
     * @param obj the object to send as data, it will be converted to text representation
     * @return the REST service response
     * @throws IOException if the REST service returned an error return code
     */
    public static String processHttpRequest(String host, int port, CharSequence service, String dataParamName, File obj) throws IOException {
        return processHttpRequest(host, port, service, dataParamName, new FileInputStream(obj));
    }

    /**
     * Process request on a HTTP REST service.
     * @param host the host (IP address) where the service is running
@@ -63,22 +130,39 @@ public class TestHttpService {
     * @return the REST service response
     * @throws IOException if the REST service returned an error return code
     */
    public static String processHttpRequest(String host, int port, String service, String dataParamName, String data) throws IOException {
    public static String processHttpRequest(String host, int port, CharSequence service, String dataParamName, Object data) throws IOException {
        // Append data parameter if needed
        if (dataParamName != null && data != null)
            service = service + (service.indexOf('?') != -1 ? '&' : '?') + dataParamName + '=' + URLEncoder.encode(data, "utf-8");
            service = appendQueryParameter(service, dataParamName, data.toString());

        // Open connection
        URL url = new URL("http", host, port, service);
        URL url = new URL("http", host, port, service.toString());
        URLConnection conn = url.openConnection();

        // Send data if not sent by parameter
        if (dataParamName == null && data != null) {
            conn.setDoOutput(true);
            if (data instanceof byte[]) {
                OutputStream out = conn.getOutputStream();
                out.write((byte[])data);
                out.close();
            } else if (data instanceof InputStream) {
                InputStream in = (InputStream)data;
                OutputStream out = conn.getOutputStream();
                byte[] buffer = new byte[1024];
                int bytes = in.read(buffer);
                while (bytes != -1) {
                    out.write(buffer, 0, bytes);
                    bytes = in.read(buffer);
                }
                out.close();
                in.close();
            } else { // Convert to string and send
                Writer out = new OutputStreamWriter(conn.getOutputStream(), "utf-8");
            out.write(data);
                out.write(data.toString());
                out.close();
            }
        }

        // Read result
        try {
@@ -113,19 +197,17 @@ public class TestHttpService {
    }

    /**
     * Test the REST service by calling two inserts and one search.
     * @param args ignored
     * @throws IOException if the REST service returned an error return code
     * Appends the HTTP query parameter to the given string.
     * @param input the string to append the parameter to
     * @param parameterName the parameter name
     * @param parameterValue the value of the parameter to append
     * @return the updated string
     * @throws UnsupportedEncodingException if the given parameter value cannot be converted to UTF-8 encoding
     */
    public static void main(String[] args) throws IOException {
        String host = "localhost";
        int port = 12345;
        String dataParamName = "data"; // data sent in GET parameter "data"; set to null to use POST transfer method
        String longData = createLongFloatVector(100);
        System.out.print("Inserting long data (" + longData.length() + " characters): ");
        System.out.println(processHttpRequest(host, port, "/insert?locator=1", dataParamName, longData));
        System.out.println(processHttpRequest(host, port, "/insert?locator=2", dataParamName, createLongFloatVector(100)));
        System.out.println(processHttpRequest(host, port, "/search?locator=&k=2", dataParamName, createLongFloatVector(100)));
    public static StringBuilder appendQueryParameter(CharSequence input, String parameterName, String parameterValue) throws UnsupportedEncodingException {
        StringBuilder str = input instanceof StringBuilder ? (StringBuilder)input : new StringBuilder(input);
        str.append(str.indexOf("?") != -1 ? '&' : '?');
        str.append(parameterName).append('=').append(URLEncoder.encode(parameterValue, "utf-8"));
        return str;
    }

}