2014/05/13

LocalServe

One of the things I have found irritating in the past is the need to install and configure a web-server each time the urge takes me to try something out. I don't run a local web-server permanently and being of a JFDI disposition the hurdle needed to get a server running is usually enough to stall what motivation I've managed to muster. Then I discovered that from Java 7 onwards it's fairly simple to implement your own web-server in plain Java - no need for an application server.

LocalServe implements two types of handlers:

1. File-handler -  This serves any content (and sub-directories) in the directory in which localserve is run. Any file which is not found returns a 404 and  any request for the root of a folder (path ending in "/") attempts to return the index.html file in the directory. Note that localserve does not provide any listing of directories.

If all you want to do is serve static content then the above is sufficient and LocalServe can be run using the command below in the directory you want to serve content from. This will run a webserver on port 8765 by default:

java -jar localserve.jar

The port number can also be changed by adding this to the end - e.g.:

java -jar localserve.jar 5678

2. SQL-handler - Often static content isn't enough and you need to use a database. This handler provides access to a database that can be called from JavaScript (typically via an AJAX request). A configuration file can be specified on the command line when running localserve. This configuration file provides details of a SQLite database, and the SQL statements that are supported. Each SQL statement has a logical name, SQL statement (including bindings), supported methods (POST or GET) and optionally a redirect (where to send the user on success). Calls to paths starting "/sql/" are sent to the SQL handler and the path element after this is used to match against a logical name in the configuration file. If found the SQL statement is executed with any HTTP parameters matching the bind names being bound accordingly. Two special names "PAGE" and "PAGE_SIZE" are defined such that queries which may return many many rows can be restricted to returning only certain pages of a certain size. Results from SQL commands are returned in JSON format.

The configuration file can be specified on the command line when running localserve as below:

java -jar localserve.jar config.json
or to run on a port other than 8765:
java -jar localserve.jar config.json 5678

(note that the position of these two parameters doesn't matter).

An example configuration file is shown below:
{ "wsm": {
"connString": "jdbc:sqlite:/path/to/database/file.db",
"driver": "org.sqlite.JDBC",
"operations": [
{ "path": "listbob",
"statement": "select name, id from bob",
"methods": "GET,POST" },
{ "path": "querybob",
"statement": "select id, name from bob where id={ID} order by name desc",
"methods": "GET,POST" },
{ "path": "insertbob",
"statement": "insert into bob (id, name) values ({ID}, {N})",
"redirect": "/sql/querybob",
"methods": "GET,POST" }
]
}
}

The database here contains one very simple table (BOB) as:

CREATE TABLE BOB (ID VARCHAR(20), NAME VARCHAR(100));


The only database used is SQLite and the main JAR file contains all libraries for this to work. I have tried other databases (notably IBM DB2) which worked fine so long as the JAR libraries can be found.

An example response to something like http://localhost:8765/sql/listbob looks like:
{ "dataset": 
{ "page": 0,
"pageSize": 20,
"record": [
{ "name": "James Brown", "id": "1"} ,
{ "name": "Simple Simon", "id": "2"} ,
{ "name": "Ducky Duncan", "id": "3"}
]
}
}

The attribute names are derived from the query names and are usually lower case. HOWEVER, you may find that if you explicitly state a column name the attribute may come back in uppercase (e.g. "select id||' - '||name LONG_NAME from bob" will result in an attribute with name "LONG_NAME").

Once you have the database setup and working then it's a relatively simple task to use JQuery to submit AJAX requests to the SQL handler to create/read/update/delete/list the database. A hastily knocked up example is below:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta charset="utf-8"/>

<script src="jquery-2.0.3.min.js"></script>
<script src="purl.js"></script>
</head>
<body>
...
<script type="text/javascript">

function query(searchId) {
$.ajax({ url: "/sql/querybob",
data: { "PAGE": 0, "PAGE_SIZE": 32000, "ID": searchId },
success: function( data ) {
var dataset = (JSON.parse(data)).dataset;
var arr = new Array();
for (var i=0;i<dataset.record.length;i++) {
alert(dataset.record[i].id + " " + dataset.record[i].name);
}
});
}
query(1);
</script>
</body>
</html>

Anyway, it's been useful when I just want to prototype something and this is definitely not intended for any production use. It's simply a very simple webserver so that I can quickly get on with prototyping something.

If you're interested then the source can be found on GitHub where the LocalServe JAR file can be downloaded. The code is what I call "prototype quality" which means it's been made to work by beating it into some shape with a hammer - it is uncommented and not of production quality.

Java 7 and this JAR are all you should need to run LocalServe. As ever, no assurances, warranties, guarantees etc. are provided and; whether you lose a little data or the world goes into meltdown (and everything in-between), I'll accept no responsibility for any damages caused...

No comments:

Post a Comment

Voyaging dwarves riding phantom eagles

It's been said before... the only two difficult things in computing are naming things and cache invalidation... or naming things and som...