Putting it all together

To get started with your own Web Server based on routemaster, you need to do the following:

Step 1: The Dependencies

In order to get started with routemaster, you will need to include the dependencies:

By including the above dependency, the artefacts that this depends on (i.e. transitive dependencies) will also be retrieved.

Command line (i.e. all-in-one-jar)

If you are delivering an all in one executable jar file, for optimal experience, in the META-INF/MANIFEST.MF file you may point the Main-Class to synapticloop.nanohttpd.RouteMasterServer

Manifest-Version: 1.0
Main-Class: synapticloop.nanohttpd.RouteMasterServer

AND include a routemaster.properties file that configures the routemaster to your specifications.

You may wish to invoke your own class as the main entry point, in which case you will need to do the following:

public static void main(String[] args) {
	// The following will read and parse the properties files and set up all of the routes
	RouteMaster.initialise();

	// set up the default bindings for ports and hosts
	ServerRunner.executeInstance(new RouteMasterServer(host, port, rootDir, quiet));

	// at this point the routemaster is up and running
}

As part of a larger application

As per the above, you MUST call the RouteMaster.initialise(); and the ServerRunner.executeInstance(new RouteMasterServer(host, port, rootDir, quiet));. The executeInstance is a blocking call - so you will need to execute it in a separate thread if your application also performs other work.

Writing Servants

Skip the boring

Make full use of the in-built servants to stop duplication of effort, to serve up static resources from the filesystem use the StaticFileServant, and from the classpath, use either the ClasspathFileServant, or the CachingClasspathFileServant. These can be easily configured in the routemaster.properties file as follows:

route./static/*=synapticloop.nanohttpd.servant.StaticFileServant

or

route./static/*=synapticloop.nanohttpd.servant.ClasspathFileServant

or

route./static/*=synapticloop.nanohttpd.servant.CachingClasspathFileServant
Writing your own

Servants are the way to extend the functionality of the routemaster through Routing (either simple or RESTful). Simple Servants are included to reduce the drudgery of writing your own servants to serve up static content, either from the filesystem or from the classpath.

In this example, we will use the a RESTful Servant that adds some functionality to routemaster. We will create a simple Servant that extends RestRoutable that responds to either a GET or POST HTTP verb. Let us create the PostGetServant - create a new class:

package synapticloop.nanohttpd.example.servant;

import java.util.List;

import synapticloop.nanohttpd.router.RestRoutable;

public class PostGetServant extends RestRoutable {

	public PostGetServant(String routeContext, List<String> params) {
		super(routeContext, params);
	}
}

Now we need to override the methods that we want to implement

	@Override
	public Response doGet(File rootDir, 
			IHTTPSession httpSession, 
			Map<String, String> restParams, 
			String unmappedParams) {
		return super.doGet(rootDir, httpSession, restParams, unmappedParams);
	}

	@Override
	public Response doPost(File rootDir, 
			IHTTPSession httpSession, 
			Map<String, String> restParams,
			String unmappedParams) {
		return super.doPost(rootDir, httpSession, restParams, unmappedParams);
	}

This is in itself does not provide any functionality, and just chains the call to super which will invoke HttpUtils.methodNotAllowedResponse(). So let's get started with investigating the restParams and unmappedParams for the doGet() method.

But first we need to update the routemaster.properties file to bind the new route. This will be a simple one liner:

rest./postget/%title%/=synapticloop.nanohttpd.example.servant.PostGetServant

Run the RouteMasterServer and in the console output you will see that your new routes have been mapped.

[INFO]:  +--------------------------+----------------------------------------------------------------+
[INFO]:  |  rest: /postget/%title/  | synapticloop.nanohttpd.example.servant.PostGetServant@76ed5528 |
[INFO]:  |  rest: /postget/%title/* | synapticloop.nanohttpd.example.servant.PostGetServant@2c7b84de |
[INFO]:  +--------------------------+----------------------------------------------------------------+

Let's add some functionality to write out the mapped %title% parameter:

	@Override
	public Response doGet(File rootDir, 
			IHTTPSession httpSession, 
			Map<String, String> restParams, 
			String unmappedParams) {

		return(HttpUtils.okResponse("text/html", restParams.get("title")));
	}

Now when you hit the URL http://localhost:5474/postget/something/, the output will show something, similarly if you change the something to something a little bit different (i.e. http://localhost:5474/postget/something a little bit different/) the mapped rest parameter will be output.

Similarly, if you change the post method:

	@Override
	public Response doPost(File rootDir, 
			IHTTPSession httpSession, 
			Map<String, String> restParams,
			String unmappedParams) {

		return(HttpUtils.okResponse("text/html", restParams.get("title")));
	}

And test it with curl -X POST http://localhost:5474/postget/something/, you will get the output of something.

Going further with the other HTTP verbs: curl -X DELETE http://localhost:5474/postget/something/, you will get a 405 Method Not Allowed response.

The final Servant is:

package synapticloop.nanohttpd.example.servant;

import java.io.File;
import java.util.List;
import java.util.Map;

import fi.iki.elonen.NanoHTTPD.IHTTPSession;
import fi.iki.elonen.NanoHTTPD.Response;
import synapticloop.nanohttpd.router.RestRoutable;
import synapticloop.nanohttpd.utils.HttpUtils;

public class PostGetServant extends RestRoutable {

	public PostGetServant(String routeContext, List params) {
		super(routeContext, params);
	}

	@Override
	public Response doGet(File rootDir, 
			IHTTPSession httpSession, 
			Map restParams, 
			String unmappedParams) {

		return(HttpUtils.okResponse("text/html", restParams.get("title")));
	}

	@Override
	public Response doPost(File rootDir, 
			IHTTPSession httpSession, 
			Map restParams,
			String unmappedParams) {

		return(HttpUtils.okResponse("text/html", restParams.get("title")));
	}
}

Conclusion

There really is not a lot more to routing and routemaster. Generally, what is required is a templating engine to enable rapid development and give the ability to cut up a page. To this end - you can include the templar templating engine:

Handler Templar

For a very quick example of integrating the light-weight templating engine, add the following lines to the routemaster.properties file:

route./*=synapticloop.nanohttpd.servant.StaticFileServant

handler.templar=synapticloop.nanohttpd.handler.TemplarHandler

When the server is started you will see the handler registered for all files that match *.templar.

[INFO]:  +--------------------------------------------------------------------+
[INFO]:  | Handlers                                                           |
[INFO]:  +-----------+--------------------------------------------------------+
[INFO]:  | extension | handler class                                          |
[INFO]:  +-----------+--------------------------------------------------------+
[INFO]:  | templar   | synapticloop.nanohttpd.handler.TemplarHandler@24d46ca6 |
[INFO]:  +-----------+--------------------------------------------------------+
NOTE that you need to define a route before you can invoke any handlers.

Three files have been created

src/example/index.html.templar

The above contents just statically includes the following two files.

src/example/head.templar

src/example/body.templar


{static src/example/head.templar}
{static src/example/body.templar}

If you run the routemaster server and navigate to http://localhost:5474/src/example/index.html.templar the templar handler will automatically parse and render the file.

If you also add in the indexfiles option to the routemaster.properties file you can just navigate to http://localhost:5474/src/example/ and the templar handler will serve up the file

option.indexfiles=index.html.templar,index.html,index.htm

Debugging

To debug what is going on, the best place to start the process is in the synapticloop.nanohttpd.router.RouteMaster file at the private static Response routeInternal(File rootDir, IHTTPSession httpSession) method.