Restolino is in the process of being depricated and removed from our tech stack. We strongly discourage the use of Restolino for any production application due to a range of performance issues
Java for the Web. A brutally opinionated, super-simple REST API framework, plus static file serving.
It's not comprehensive, won't give you the flexibility you want and doesn't really care about your special needs.
For an overview and examples of the framework, head to the [restolino-example](https://github
.com/davidcarboni/restolino-example) repo to see a template project.
What it does do is let you work and stays out of your way, because configuring frameworks is a distraction. Java is a great language with loads of support, but it's never been efficient for Web development. This changes that.
Life is hard enough. Complexity will quitetly come and steal your time. Bar the door. Don't go looking for it. If it sits down it won't want to leave.
If you're a Java purist, this is not the framework you are looking for.
And that's it.
Restolino has unreasonable opinions:
- Web applications should be APIs: data should not be wrapped in markup.
- HTML/Javascript is youn API client: then you can add mobile apps, IoT devices, etc.
- Templating is great: do it in Javascript, not server-side.
- Efficient development: add classes, change interfaces, whatever, then refresh.
- Immutable releases: single-jar artifact. To make a change to the deployed version, deploy a new version and delete the old vesion (blue-green and all that).
- Stateless requests: when deployed, you'll probably have multiple nodes. Everything gets reinitialised after each change in development. That makes statefulness difficult, which is a good thing.
- Restolino will cause your design to hurt and adapt. You'll be better for it in the long run.
- Constraints are your path to simplicity. Enjoy them.
The framework does less than you'd expect, and that's better:
- Runs an embedded Jetty server with raw
Handler
classes. No Servlets, no Filters, no Context. Noweb.xml
. - Requests that have a file extension are static files. They will be handled by a Jetty
ResourceHandler
. - Requests that do not have a file extension go to your API.
- APIs consume and return JSON. Accept a parameter of any type, return a result of any type. Serialisation is done for you using Gson.
- You get direct access to
HttpServletRequest
andHttpServletResponse
. - If you need to return something other than Json, use a
void
method or returnnull
. - Unmapped requests go to your implementation of the
NotFound
interface, or theDefaultNotFound
implementation. A 404 status code is set by default. - Errored requests go to your implementation of the
ServerError
interface, or theDefaultServerError
implementation. A 500 status code is set by default. - Responses are compressed automatically by a Jetty
GzipHandler
if the client supports gzip encoding.
- You can only
GET
/
. Why would youPUT
,POST
orDELETE
the root? You wouldn't. If you think you would, your design sucks. - By default
/
will give you Json documentation of your API. To change this, implement theHome
interface, which provides a single method:get(req, res)
(or subclassHomeRedirect
). - Put all your static files under
web
- i.e.src/main/resources/web/...
(orsrc/main/web/...
and add aresources
section to your pom). - Annotate your API classes as
@Api
. - API names are lowercased class names. More complexity would need more of your time. Get over it.
- Annotate your methods with JAX-RS
@GET
,@PUT
,@POST
and@DELETE
. - Method parameters can include
HttpServletRequest
andHttpServletResponse
. You can optionally have one parameter of any type you want. Gson will attempt to deserialise this from the request body. - The return type of your method can be any type you want Gson to attempt to serialise into the response. Returns of
void
andnull
are fine, in which case Restolino won't change your response. - Request and response messages are [de]serialised as JSON using Gson. If you need to add custom type adapters for serialisation, you can access the
GsonBuilder
viaSerialiser.getBuilder()
. A few default type adapters (in thejson.typeadapters
package) and a sensible Javascript isoDate format are set for you by default. - There's no context path. Why would you run more than one app in the same server process? The Jetty process is one-to-one with your app.
- You only need one not-found handler. Implement the
NotFound
interface. It provides a single method:handle(req, res)
. A 404 status will be pre-set for you. You can update it if you want. - You only need one error handler, but you do need to know where the error occurred. Implement the
ServerError
interface, which provides a single methodhandle(req, res, RequestHandler, Throwable)
. A 500 status will be pre-set for you. You can update it if you want. - No clever (read: fiddly and time consuming) path/parameter parsing. Simple helper classes are provided instead:
Path
,QueryString
andParameter
. See thecom.github.davidcarboni.restolino.helpers
package. OPTIONS
will query the configuration and tell you which ofGET
,PUT
,POST
andDELETE
are implemented for that API.OPTIONS
on/
will return GET if you have implementedHome
or subclassedHomeRedirect
.- To see the whole framework - all the interfaces and annotations you can use - have a look in the
com.github.davidcarboni.restolino.framework
package. It's intentionally small. - Java 1.7. If you're using anything older, try using Bing to look up SOAP. I know, that's not fair. If you're smart enough to be able to use Google, fork and build from source.
- There are non-private fields in the classes. Like semi-colons in Javascript, the usual modifiers are visual clutter that provide too little benefit. That's my opinion. I'm not asking you to agree if you don' want to.
Restolino aims to be simple enough for you to understand the code. This is not intended as an academic achievement of subtle software engineering prowess. It's meant to be basic, pragmatic, helpful and uncomplicated. Sort of like the girl/boy next door you realise is a far deeper friend than cool kids you've been trying to impress.
Here is the list of packages and what they mean. There are no more than a pizza's worth of classes in each:
com.github.davidcarboni.restolino.
framework - The stuff you need to define your API - annotations and interfaces.com.github.davidcarboni.restolino.
helpers - Convenience classes you may (or may not) want to use to make your life easier.com.github.davidcarboni.restolino.
handlers - Default and exampleHome
,NotFound
andServerError
handlers.com.github.davidcarboni.restolino.
json - Json serialisationcom.github.davidcarboni.restolino.
api - The classes that set up your API.com.github.davidcarboni.restolino.
reload - The classes that detect file changes and trigger reloading.com.github.davidcarboni.restolino.
jetty - The Jetty handlers that make up Restolino.MainHandler
receives requests, decides whether a request is for an API or a file and delegates toApiHandler
orFilesHandler
. There's also a handler for HTTP Basic authentication.com.github.davidcarboni.restolino
- TheMain
andConfiguration
classes used to start the server.
<dependencies>
<dependency>
<groupId>com.github.davidcarboni</groupId>
<artifactId>restolino</artifactId>
<version>0.1.14</version>
</dependency>
</dependencies>
The configuration below provides both a -jar-with-dependencies (for deployment) and a folder of dependencies for reloading in development (${project.build.directory}/dependency
). NB this should work if you want to deploy to Heroku.
This also configures your project for Java 1.8. You could do this with profiles if you want to:
<build>
<resources>
<!-- Standard resources -->
<resource>
<directory>src/main/resources</directory>
</resource>
<!-- Your static content - this will end up in target/web/ -->
<resource>
<directory>src/main/web</directory>
<targetPath>${project.build.outputDirectory}/web</targetPath>
</resource>
</resources>
<plugins>
<!-- Java 1.8. You're not still using 1.6 - are you: -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- An assembly that includes all dependencies is produced for deployment: -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.github.davidcarboni.restolino.Main</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- A folder of dependencies is used in development when reloading classes: -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<!-- Default output folder is ${project.build.directory}/dependency -->
</execution>
</executions>
</plugin>
</plugins>
</build>
NB you could use the maven-shade-plugin
if you want finer control of the "uberjar", but jar-with-dependencies
is pretty restrictive, which is almost always a good thing - and should help you keep a clean and simple design.
This is what you want, right? Minimum time-to-working. This runs your API on port 8080 with remote debug on 8000.
#!/bin/bash
# Remote debug:
export JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"
# File reloading:
export RESTOLINO_STATIC="src/main/web"
# Class reloading
export RESTOLINO_CLASSES="target/classes"
# Optional package prefix:
# export RESTOLINO_PACKAGEPREFIX=com.mycompany.myapp
# Basic authentication
#export USERNAME=java
#export PASSWORD=fortheweb
# Optional: Practically speaking, any value you like that describes your app.
# see also: http://stackoverflow.com/questions/9311353/java-ee-security-realms
#export REALM=niceapp
mvn clean package && \
# Development: reloadable
java $JAVA_OPTS -Drestolino.files=$RESTOLINO_STATIC -Drestolino.classes=$RESTOLINO_CLASSES -Drestolino.packageprefix=$RESTOLINO_PACKAGEPREFIX -cp "target/dependency/*" com.github.davidcarboni.restolino.Main
# Deployment: non-reloadable
#java $JAVA_OPTS -jar target/*-jar-with-dependencies.jar
Why not reload when deployed? You want to be using containers, but as a minimum you should be designing for stateless, immutable nodes. If something needs to change in your deployment, update a the build using your flavour of continuous delivery. Changing running servers manually is a brew of risk that ends in pain.
I used Restolino to implement a significant Alpha project for
the UK government. The focus was on creating a working prototype, so speed of iteration was very important. It
worked. The best thing about using Restolino was nobody talked about Restolino - it stayed out of the way and never
became the conversation.
Restolino is in the process of being depricated and removed from our tech stack. We strongly discourage the use of Restolino for any production application due to a range of performance issues
I don't know. What I do know is that both the server and the framework are doing much less before your code gets to see a request. That's a good indicator for performance (less layers) and security (less places for bugs to hide), so chances are good.
Restolino takes more away (Servlets, Filters, Context, etc.) than it adds. If you trust Jetty and want to make simple things freely, Restolino will probably work for you. (see also http://stackoverflow.com/questions/16063576/lightweight-servlet-container-for-production-use)
Yes. Restolino intentionally doesn't stray too far from JAX-RS. If you add @Path(<classname>)
to your classes and @Context
to your HttpServletRequest
and HttpServletResponse
method parameters, your code should slot straight in to a Jersey application.
I've tried, but it adds complexity, doesn't add value and makes life harder. Why? Because the interface has to take into account that there may or may not be a request message type (so generic varargs would be needed - ever tried that?) and you can return any (or no) response message type (so the retun type is variable). That means either an altogether too generic interface (everything has to be Object, even if you'd prefer void) or individual interfaces for every permutation.
Using reflection to search for methods with either two or three parameters, where the first two are HttpServletRequest
and HttpServletResponse
turns out to be simpler, cleaner and more easily understood. Like I said, if you're a purist this isn't the framework you're looking for. Try it if you like - if you can come up with an elegant, pragmatic and developer-friendly solution, send me a pull request.
First port of call is to look at the information printed to the console on startup. If you see Reflections URLs: []
you might need to check your restolino.packageprefix
setting - I've made this mistake a couple of times. If nothing is happening when you update a class, check the values of classesReloadable and classesInClasspath. It's not possible to reload classes that are on the JVM classpath at startup.
Restolino uses the SLF4J interface for logging. Please refer to the SLF4J documentation for further information on enabling its output.
I'm still aced that I can edit code, adding new fields, methods, annotations - even classes - and it's there when I refresh the browser. After years of productivity-sapping build-redeploy cycles, there's something utterly delicious and deeply freeing about it.
I was able to create a project from scratch and get to a running API in 10 minutes by cutting and pasting the above snippets. For me that's a lot faster than configuring Spring or even Jersey, not to mention the time I've spent debugging config.
Honestly, I originally built this in a day so don't expect a miracles. I hope it gives you a boost with getting stuff done rather than learning to love a fancy framework. Class reloading took longer and I ditched Servlets for embedded Jetty with raw Handlers along the way.
The code isn't perfect, but it should just work - and let you work. Let me know if you like it.