public class HelloWorld {
public static void main(String[] args) throws IOException {
WebServer server = WebServer.create();
server.start((request, response) -> response.done("Hello, World"));
}
}
Access your application at:
http://localhost:8080
You can get the latest release version from Maven Central:
<dependency>
<groupId>com.vtence.molecule</groupId>
<artifactId>molecule</artifactId>
<version>0.9</version>
</dependency>
If you want the development version, grab the latest snapshot from Sonatype snapshots repositories
(https://oss.sonatype.org/content/repositories/snapshots
):
<dependency>
<groupId>com.vtence.molecule</groupId>
<artifactId>molecule</artifactId>
<version>0.10-SNAPSHOT</version>
</dependency>
To use the default web server, you also need to add Simple as a dependency:
<dependency>
<groupId>org.simpleframework</groupId>
<artifactId>simple-http</artifactId>
<version>6.0.1</version>
</dependency>
Try out the following examples (Java 6 language level):
- Hello World
- Rendering HTML
- Dynamic Routes
- Static Files
- REST
- Asynchronous Processing
- Cookies
- Locale Negotiation
- Multipart Forms
- View Templates and Layout
- HTTP Sessions
- Filters
- Creating a Custom Middleware
- Caching and Compression
- SSL
- A Sample Application
First thing first, you need a server to run your app:
WebServer server = WebServer.create();
This will set the default web server, which is powered by Simple, to run locally on port 8080.
To start the server, give it an app:
server.start((request, response) -> response.done("Hello, World!"));
To stop the server, call the stop method:
server.stop()
You can optionally specify the interface and port to bound to when creating the server, e.g. if you want to make your server globally available:
WebServer server = WebServer.create("0.0.0.0", 8088);
Molecule, uses Simple as a default webserver. Both are fully asynchronous and non-blocking. This allows the server to scale to very high loads and handle as many concurrent connections as possible, even when depending on a high latency external resource.
What this means is you can serve your response content from a thread separate to the original servicing thread. For instance your application might need to wait for some remote process that takes some time to complete, such as an HTTP or SOAP request to an external server. You can simply call this external resource from a different thread, and complete the response when you get the result.
To tell the server that you're ready to serve the response, call the done method on the response (see Asynchronous Processing).
Look at the Asynchronous example to see how to serve content from a separate thread.
Most modern webapps have nice URLs. Simple URLs are also easier to remember and more user friendly.
Molecule comes with a routing middleware that let you define your URL routes.
Routes let you map incoming requests to different applications based on the request verb and path. A route is composed of a path pattern, an optional set of verbs to match, and an application endpoint:
server.start(new DynamicRoutes() {{
get("/posts/:id").to((request, response) -> {
// retrieve a given post
});
post("/posts").to((request, response) -> {
// create a new post
});
put("/posts/:id").to((request, response) -> {
// update an existing post
});
delete("/posts/:id").to((request, response) -> {
// delete a post
});
map("/").to((request, response) -> {
// show the home page
});
}});
Routes are matched in the order they are defined. If not defined route matches, the default behaviour is to render a 404 Not Found. This can be configured to pass the control to any default application.
By default, a route matches a single verb, specified by the method you use, i.e. get, post, put, delete. That can be changed by providing the verbs as arguments to the via method:
map("/").via(GET, HEAD).to((request, response) -> {
// show the home page
});
If you don't provide any verbs, map will match on all verbs.
Route patterns can be matched exactly - they are said to be static - or can include named parameters, which are then accessible as regular request parameters on the request object:
// matches "GET /photos/18" and "GET /photos/25"
// request.parameter("id") is either '18' or '25'
get("/photos/:id", (request, response) -> {
response.done("Photo #" + request.parameter("id"));
});
You are not limited to the provided match patterns. You can easily implement your own matcher and decide exactly how to match an incoming url to an application.
To do this, use the route definition methods that accept a Matcher rather than a String.
Middlewares are a way to enhance your application with optional building blocks, using a pipeline design.
They implement functionality you tend to need across all your applications, but you don't want to build everytime. Things like access logging, authentication, compression, static files, routing, etc.
Being able to separate the processing of the request (and post-processing of the response) in different stages has several benefits:
- It separate concerns, which helps keep your design clean and application well-structured
- It let you only include the functionality you need, so your server is as small and fast as possible
- It let you plug in your own processing stages, to customize the behavior of your application
- It let you reuse and share middlewares, as elemental building blocks of application behavior
For example you could have the following separate stages of the pipeline doing:
- Capturing internal server errors to render a nice 500 page
- Monitoring, logging accesses to the server
- Authentication and authorisation, to control access to your applicatin
- Caching, returning a cached result if request has already been processed recently
- Compression, to reduce bandwith usage
- Security, to prevent attacks such as CSRF
- Processing, to actually process the request
Molecule comes with a number of middlewares (more are coming), that you can use to build your processing pipeline:
- Router (See Routing)
- Static Assets
- File Server
- Access Log
- Cookies
- Locale Negotiation
- Compression
- ETag
- Conditional Get
- Connection Scope
- Server Header
- Date Header
- Content-Length Header
- Filter Map
- Cookie Session Tracker
- Fail Safe
- Failure Monitor
- Not Found
- Http Method Override
- Layout