Index

javalightning

This is an experimental web framework by me, the documentation for my own use.

Hello World

add libs
* toolkit.jar
* javalightning.jar
create web.xml
/WEB-INF/web.xml:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="eriklievaart" version="3.0">

	<filter>
		<filter-name>MvcFilter</filter-name>
		<filter-class>com.eriklievaart.javalightning.filter.MvcFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>MvcFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

</web-app>
create your first url mapping
/WEB-INF/mvc/route.txt
GET / com.example.HelloWorld#get
create backing bean
package com.example;

import com.eriklievaart.javalightning.api.ResponseBuilder;
import com.eriklievaart.javalightning.api.render.StringRenderer;

public class HelloWorld {
	public void get(ResponseBuilder response) {
		response.setRenderer(new StringRenderer("It Works!"));
	}
}
open browser @ localhost:8080

URL mapping in route.txt

every entry in this file has 3 whitespace separated entries([METHOD] [PATH] [COMMAND]).

specify a fully qualified path a hash '#' and the method name to indicate a controller method to call as in the hello world example:

GET / com.example.HelloWorld#get
You can specify any HTTP method, so the following example will call HelloWorld.get on a HTTP POST.
POST * com.example.HelloWorld#get
It is possible to use wildcards for the first two fields, the following will pass ALL GET requests to HelloWorld#get:
GET * com.example.HelloWorld#get
If no URL without wildcard matches, then the entries will be processed in order of entry, so the second wildcard will never be called here:
GET /foo/*     com.example.HelloWorld#foo
GET /foo/bar/* com.example.HelloWorld#bar
Use * as command to have an url processed by tomcat normally:
GET /static/test.html *
To process everything in /static normally
GET /static/* *
add the following line to the end of route.txt to have all unmapped urls processed normally by tomcat
* * *

Controllers

The method name referred to from route.txt must be unique for the class.Parameters are injected dynamically from amongst the following:
com.eriklievaart.javalightning.api.ResponseBuilder
com.eriklievaart.javalightning.api.Parameters
com.eriklievaart.javalightning.api.FilterContext;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
The easiest way to return a response is using the StringRenderer:
response.setRenderer(new StringRenderer("It Works!"));
By default the FreemarkerRenderer is used, set the view name to load the template (requires freemarker jar on build path)

For example, to load "/WEB-INF/freemarker/render.tpl":

public void get(ResponseBuilder response) {
	response.setView("render");
}
Use DownloadRenderer to upload a file to the client browser
public void upload(ResponseBuilder response) throws FileNotFoundException {
	response.setRenderer(new DownloadRenderer(new File("/tmp/file.txt")));
}
Client side redirect (tell the client browser to perform a redirect):
public void external() {
	throw new ExternalRedirectException(new UrlMapping("home", "/"));
}
Server side redirect (resolve another result internally and push the results to the client)
public void internal() {
	throw new InternalRedirectException(new UrlMapping("home", "/"));
}
Use ServletReponseRenderer for custom behavior

Configuration

the main configuration.ini file is loaded from the "/data" directory.This can be changed with a init param for the MvcFilter
<init-param>
         <param-name>mvc.config.dir</param-name>
         <param-value>/data</param-value>
     </init-param>
add the following to [mvc.config.dir]/config.ini to override the freemarker settings.
config[freemarker]
	template.dir=/data/freemarker
	refresh=0
The refresh setting is freemarker's template update delay in milliseconds (see the freemarker documentation).The template.dir property can be used as an override for loading templates.The defaults are production settings with a 10 second refresh timeout and templates are loaded from the classpath only.Overriding both settings allows zero turnaround for freemarker template changes.

Aspects

javalightning makes it possible to create aspects in two simple steps.First we need a java class that implements the AspectController interface:
package com.example;

import java.io.IOException;

import javax.servlet.ServletException;

import com.eriklievaart.javalightning.api.FilterContext;
import com.eriklievaart.javalightning.api.aspect.AspectController;
import com.eriklievaart.javalightning.control.RequestController;

public class Aspect implements AspectController {

	private RequestController delegate;

	@Override
	public void invoke(FilterContext context) throws IOException, ServletException {
		long start = System.currentTimeMillis();
		delegate.invoke(context);
		long spent = System.currentTimeMillis() - start;
		System.out.println("Total time spent on request in millis: " + spent);
	}

	@Override
	public void setDelegate(RequestController delegate) {
		this.delegate = delegate;
	}
}
next we need to add a line to "/WEB-INF/mvc/aspects.txt" in the same syntax as used when creating a route:
* * com.example.Aspect
Now the aspect will be called for all configured paths. Only paths filtered by the MvcFilter will be affected.Aspects are processed in order of occurrence in "aspects.txt".Remember to invoke the delegate in the aspect or the request will not be processed further.

Documentation todo's:

/WEB-INF/mvc/init.txt

antastic config

src/main/config/antastic.properties
webserver.root.dir=/path/to/apache-tomcat-8.0.38

src/main/config/dependencies.txt
main-compile toolkit
main-compile javalightning

Javalightning OSGI version

routes

The most basic example of a route (the PageService must be registered as an OSGI service in the activator):
public class SimplePageService implements PageService {
	@Override
	public String getPrefix() {
		return "prefix";
	}
	@Override
	public Route[] getRoutes() {
		return new Route[] { new Route("example", EnumSet.of(RouteType.GET), () -> new StringRendererController()) };
	}
}
associated controller
public class StringRendererController implements PageController {
	@Override
	public void invoke(ResponseBuilder builder) {
		builder.setRenderer(new StringRenderer("hello<br/><br/>" + new Date()));
	}
}
accessing the resulting page
firefox localhost:[port]/mvc/prefix/example
wildcard capture
new Route("wildcard/*", EnumSet.of(RouteType.GET), () -> new StringRendererController())
rendering a (freemarker) template
public void invoke(ResponseBuilder response) {
	response.getModel().put("date", new Date());
	response.setRenderer(new TemplateRenderer("/example/freemarker.tpl"));
}
performing an internal redirect
public void invoke(ResponseBuilder builder) {
	throw new InternalRedirectException(new UrlMapping("internal", "/mvc/prefix/example"));
}
performing an external redirect
public void invoke(ResponseBuilder builder) {
	throw new ExternalRedirectException(new UrlMapping("github", "http://www.github.com"));
}
dependency injection
public class BeanController implements PageController {
	
	@Bean
	private HttpServletRequest request;
	@Bean
	private HttpServletResponse response;
	@Bean
	private RequestContext context;
	@Bean
	private HttpSession session;
	@Bean
	private Parameters parameters;

	@Override
	public void invoke(ResponseBuilder rb) {
....

Templates

making (freemarker) template available in the activator
private ServiceRegistration<TemplateSource> templates;
@Override
public void start(BundleContext context) throws Exception {
	ClasspathTemplateSource templateSource = new ClasspathTemplateSource(getClass(), "[prefix]");
	templates = context.registerService(TemplateSource.class, templateSource, new Hashtable<>());
}
@Override
public void stop(BundleContext context) throws Exception {
	templates.unregister();
}
global variables are exposed through the TemplateSource
ClasspathTemplateSource templateSource = new ClasspathTemplateSource(getClass(), "[prefix]");
templateSource.addGlobal("date", () -> new Date());
referencing the global in the (freemarker) template
${globals.get("date")?time}
referencing a HTTP parameter in the template
${parameter.get('key')}
${parameter.get('key', 'default')}
getting the remote url of a route
${lightning.getRemotePath('service', 'id')}

osgi properties for configuring the framework (defaults after '=')

path pattern to serve with the javalightning servlet
com.eriklievaart.javalightning.bundle.servlet_pattern=/mvc/*
com.eriklievaart.javalightning.freemarker.timeout=60000
specify diretory containing freemarker templates (for real time editing during development)
com.eriklievaart.javalightning.freemarker.path=
redirect incoming requests on root to home path
com.eriklievaart.javalightning.bundle.home=/web/
redirect favicon
com.eriklievaart.javalightning.bundle.favicon=/web/static/favicon.ico
remote URL used for generating links
com.eriklievaart.javalightning.bundle.host=localhost:8000

antastic config

[bundle]
javalightning-bundle
javalightning-freemarker
osgi-toolkit
toolkit-convert
toolkit-io
toolkit-lang
toolkit-logging
toolkit-reflect
freemarker org.freemarker 2.3.28
org.apache.felix.http.api org.apache.felix 3.0.0
org.apache.felix.http.jetty org.apache.felix 4.0.6
org.apache.felix.http.servlet-api org.apache.felix 1.1.2

[provided]
org.apache.felix.framework org.apache.felix 5.6.10