Skip to content

Instantly share code, notes, and snippets.

@javajosh
Created February 13, 2010 22:49
Show Gist options
  • Save javajosh/303707 to your computer and use it in GitHub Desktop.
Save javajosh/303707 to your computer and use it in GitHub Desktop.

Hi, this is the Lightweight Front Controller concept in JSP form.

Requirements: Java Container with JSP & JSTL, jquery + jquery ui. A browser. Note that both the Google App Engine and Glassfish should be able to run this out-of-the-box.

What it does: It makes the creation of endpoints easy. You don't have to make a new file. You don't have to modify a configuration file. You can write the client to the endpoint right next to the endpoint definition. Because it's JSP, this means you can add and modify endpoints on-the-fly without a recompile, and without restarting the container.

How to use it: write your HTML as normal. Put the Endpoint class scriptlet at the top, and the Master endpoint at the bottom (see the example file). When you need an endpoint, create a new endpoint class like this:

<%
	new Endpoint("happy") {
		public void run(){
			result = "happy times, the endpoint returned";
		}
	};
%>

By default the constructor will emit a button into the HTML page. You can turn this off by adding false to the constructor (e.g. "new Endpoint("happy", false)..."). Note the use of "result" to indicate a result. Yes, this is totally ghetto but there are no good interfaces that ship with Java that are like Runnable but return a String, and I wanted zero Java dependencies for the LFC.

The client-code to consume the response is up to you. If you use the emitted button, you can write a JavaScript function with the same name as the Endpoint, which will be executed when the response gets back to the client, like so:

<script>
	function happy(response){
		alert("Cool, got this from the server: "+response);
	}
</script>

How it works: as you may know, a JSP is basically a Servlet with a lot of literal text. Everything that occurs occurs within the doGet method. When the page is entered with no parms, it acts like a normal JSP. When the page is entered with an "exec" parm, it functions like an Ajax endpoint. The line at the end throws away everything the JspWriter has accumulated, and then prints out the result of the named Endpoint. Some of the stuff at the top are just Java-language-lawyer contortions required by the fact that Endpoint is a local class - for example, you can't use statics and you can't refer to non-final local variables in local classes.

TODO: In truth, the example could be substantially cleaned up. Because JSTL is notoriously difficult to install, I should probably remove all of that. But JSTL is really nice for supporting nicely formatted XML and SQL strings. Feel free to remove this. All the jQuery stuff is also not necessary - it just makes things look nicer. I'll pull out the emission into an overridable method and remove the jquery dependencies.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page import="java.util.*,java.net.*,java.io.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Lightweight Front Controller (LFC)</title>
<script src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1");
</script>
<style>
body{font: 12px sans-serif}
.data {display:none}
</style>
</head>
<body>
<%
final Map<String,Runnable> endpoints = new HashMap<String,Runnable>(); //simulate static
final HttpServletResponse res = response; final HttpServletRequest req = request; final PageContext ctx = pageContext; //inner class silliness.
final JspWriter o = out;
class Endpoint implements Runnable{ //in truth I'd like a better interface, something that returns a string. Oh well.
protected String result;
Endpoint(String name){
this(name, true);
}
Endpoint(String name, boolean emitButton){
endpoints.put(name,this);
if (!name.equals("Master") && emitButton){
try{
o.print(html(name));
} catch (IOException ignored){}
}
}
/** by default produces a button that, on successful ajax, calls a JavaScript
function of the same name as the endpoint.*/
protected String html(String name){
return String.format("<input onClick=\"$.get('?exec=%s',{},function(response){%s(response);});\" type=\"button\" name=\"%s\" value=\"Exec %s\" id=\"%s\" />", name, name, name, name, name);
}
public void run(){} //might emit javascript to call all endpoints :)
public boolean runEndpoint(){
String exec = req.getParameter("exec");
Endpoint test = (Endpoint) endpoints.get(exec);
if (test!=null){
test.run();
} else {
return false;
}
res.reset();
try{
res.getWriter().print(test.result);
res.getWriter().close();
} catch (IOException ignored){}
return true;
}
}
%>
<div class="widget xml">
<h2>Basic XML Response</h2>
<p>An example of asking the server for some XML, parsing the response, and manipulating the DOM to show it.</p>
<div class="data"></div>
<script>
function XML(response){
$('.xml .data').text($('bar', response).text());
$('.xml .data').fadeIn('slow');
}
</script>
<%
new Endpoint("XML") {
public void run(){
result = "<foo>2<bar>4</bar><baz>8</baz></foo>"; //note: it's nicer to specify XML with JSTL
}
};
%>
</div>
<div class="widget clock" id="clock1">
<h2>Clock</h2>
<p>An example of executing "real code" on the server. In this case, <code>System.currentTimeMillis()</code></p>
<div class="data">
<ol>
<li title="time in milliseconds since epoch" class="time"></li>
</ol>
</div>
<script>
function Clock1(response){
$('#clock1 .time').text(response);
$('#clock1 .data').fadeIn("slow");
}
</script>
<%
new Endpoint("Clock1") {
public void run(){
result = "" + System.currentTimeMillis();
}
};
%>
</div>
<div class="widget gps" id="Gps1">
<h2>GPS</h2>
<p>An example of an arbitary protocol, in this case semi-colon separated</p>
<div class="data">
<ol>
<li title="latitude in degrees" class="lat"></li>
<li title="longtitude in degrees" class="lon"></li>
<li title="altitude in meters" class="alt"></li>
</ol>
</div>
<script>
function Gps1(response){
var values = response.split(";");
var lat = values[0];
var lon = values[1];
var alt = values[2];
$('#Gps1 .lat').text(lat);
$('#Gps1 .lon').text(lon);
$('#Gps1 .alt').text(alt);
$('#Gps1 .data').fadeIn("slow");
}
</script>
<%
new Endpoint("Gps1") {
public void run(){
//TODO: connect this to a real GPS
result = "1;2;3";
}
};
%>
</div>
<h2>Camera</h2>
<div class="widget camera" id="Camera1">
<p>An example of returning "complex" data to the client; in this case an image, both as a
relative URL and as base64 encoded text. Oddly, the JDK doesn't have a base64 decode function,
so it's omitted.</p>
<div class="data">
<ol>
<li title="display an image" class="image"><img src=""></img></li>
<li title="image encoded in base64" class="base64"></li>
</ol>
</div>
<script>
function Camera1(response){
var values = response.split(";");
var imgUrl = values[0];
var imgEncoded = values[1];
$('#Camera1 .image img').attr('src', imgUrl);
$('#Camera1 .base64').text(imgEncoded);
$('#Camera1 .data').fadeIn("slow");
}
</script>
<%
new Endpoint("Camera1") {
public void run(){
//TODO: real base64 encoding of subject image
result = "test.gif" + ";" + "adfasd4q3rfadf232" + ";" + "";
}
};
%>
</div>
</body>
</html>
<!-- This last line makes it all go, so don't remove it! It needs to be the last line to "know" about all your endpoints -->
<%if (new Endpoint("Master").runEndpoint()) return;%>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment