Simple Web OOp!
import static swoop.Swoop.get;
import swoop.Action;
import swoop.Request;
import swoop.Response;
public class Hello {
public static void main(String[] args) {
get(new Action() {
@Override
public void handle(Request request, Response response) {
response.body("<h1>Hello!</h1>");
}
});
}
}
Launch the main and view it:
http://0.0.0.0:4567
- Simple and extensible
- Sinatra based routing (Sinatra Route)
- Route patterns support
- Condition support (in progress)
- Cookie support
- WebSocket support (almost done still need to figure out how to write integration tests on it)
- EventSource support (not even in progress yet)
- Static files support
- Pluggable HTTP server
- Default implementation based on an event-driven and non-blocking http server (Webbit)
Define a filter that mesure the time spent when handling request to any /hello/
sub routes. And define two handlers on Get
:
- one on
/time
route that simply returns the current time - the other one on
/hello/:name
that extracts thename
parameter from the called uri, simulates some random job and returns a pretty nice greeting! - The second route match the filter, so its content will be modified with the time spent
import static swoop.Swoop.*;
import java.util.Random;
import swoop.*;
public class TwoMinutes {
public static void main(String[] args) {
around(new Filter("/hello/*") {
@Override
public void handle(Request request, Response response, RouteChain routeChain) {
long t0 = System.currentTimeMillis();
try {
routeChain.invokeNext();
}
finally {
long t1 = System.currentTimeMillis();
String body = response.body();
body += "<br/><small>Request " + request.logInfo()
+ " executed in " + (t1-t0) + "ms</small>";
response.body(body);
}
}
});
get(new Action("/time") {
@Override
public void handle(Request request, Response response) {
response.body("<h1>Current time is: " + new java.util.Date() + "</h1>");
}
});
get(new Action("/hello/:name") {
@Override
public void handle(Request request, Response response) {
try {
// simulate some random processing
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
/* ignore */
}
response.body("<h1>Hello " + request.routeParam("name") + "!</h1>");
}
});
}
}
Launch the main and view it:
http://localhost:4567/time
Check now at:
http://localhost:4567/hello/world
http://localhost:4567/hello/Everybody
and see the filter that has added the processing duration
Let's see how to use and define websocket. First of all the code:
import static swoop.Swoop.*;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import swoop.*;
public class FiveMinutes {
public static void main(String[] args) {
get(new Action("/hello") {
@Override
public void handle(Request request, Response response) {
// for code simplicity page is loaded from a resource
// use Swoop.staticDir(dir) for static content instead
response.body(resourceAsString("FiveMinutes.html"));
}
});
webSocket(new WebSocket("/hellowebsocket") {
@Override
public void onMessage(WebSocketConnection connection, WebSocketMessage msg) {
// echo back message in upper case if it is text
if(msg.isText())
connection.send(msg.text().toUpperCase());
}
});
}
/**
* utility method that simply read a resource and returns its content as string
*/
private static String resourceAsString(String resourcePath) {
InputStream input = FiveMinutes.class.getResourceAsStream(resourcePath);
try {
return IOUtils.toString(input);
}
catch(IOException ioe) {
throw new SwoopException("Failed to load resource <" + resourcePath + ">", ioe);
}
finally {
IOUtils.closeQuietly(input);
}
}
}
Launch the main and view it:
http://localhost:4567/hello
The route /hello
simply load the html page from the resource and return it as is. The Send
button on the html page send the content of the input text through a websocket. The corresponding route is defined on the server at /hellowebcocket
which simply returns the content of the message in upper case.
Html file FiveMinutes.html
(copied from Webbit) is in src/test/resources
<html>
<body>
<!-- Send text to websocket -->
<input id="userInput" type="text">
<button onclick="ws.send(document.getElementById('userInput').value)">Send</button>
<!-- Results -->
<div id="message"></div>
<script>
function showMessage(text) {
document.getElementById('message').innerHTML += "<br/>" + text;
}
// Have a look to www.modernizr.com as an efficient library to figure out
// if your browser support webSocket, and other html5 features
var ws = new WebSocket('ws://' + document.location.host + '/hellowebsocket');
showMessage('Connecting...');
ws.onopen = function() { showMessage('Connected!'); };
ws.onclose = function() { showMessage('Lost connection'); };
ws.onmessage = function(msg) { showMessage(msg.data); };
</script>
</body>
</html>
In progress
Port of NodeJS and Callbacks article using SwOOp.
$ mvn clean test
Integration/Functional1 tests:
$ mvn clean test -Pfunc
Performance tests:
$ mvn clean test -Pperf
1: Whereas it is really debatable, in the case of a middleware library i guess both are strongly related, by the way "don’t worry too much about what you call a test, as long as you are clear on what it does and it does a single thing." — The false dichotomy of tests
- SwOOp route management and http dispatch method is strongly based on the Interceptor Pattern (see Core J2EE Patterns - Intercepting Filter)
SwOOp was originally a fork from Spark. Idea was to replace JEE Servlet dependency (originally from Jetty) by a non-blocking and event based HTTP server. After some initial refactorings, this project has emerged as a complete rewriting in order to have a more flexible and easier to test basis. There are some remaining especially the static bootstrap initialization.
After investigation, the by-default underlying HTTP server is Webbit which is based on Netty.