jReflectServer 2.0

jReflectServer is a very small & lightweight java web-server / webservice-framework for creating pure java web applications. It can

  • ... forward web requests to a cluster of server nodes using a single code base and
  • ... dynamically load distributed webpages from remote jars using reflection.

Features

  • Super easy-to-use
    • Straight-forward, programmatic page generation (no java and html code mixed)
    • Use standalone or embedded
    • No configuration file hacking
  • Super easy to scale up
    • Launch multiple server instances which register at a main server
      • The main server randomly forwards requests to the cluster
    • Use a distributed session for storing objects
    • No special code needed
    • Based on non-blocking connections
  • Super easy-to-deploy
    • Reflection-based, distributed deployment at runtime
      • Place parts of your web application on different servers
      • Automatic modification detection and class reloading
    • Access resources inside jars as if they were in document root (simple get requests with path to file)
    • No J2EE needed
    • Single all-in-one jar (~ 1 MB size)
  • HTTP 1.1 spec compliant, supports MIME types
  • Supports (distributed) session management
  • Supports routing through annotations
  • Supports file uploads
  • Supports SSL/TLS
  • Supports HTTP gzip compression

Distributed under MIT license.

jReflectServer is based on proven components like HttpCoreFileUpload and jsoup.

Use cases

  • Projects with little presentation- but extensive business-logic
    • Writing generic REST-based web services
  • Projects that need to be scaled up in terms of server processing power
    • Distributed requests ensure an arbitrary amout of server processing power in peak times
  • Projects involving limited hardware constraints
    • Perfect for embedded harware (like the raspberry pi) due to small footprint

You should not use jReflectServer alone if you expect your project to grow into a huge website that needs organized content! You can however use it as an addition to any existing system.

Download

Download from sourceforge project page.

Tutorial and user guide

Learn using jReflectServer within minutes. This tutorial is split into two parts, a webservice part and an html part

Each page is generated by a java class implementing the WebPage interface (i.e. the generatePage method which has a request and a response parameter).

Writing REST services

This is a first REST-based "hello world" example:

public class FirstPage implements WebPage {
   
   @Override
   @Route("MyPage")
   public void generatePage(JReflectServer server, WebRequest request, WebResponse response) throws Exception {
       reponse.text(200, "Hello World!");
   }
   
}

 

How to run this in standalone mode

  1. Create a new Java project using Eclipse or Netbeans. Reference jReflectServer_2.0.jar.
  2. Create the class FirstPage.java shown above. Compile and package your project in a resulting jar file named helloworld.jar.
  3. Start in standalone mode using the command "java -jar jReflectServer_2.0.jar helloworld.jar -local:http://127.0.0.1:80,id_123".

Finally, direct your browser to http://localhost/MyPage.

See more REST code snippets here!

