https://github.com/arnauld/swoop
Simple Web oop(s) -- A sinatra like Java web framework
https://github.com/arnauld/swoop
Last synced: about 1 year ago
JSON representation
Simple Web oop(s) -- A sinatra like Java web framework
- Host: GitHub
- URL: https://github.com/arnauld/swoop
- Owner: Arnauld
- License: mit
- Created: 2012-03-07T23:03:42.000Z (over 14 years ago)
- Default Branch: master
- Last Pushed: 2013-12-06T23:12:23.000Z (over 12 years ago)
- Last Synced: 2025-04-05T22:31:58.406Z (about 1 year ago)
- Language: Java
- Homepage:
- Size: 535 KB
- Stars: 6
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.markdown
- License: LICENSE.txt
Awesome Lists containing this project
README

*Simple Web OOp!*
# Quick start
```java
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("
Hello!
");
}
});
}
}
```
Launch the main and view it:
http://0.0.0.0:4567
# Features
* Simple and extensible
* Sinatra based routing ([Sinatra Route](http://www.sinatrarb.com/intro.html#Routes))
* 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](https://github.com/webbit/webbit))
# *SwOOp* in...
## ...two minutes!
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 the `name` 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
```java
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 += "
Request " + request.logInfo()
+ " executed in " + (t1-t0) + "ms";
response.body(body);
}
}
});
get(new Action("/time") {
@Override
public void handle(Request request, Response response) {
response.body("
Current time is: " + new java.util.Date() + "
");
}
});
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("Hello " + request.routeParam("name") + "!
");
}
});
}
}
```
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
## ...less than five minutes! (and with WebSocket)
Let's see how to use and define websocket. First of all the code:
```java
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](https://github.com/webbit/webbit)) is in `src/test/resources`
```html
Send
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); };
```
## ... in more than ten minutes (but non-blocking and functional!)
**In progress**
Port of [NodeJS and Callbacks](http://tapestryjava.blogspot.com/2012/03/nodejs-and-callbacks.html) article using SwOOp.
# Contributing
```bash
$ mvn clean test
```
Integration/Functional1 tests:
```bash
$ mvn clean test -Pfunc
```
Performance tests:
```bash
$ 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](http://gojko.net/2011/01/12/the-false-dichotomy-of-tests/)
# Implementation notes
* SwOOp route management and http dispatch method is strongly based on the Interceptor Pattern (see [Core J2EE Patterns - Intercepting Filter](http://java.sun.com/blueprints/corej2eepatterns/Patterns/InterceptingFilter.html))
## Inspirations & Credits
*SwOOp* was originally a fork from [Spark](https://github.com/perwendel/spark). Idea was to replace JEE Servlet dependency (originally from [Jetty](http://jetty.codehaus.org/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](https://github.com/webbit/webbit) which is based on [Netty](http://www.jboss.org/netty).
* Spark: [github](https://github.com/perwendel/spark) and [Website](http://www.sparkjava.com/)
* [Webbit](https://github.com/webbit/webbit)
* [Sinatra](https://github.com/sinatra/sinatra)
* [Base code](https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb)
* [Routing tests](https://github.com/sinatra/sinatra/blob/master/test/routing_test.rb)
* [Rake parse_query](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L65)
* [Rake parse_query tests](https://github.com/rack/rack/blob/master/test/spec_utils.rb#L103)