Guide Tutorial: How to make a web server with Java easy, simple and with few lines of code.

No complicated frameworks. No Java EE, GlassFish, JAX, etc.

This is a guide to show 2 ways to easily run a web server or file server using Java without using Java EE and a ton of frameworks and overhead. The KISS principle and minimal code is the idea of this approach. If you don’t need a huge framework to do a thing you want to do, it’s not always the best idea to have all that complexity and overhead. Also you might not want to install all that or learn it. This code is very simple. It can also be used on Android.


There are 2 approaches here. The first only uses the Java JDK, the second one uses one library for added functionality.


NOTE: Attached to this article is an item that is uploaded here on PieceX.

You can find it here, for free: https://www.piecex.com/products/Easy-Java-Web-Server-No-Java-EE-needed-no-JAX-no-GlassFish-542

The com.sun.net.httpserver approach

This approach uses classes located inside com.sun. In general using these packages is discouraged because they are not part of the general JDK spec and could in theory be changed or removed. However this is likely not going to happen with this package as it has been there for a very long time and needs no changing. If you would still be uncomfortable with the idea that future version might not work with this, look at the second option.


In this example we are going to use the following imports:

import com.sun.net.httpserver.HttpExchange;

import com.sun.net.httpserver.HttpHandler;

import com.sun.net.httpserver.HttpServer;


These are the imports we will be using.

First let’s look at the very basic starting of the server. Which is indeed very simple:

HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);

server.createContext("/some_route", new HandleSomeRoute());

server.setExecutor(null);

server.start();


The first line creates the HttpServer instance into a variable, as you can see.Here you can specify an IP address to run the server from, the port and a socket backlog. We just set the port to 8000 in this example.


The createContext method is used for routes. Every route you want to cover, that should be reachable, has to be registered here with this command. You can use this command as many times as you want to register many routes. Every route needs a handling class which defines what code should run when a request is incoming at this specific route. In that class you do the rest, we will look at this in a moment.


Next is the Executor which is here set to default. You may set a task scheduler, queue, load balancing, prioritizer, threading logic and so on. The default is to run every task as it comes instantly.


Finally we just start the server using all the setup we just did before.
And now the server is listening to the port you set it to, at localhost. So if you open a web browser pointing to http://localhost:8000 it should work, but nothing will happen at all, so it might be hard to tell for now.


Let’s look at how to respond to an incoming request for a specific route:

static class HandleSomeRoute implements HttpHandler

{

       @Override

       public void handle(HttpExchange he) throws IOException

       {

             String response = "<h1>This is the /some_route. Hi there.</h1>";

             he.sendResponseHeaders(200, response.length());

             OutputStream os = he.getResponseBody();

             os.write(response.getBytes());

             os.close();

             he.close();

       }

}

We registered the route “/some_route” with this class. Here you can see we implement the interface HttpHandler and as such override the method called handle(HttpExchange). In there we define what should happen when we get any request on this route.

In the body of this method we write code that will push a response. The response is just a hello message. The sendResponseHeaders makes it possible to set the response status code.


We do want to know what request method was used: GET, POST or others. And we want to know the parameters supplied and other information. All that information is included in the HttpExchange variable which is supplied:



System.out.println("REQUEST METHOD: "  + he.getRequestMethod());

System.out.println("REQUEST URI: "  + he.getRequestURI());

Here is how to get the URI from the request as well as the method used, which is pretty vital information.


Now if you want to access the parameters it depends whether or not we are talking about GET or POST.


In case of GET it’s simply part of the requestURI encoded with ? and & as you would expect.

For POST you use:

he.getRequestBody()


Again its encoded here. If you want convenient methods for getting a hashmap for either type of parameters, download the attached item and check the source code which has these two methods:


getPOSTParamsFromRequest(...)

getGETParamsFromRequest(...)


Check the code for that example to see how it is used.


And basically that is all. You start the server, configure things, set routes, make classes for routes. Inside the handle function you write your code. You check which method was used and so on.



Second Option. Using NanoHttpd


For the other  option we will be using NanoHttpd, which can be found here:

https://github.com/NanoHttpd/nanohttpd


Please read the instructions on how to install everything you need in order to get the library to work. Which is very simple. Just Gradle or maven and import it as a dependency as per usual. You can also just use the item uploaded on piecex.com and just work with it.

First, your server class has to extend the class NanoHTTPD, which will serve as the main core class.


public class LiteServer extends NanoHTTPD


Now we need a constructor:

public LiteServer() throws IOException

   {

             Int port = 8080;

       super(port);

       start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);

       System.out.println("\nRunning a t http://localhost:"+port+"/ \n");

   }



This is how you initialize the server with a specific port.

We need to call the constructor of the parent class, as such we have to use super here in the constructor on the first line with the port.


Once you have done that, all you need is to initialize your class. I called mine LiteServer. Which is from the item you can download.

new LiteServer();


Next we need to implement and override a method from the super class called:

@Override

public Response serve(IHTTPSession session)

{

}

As you guessed it, this method will handle all incoming requests.

You just use the IHTTPSession variable to access all the data you need.


Note: On contrast to the other code, we did not register any routes here. All routes will come through this method.


In order to retrieve the requests headers, its method and the Uri, use the following methods:

session.getHeaders();

session.getMethod();

session.getUri();


Check for the method and then handle GET or POST or any method like you see fit.

To get the GET parameters you can simply call:

Map<String, List<String>> params = session.getParameters();

Which delivers a map which you can easily traverse.


To get the POST parameters you first have to establish that the method was indeed POST. Then you need to call session.parseBody

Map<String, String> files = new HashMap<String, String>();

session.parseBody(files);

session.getQueryParameterString()


If you download the item for this article, you will find a convenient method called:

queryParamStringToMap

With this you can easily pull the parameters out into a hashmap for easy use.



Comments

Add your comment

user-symbol

Stay in touch

Get Practical Tips For Business and Developers.

Learn about PieceX Community Needs for Source Code Projects.

Be the First to Know PieceX Newest Free Community Code Projects.