Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Simple HTTP Server application that responds in JSON

I want to create a very simple HTTP server application in Java.

For example, if I run the server on localhost in port 8080, and I make to following call from my browser, I want to get a Json array with the string 'hello world!':

http://localhost:8080/func1?param1=123&param2=456

I would like to have in the server something that looks like this (very abstract code):

// Retunrs JSON String
String func1(String param1, String param2) {
    // Do Something with the params
    String jsonFormattedResponse = "['hello world!']";

    return jsonFormattedResponse;
}

I guess that this function should not actually "return" the json, but to send it using some HTTP response handler or something similar...

What it the simplest way to do it, without a need to get familiar with many kinds of 3rd party libraries that have special features and methodology?

like image 513
SomethingSomething Avatar asked Feb 17 '15 21:02

SomethingSomething


3 Answers

You could use classes from the package com.sun.net.httpserver:

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class JsonServer {
    private static final String HOSTNAME = "localhost";
    private static final int PORT = 8080;
    private static final int BACKLOG = 1;

    private static final String HEADER_ALLOW = "Allow";
    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    private static final Charset CHARSET = StandardCharsets.UTF_8;

    private static final int STATUS_OK = 200;
    private static final int STATUS_METHOD_NOT_ALLOWED = 405;

    private static final int NO_RESPONSE_LENGTH = -1;

    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String ALLOWED_METHODS = METHOD_GET + "," + METHOD_OPTIONS;

    public static void main(final String... args) throws IOException {
        final HttpServer server = HttpServer.create(new InetSocketAddress(HOSTNAME, PORT), BACKLOG);
        server.createContext("/func1", he -> {
            try {
                final Headers headers = he.getResponseHeaders();
                final String requestMethod = he.getRequestMethod().toUpperCase();
                switch (requestMethod) {
                    case METHOD_GET:
                        final Map<String, List<String>> requestParameters = getRequestParameters(he.getRequestURI());
                        // do something with the request parameters
                        final String responseBody = "['hello world!']";
                        headers.set(HEADER_CONTENT_TYPE, String.format("application/json; charset=%s", CHARSET));
                        final byte[] rawResponseBody = responseBody.getBytes(CHARSET);
                        he.sendResponseHeaders(STATUS_OK, rawResponseBody.length);
                        he.getResponseBody().write(rawResponseBody);
                        break;
                    case METHOD_OPTIONS:
                        headers.set(HEADER_ALLOW, ALLOWED_METHODS);
                        he.sendResponseHeaders(STATUS_OK, NO_RESPONSE_LENGTH);
                        break;
                    default:
                        headers.set(HEADER_ALLOW, ALLOWED_METHODS);
                        he.sendResponseHeaders(STATUS_METHOD_NOT_ALLOWED, NO_RESPONSE_LENGTH);
                        break;
                }
            } finally {
                he.close();
            }
        });
        server.start();
    }

    private static Map<String, List<String>> getRequestParameters(final URI requestUri) {
        final Map<String, List<String>> requestParameters = new LinkedHashMap<>();
        final String requestQuery = requestUri.getRawQuery();
        if (requestQuery != null) {
            final String[] rawRequestParameters = requestQuery.split("[&;]", -1);
            for (final String rawRequestParameter : rawRequestParameters) {
                final String[] requestParameter = rawRequestParameter.split("=", 2);
                final String requestParameterName = decodeUrlComponent(requestParameter[0]);
                requestParameters.putIfAbsent(requestParameterName, new ArrayList<>());
                final String requestParameterValue = requestParameter.length > 1 ? decodeUrlComponent(requestParameter[1]) : null;
                requestParameters.get(requestParameterName).add(requestParameterValue);
            }
        }
        return requestParameters;
    }

    private static String decodeUrlComponent(final String urlComponent) {
        try {
            return URLDecoder.decode(urlComponent, CHARSET.name());
        } catch (final UnsupportedEncodingException ex) {
            throw new InternalError(ex);
        }
    }
}

On a side note, ['hello world!'] is invalid JSON. Strings must be enclosed in double quotes.

like image 151
xehpuk Avatar answered Nov 16 '22 04:11

xehpuk


You could :

Install Apache Tomcat, and just drop a JSP into the ROOT project that implements this.

I second @xehpuk. It's not actually that hard to write your own single class HTTP server using just standard Java. If you want to do it in earlier versions you can use NanoHTTPD, which is a pretty well known single class HTTP server implementation.

I would personally recommend that you look into Apache Sling (pretty much THE Reference implementation of a Java REST api). You could probably implement your requirements here using Sling without ANY programming at all.

But as others have suggested, the standard way to do this is to create a java WAR and deploy it into a 'servlet container' such as Tomcat or Jetty etc.

like image 32
Richard Avatar answered Nov 16 '22 03:11

Richard


If you are already familiar with servlet you do not need much to create a simple server to achieve what you want. But I would like to emphasize that your needs will likely to increase rapidly and therefore you may need to move to a RESTful framework (e.g.: Spring WS, Apache CXF) down the road.

You need to register URIs and get parameters using the standard servlet technology. Maybe you can start here: http://docs.oracle.com/cd/E13222_01/wls/docs92/webapp/configureservlet.html

Next, you need a JSON provider and serialize (aka marshall) it in JSON format. I recommend JACKSON. Take a look at this tutorial: http://www.sivalabs.in/2011/03/json-processing-using-jackson-java-json.html

Finally, your code will look similar to this:

public class Func1Servlet extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String p1 = req.getParameter("param1");
    String p2 = req.getParameter("param2");

    // Do Something with the params

    ResponseJSON resultJSON = new ResponseJSON();
    resultJSON.setProperty1(yourPropert1);
    resultJSON.setProperty2(yourPropert2);

    // Convert your JSON object into JSON string
    Writer strWriter = new StringWriter();
    mapper.writeValue(strWriter, resultJSON);
    String resultString = strWriter.toString();

    resp.setContentType("application/json");
    out.println(resultString );
  }
}

Map URLs in your web.xml:

<servlet>
  <servlet-name>func1Servlet</servlet-name>
  <servlet-class>myservlets.func1servlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>func1Servlet</servlet-name>
  <url-pattern>/func1/*</url-pattern>
</servlet-mapping>

Keep in mind this is a pseudo-code. There are lots you can do to enhance it, adding some utility classes, etc...

Nevertheless, as your project grows your need for a more comprehensive framework becomes more evident.

like image 1
Rafa Avatar answered Nov 16 '22 02:11

Rafa