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:
routemaster.jar
, this is the jar which resides in the Bintray Maven repository
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:
templar.jar
, this is the jar which resides in the Bintray Maven repository
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.