Writing dynamic HTML pages

  • Each page is generated by a java class implementing the WebPage interface (i.e. the generatePage method which has a request and a response parameter).
  • A response instance contains a WebDocument which defines the dynamic html content.
  • You can access the head-, body- and CSS- sections by getHead(), getBody() and getStyle() and construct the content.
  • View pages in the browser
    • By default, the page is reachable by a path that contains the full class name (e.g. http://localhost/[package.subpackage.Class]). A custom route can be defined by the @Route annotation (see example below).

 

Hello HTML

This is a first HTML-based "hello world" example:

 

public class FirstPage implements WebPage {
   
   @Override
   @Route("MyPage")
   public void generatePage(JReflectServer server, WebRequest request, WebResponse response) throws Exception {
       WebDocument wd = response.getWebDocument();
       wd.getBody().append("<h1>Hello World!</h1>");
   }
   
}

How to run this (same as before)

  1. Create a new Java project using Eclipse or Netbeans. Reference jReflectServer_2.0.jar.
  2. Create the class FirstPage.java shown above. Compile and package your project in a resulting jar file named helloworld.jar.
  3. Start in standalone mode using the command "java -jar jReflectServer_2.0.jar helloworld.jar -local:http://127.0.0.1:80,id_123".

Finally, direct your browser to http://localhost/MyPage.

Hello World with style

Let's extend the example with some CSS:

wd.getStyle().append("h1 { color:red; font-size:48px; }");

See more HTML code snippets here.

Modes and Basics

JReflectServer can be used in embedded mode (programmatically) and in standalone mode (as shown before).

 

Embedding jReflectServer

Create the following class JReflectTest.java as part of the same project which contains FirstPage.java:

public class JReflectTest {
 

   private final JReflectServer server = new JReflectServer("http://127.0.0.1:1234");


   public JReflectTest() {

       server.launchServer(this);
   }
   
   public static void main(String[] args) {
       new JReflectTest();
   }
 
}

The above code creates a new server instance on port 80. Again, direct your browser to http://localhost/MyPage.

So, where is the reference to FirstPage? There is none, but jReflectServer is able to scan the binary code source at runtime without prior knowledge of any classes implementing WebPage. You only need to pass one arbitrary instance which is part of the same code source, in the case above passing "this" to launchServer.

A request listener can be shut down using:

server.shutdownListener(80);

 

Adding remote pages (programmatically)

Here's how you reference more jars using the embedded mode:

server.register("file:///home/some/folder/dynamicpages.jar");

 

No need for your implementation to be in the same file system. The following shows how to use a remote code base:

server.register("http://your.server.com/dynamicpages.jar");

 

The default update cycle time for jar files is 60 seconds. However, local jar files which are replaced should be reloaded immediately. You can change the update cycle time (in seconds) like this:

server.setDependencyUpdateCycle(30);

 

Adding remote pages (standalone mode)

Let's say you'd like to run jReflectServer on a dedicated web server but keep working on your hello world application without copying files after each build onto the server. Just run the command above from your dedicated server's shell but place the helloworld.jar on your home server (you can use jReflectServer as file server).

Try the following command: "java -jar jReflectServer_2.0.jar http://home.somedyndns.org/helloworld.jar -local:http://127.0.0.1:80,id_123 -update:30". jReflectServer checks by default every 30 seconds for modifications. When you update your jar, it will be reloaded automatically on the dedicated server.

 

File organization and javascript integration

jReflectServer transparently loads arbitrary files from the document root folder and from inside the code location (file system or jar). You may place java files side-by-side with html, js or any other files and reference these by simple relative file paths.

Example structure:

  • src
    • mypackage
      • Sample.java: uses response.initFrom("mypackage/sample.html")
      • sample.html: references javascript by <script src="js/somelib.js"/>
    • js
      • somelib.js: some external javascript library

Deploying sources (java / class, js and html) as single jar will work perfectly fine when running it with jReflect.

 

Distributed request execution

As a reminder: No special code is needed to handle distributed code execution on remote server nodes. Using a distributed session which is synchronized with the "master" server, the code can be assumed to work on all nodes.

To create a server cluster, just start a master server node. Such a node is a normal server node which get's a unique server ID so that the slave server nodes can register at the master server:

JReflectServer master = new JReflectServer("http://127.0.0.1:1234", "id_ee0c4d16");

master.lauchServer(this);

 

The ID can be any unique string, e.g. a UUID. Now, create a number of slave server nodes and register at the master node using the IP address and the ID:

JReflectServer slave1 = new JReflectServer("http://127.0.0.1:1235", "id_slave_01_abc");

slave1.setMasterHost("http://127.0.0.1:1234", "id_3ebd9e13");

slave1.lauchServer(this);

JReflectServer slave2 = new JReflectServer("http://127.0.0.1:1236", "id_slave_02_xyz");

slave2.setMasterHost("http://127.0.0.1:1234", "id_3ebd9e13");

slave2.lauchServer(this);

 

That's pretty much it. Note the different port numbers used in this example for testing 3 servers on the same machine (1235 and 1236). Any generatePage implementation will now be executed randomly on the master or on one of the slave nodes.

You can see the results be printing out the server ID on the request:

public void generatePage(JReflectServer server, WebRequest request, WebResponse response) throws Exception {
    System.out.println("Called on: " + server.getId());
}

 

Besides programmatically lauching multiple server instances, you can do this also in standalone mode from the command-line, e.g.:

  • java -jar jReflectServer_2.0.jar hellodist.jar -local:http://127.0.0.1:1234,id_ee0c4d16
  • java -jar jReflectServer_2.0.jar hellodist.jar -local:http://127.0.0.1:1235,id_slave_01_abc -master:http://127.0.0.1:1234,id_ee0c4d16
  • java -jar jReflectServer_2.0.jar hellodist.jar -local:http://127.0.0.1:1236,id_slave_02_xyz -master:http://127.0.0.1:1234,id_ee0c4d16

See a distributed code execution example here.

Some more code snippets...

GZIP compression

Be nice to low bandwidth connections. Setting the gzip flag will compress the http response:

response.setGzip(true);

 

Note that the response will only be compressed if the request header field "Accept-Encoding" contains the entry "gzip".

Exception handling (printing out the stack trace)

Any exception thrown in the generatePage method will produce an Internal Server Error (500) as response. If the exception is a WebPageException the response will contain a stack trace which can be viewed in the browser. If stack traces should always be visible, the best practice is to catch all exceptions and rethrow a WebPageException:

public void generatePage(JReflectServer server, WebRequest request, WebResponse response) throws Exception {
   try {
       ...
   } catch (Exception e)
   {
       throw new WebPageException("Error", e);
   }
}

 

SSL/TLS support

SSL/TLS can be enabled by providing an SSLContext instance to the jReflectServer.

SSLContext sslContext = SSLContext.getInstance("Default");
...
JReflectServer.getInstance().launchListener(this, sslContext, 443);

Dependencies

jReflectServer depends on the following components (all integrated):

jsoup MIT License
HttpCore Apache License 2
FileUpload Apache License 2
servlet-api (as part of tomcat core) Apache License 2