This is the new documentation for Rapidoid (v5.4).

For older versions of Rapidoid, please see v5.3 and the old docs.

Things to improve in the docs:

  • the important concepts (architecture, design etc.) should be explained in more details,

  • the examples should be organized by categories,

  • information about the HTTP routes and sample requests/responses should be included for each example.

1. What is Rapidoid?

Rapidoid is an extremely fast HTTP server and modern Java web framework / application container, with a strong focus on high productivity and high performance.

rapidoid

2. Web Framework or Dockerized Web Platform?

The most of this documentation covers the aspects of using Rapidoid as a web framework with Java.

But Rapidoid can also be used as a web platform. For more details, please see the documentation of the Official Docker repository for Rapidoid.

3. Quick start

For a quick start please use the rapidoid-quick module. It consists of rapidoid-web + batteries included:

  • Hibernate

  • Hibernate Validator

  • MySQL Connector

  • Logback

4. Request routing

A web application can be defined as a collection of HTTP request handlers. The HTTP requests are routed to the appropriate lambda, based on the combination of:

  • the request verb (GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE)

  • and the request path (e.g. /books/123, /users).

4.1. Matching route

Rapidoid tries to find a matching handler for the route of the pending request. If no handler matches the route, the first generic handler (if any) will be executed.

4.2. Generic handlers

The generic handlers are ordered in the order they were registered. The generic handlers match any route, and they can be registered with On.req(). If a generic handler returns not found, the next generic handler will be executed.

4.3. Not found

If no handler manages to process the request, a not found HTTP response is returned.

5. Performance

The network communication of Rapidoid is fully asynchronous (e.g. epoll on Linux). Several benchmarks have demonstrated stable high performance with 16K connections.

The number of I/O threads in Rapidoid equals the number of cores. E.g. on a 8-core machine there would be 8 I/O threads.

The number of job executor threads can vary (e.g. 32, 128, 512, 1024 or more).

5.1. Managed vs unmanaged request handlers

The request processing can be done in a managed and unmanaged way:

  • managed (the default): each request will be handled inside a job executed on the executor service (using Jobs.*). This is the most convenient and powerful option, as it includes some built-in functionality:

    • request wrappers/interceptors

    • access control

    • transaction management

  • unmanaged - configured by route.managed(false): every request is handled on the I/O thread that received it. This is the fastest option and most flexible option, as it gives the developer complete control over the request handling.

Note
The handlers that are executed in unmanaged way should execute quickly and in non-blocking way.

5.2. Asynchronous handlers

For asynchronous handlers, which continue to execute some logic outside the handler job, Req#async() and Req#done() should be used.

6. Profiles

Application profiles are useful to conditionally activate application beans or configuration.

6.1. The default profile

If no profiles are specified, the default profile will automatically be activated.

6.2. Conditionally activated profiles

Depending on the configured or inferred environment mode (dev / test / production), one of the dev, test or production profiles will also be automatically activated (in addition to the default or configured profiles).

6.3. Configuring active profiles

There are several ways to activate the application profiles:

  • command line argument:

profiles=foo,bar

  • environment variable:

PROFILES=foo,bar

  • programmatically:

App.profiles("foo", "bar");
Tip
Activating a prod or production profile automatically marks the environment mode as production.

6.4. Built-in profile-specific configuration

Rapidoid has built-in default configuration for the profiles default, dev and mysql.

7. Serving static files

It’s very easy to serve static files with Rapidoid. Just add them as resources inside the static folder on the classpath in your Java project.

Tip
Most of the Java projects use Maven, so the static files should usually be placed in src/main/resources/static.

7.1. Built-in static resources

Rapidoid includes some built-in static resources in a default/static folder on the classpath.

Thus, both default/static and static are default locations for serving static files.

The static location has precedense over the default/static location.

7.2. Automatically serving the static resources

Once a HTTP server is started, it will serve all the resources that exist in the static and default/static locations on the classpath. They will be publicly available.

8. RESTful services

TODO: More details should be provided here.

Please see the examples.

8.1. Minimal dependency

The minimal dependency that is required to develop RESTful services is the rapidoid-http-server module.

9. Session management

A HTTP session is a temporary data storage that spans multiple HTTP requests. Data is stored in the session to be accessed later, when handling another HTTP request from the same user (and browsing session).

There are 2 different implementations of the session:

  • server-side session a.k.a. session

  • client-side token as a session a.k.a. token

9.1. Server-side session a.k.a. session

The server-side session is a simple in-memory storage in the web server.

Non-trivial scaling out with server-side session

While there’s no problem when running an application on only one web server, scaling out is non-trivial. It requires sticky sessions or replicating the session, or storing it into some datastore.

9.2. Client-side token as a session a.k.a. token

The token is small, important data that is not stored on the server.

Super-easy scaling out with token as a session

The main purpose of the token is keeping the servers stateless (a.k.a. shared-nothing architecture). This approach allows easy scaling out of the application on multiple servers.

Inside the token

The token has a Map-like structure, and it can store arbitrary data. By default, it stores:

  • username of the logged-in user

  • token expiration time

  • scope of validity of the token

Persisting the token

The token is being serialized and sent to the web browser as a cookie. For the web browser this is just a cookie, so it sends the cookie to the server attached to the subsequent HTTP requests.

Token limitations

The main drawback of the token is the browser’s cookie size limits. Each browser has different limit for the size of the cookies, but the safest limit to assume is 4 KB. That’s why the token should be used to store only small data.

Token security

The token is encrypted and HMAC-signed with the application’s secret key. Thus, the user can’t read nor modify the token data in a malicious way (or any way).

Token expiration

After a configurable time (config token.ttl in milliseconds), the token expires.

The token cookie will also expire when the browser is closed.

9.3. Configuring the token

Please see the token configuration section.

10. Dependency injection

10.1. Annotations

Annotating a class with any of these annotations will mark the class as "managed" by Rapidoid for dependency injection:

  • org.rapidoid.annotation.Controller

  • org.rapidoid.annotation.Service

  • javax.inject.Named

  • javax.inject.Singleton

The @Controller and @Service annotations serve for more specific description of the nature of the managed component (whether it is a web controller or business logic service).

10.2. Singleton scope only

Rapidoid only supports the singleton scope.

10.3. Auto-run main entry points

The org.rapidoid.annotation.Run annotation marks a class with a main method that needs to be executed when bootstrapping an application:

RunAnnotationDemo.java
public class RunAnnotationDemo {
   public static void main(String[] args) {
      App.bootstrap(args, "aa", "bb");
   }
}

@Run
class AppEntryPoint {
   public static void main(String[] args) {
      U.print(args);
   }
}

11. Security

Role-based security checks for the web handlers are supported out-of-the-box.

11.1. Access denied

Accessing a protected page without the required roles will automatically display a nice login page.

Accessing a protected RESTful service without the required roles will return error in JSON format.

11.2. Configuring required roles

Required roles can be configured per route. Example:

On.get("/review").roles("moderator").json(...)

Required roles for POJO handlers can configured with annotations, e.g. @Roles({"moderator", "administrator"})

Rapidoid also includes annotations for the common roles (@Administrator, @Moderator, @Manager, @LoggedIn):

  • The @Administrator annotation is equivalent to @Roles("administrator") or @Roles(Role.ADMINISTRATOR)

  • The @Manager annotation is equivalent to @Roles("manager") or @Roles(Role.MANAGER)

  • The @Moderator annotation is equivalent to @Roles("moderator") or @Roles(Role.MODERATOR)

  • The @LoggedIn annotation is equivalent to @Roles("logged_in") or @Roles(Role.LOGGED_IN)

12. Authentication

12.1. Bootstrapping the authentication services

Rapidoid provides built-in authentication services:

  • POST /_login

  • GET /_logout

You can easily bootstrap these services:

App.bootstrap(args).auth()
Warning
The built-in GET /_logout handler will be changed to POST /_logout in future.

12.2. Authentication flow

  • A POST /_login request containing username and password will execute the (custom-configured or default) login handler.

  • If the login handler returns true, the user was authenticated and the authentication data (username and login expiration time) are saved in the token.

  • The token is returned by the POST /_login handler, but it is also persisted in the _token cookie.

  • With every request the authentication data is being sent through the token, so the server can identify the user.

  • When the browser is closed, the _token cookie expires, so the auth data is lost.

  • The token can be sent with a HTTP request as a _token cookie, or a _token parameter (in the URI or the body).

  • After a configurable time (config token.ttl in milliseconds), the authentication of the logged-in user expires.

  • A GET /_logout request will execute the built-in logout handler which clears the authentication data from the token.

12.3. The Admin API

The Admin.* API is a mirror of the On API, but for the Admin setup. The administrator role is configured by default for all routes of this API.

13. Configuration

13.1. The built-in configuration

Rapidoid has built-in default configuration which provides sensible defaults for many components of the framework.

built-in-config.yml
on:
  port: 8080
  address: 0.0.0.0

admin:
  port: ${on.port}
  address: ${on.address}

app:
  contextPath: ''
  home: /

gui:
  search: false
  navbar: true
  fluid: false
  cdn: auto
  menu: {}

main-zone: {}

admin-zone:
  home: /_
  brand: '<i class="fa fa-dashboard"></i> Admin Center'
  search: false
  fluid: true
  menu:
    Overview: /_
    Routes: /_routes
    Configuration: /_config
    Processes: /_processes
    Metrics: /_metrics
    Manageables: /_manageables
    Application:
      Beans: /_beans
      Entities: /_entities
    System:
      Terminate / Restart: /_terminate
      Classpath: /_classpath
      Memory pool: /_jmx/mempool
      JVM Threads: /_jmx/threads
      Operating system: /_jmx/os
      Garbage collection: /_jmx/gc
      Memory: /_jmx/memory
      Runtime: /_jmx/runtime
      Classes: /_jmx/classes
      Compilation: /_jmx/compilation

users:
  admin:
    roles: administrator

jobs:
  executor:
    threads: 256
  scheduler:
    threads: 64

jdbc:
  host: localhost
  driver: UNKNOWN
  username: UNKNOWN
  password: UNKNOWN
  url: UNKNOWN
  options: ''
  poolProvider: hikari

hibernate:
  dialect: UNKNOWN

c3p0:
  debug: false
  initialPoolSize: 5
  minPoolSize: 5
  maxPoolSize: 100
  acquireIncrement: 5
  maxStatementsPerConnection: 10
  idleConnectionTestPeriod: 300

hikari: {}

oauth:
  google:
    scope: profile,email
    clientId: YOUR_GOOGLE_CLIENT_ID_HERE
    clientSecret: YOUR_GOOGLE_CLIENT_SECRET_HERE

  github:
    scope: user:email
    clientId: YOUR_GITHUB_CLIENT_ID_HERE
    clientSecret: YOUR_GITHUB_CLIENT_SECRET_HERE

  facebook:
    scope: public_profile,email
    clientId: YOUR_FACEBOOK_CLIENT_ID_HERE
    clientSecret: YOUR_FACEBOOK_CLIENT_SECRET_HERE

  linkedin:
    scope: r_basicprofile,r_emailaddress
    clientId: YOUR_LINKEDIN_CLIENT_ID_HERE
    clientSecret: YOUR_LINKEDIN_CLIENT_SECRET_HERE

net:
  address: 0.0.0.0
  port: 8080
#  workers: ${system.cpus}
  bufSizeKB: 256
  noDelay: false
  syncBufs: true

http:
  timeout: 30000
  timeoutResolution: 5000
  maxPipeline: 10
  serverName: Rapidoid

  mandatoryHeaders:
    connection: true
    date: true
    server: true
    contentType: true

reverse-proxy:
  timeout: 10000
  retryDelay: 300
  maxConnections: 100
  maxConnectionsPerRoute: 100
  reuseConnections: true
  setHeaders:
    X-Forwarded-For: true
    X-Client-IP: false
    X-Real-IP: false
    X-Username: false
    X-Roles: false

token:
  ttl: 0 # unlimited

log:
  level: info
  fancy: false # auto

tls:
  selfSigned: true
  enabled: false
  keystore: ''
  keystorePassword: ''
  keyManagerPassword: ''
  truststore: ''
  truststorePassword: ''

13.2. Custom configuration

Custom configuration can be specified through config.yml (or config.yaml) configuration files.

13.3. Profiles

Custom, profile-specific configuration can be specified through config-<profile-name-here>.yml or config-<profile-name-here>.yaml configuration files. Examples:

  • config-mysql.yml

  • config-dev.yaml

13.4. External configuration files

The configuration files will tipically be classpath resources, but they can reside in a custom-configured external folder, as well.

Rapidoid can also be configured through command line args or system/environment properties. The custom configuration overrides the built-in default configuration.

14. Multiple servers setup

14.1. Bootstrapping multiple servers

Main.java
package org.rapidoid.docs.multisetup;

import org.rapidoid.docs.multisetup.barsetup.BarSetupCtrl;
import org.rapidoid.docs.multisetup.foosetup.FooSetupCtrl;
import org.rapidoid.http.HTTP;
import org.rapidoid.setup.App;
import org.rapidoid.setup.Setup;

public class Main {

        public static void main(String[] args) {
                App.run(args);

                Setup setup1 = Setup.create("foo").port(2222);
                Setup setup2 = Setup.create("bar").port(3333);

                setup1.scan(FooSetupCtrl.class.getPackage().getName());
                setup2.scan(BarSetupCtrl.class.getPackage().getName());

                // demo
                HTTP.get("localhost:2222/foo").print();
                HTTP.get("localhost:3333/bar").print();
        }

}
barsetup/BarSetupCtrl.java
package org.rapidoid.docs.multisetup.barsetup;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;

@Controller
public class BarSetupCtrl {

        @GET
        public String bar() {
                return "Hello, bar!";
        }

}
foosetup/FooSetupCtrl.java
package org.rapidoid.docs.multisetup.foosetup;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;

@Controller
public class FooSetupCtrl {

        @GET
        public String foo() {
                return "Hello, foo!";
        }

}

15. JDBC

15.1. API

Please take a look at the JDBC and JdbcClient classes.

15.2. Connection pools

Rapidoid provides out-of-the-box support the C3P0 and Hikari connection pools.

Note
Hikari is now the default integrated connection pool for JDBC and JPA.

15.3. Providing custom DataSource

Please take a look at JdbcClient#dataSource.

15.4. Paging

Support for automatic paging through the results of JDBC queries will be available soon.

16. JPA

Rapidoid provides generic JPA support. For a quick start it also includes Hibernate in the rapidoid-quick module.

16.1. API

Please take a look at the JPA class.

16.2. Bootstrap

Rapidoid can bootstrap Hibernate 4 (Hibernate 5 will eventually be supported):

App.bootstrap(args).jpa();

This will scan the application package on the classpath, find the annotated JPA entities and register them with Hibernate.

16.3. Connection pools

Rapidoid provides out-of-the-box support the C3P0 and Hikari connection pools.

Note
Hikari is now the default integrated connection pool for JDBC and JPA.

17. Transactions

Rapidoid also provides JPA transaction management, which is activated when a route is marked as transactional using .transaction(…​) or @Transactional handler method.

The transaction boundaries match the execution scope of the request handler, so every request is handled in a separate transaction.

For custom transaction scope and complete (programmatic) control over a transaction, you can call:

JPA.transaction(...)

18. Configuring the database

Rapidoid features some built-in configuration for HSQL, MySQL and PostgreSQL. You can use it by activating the desired application profile.

This built-in configuration also serves as a guide to configuring the database.

18.1. Embedded HSQL

Using the embedded HSQL database by default (the default profile):

built-in-config-default.yml
jdbc:
  driver: org.hsqldb.jdbc.JDBCDriver
  url: jdbc:hsqldb:mem:public
  username: sa
  password: ''

hibernate:
  dialect: org.hibernate.dialect.HSQLDialect

18.2. MySQL

Using MySQL with the mysql profile:

built-in-config-mysql.yml
jdbc:
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://${jdbc.host}:${jdbc.port}?${jdbc.options}
  username: root
  password: root
  port: 3306
  options: logger=Slf4JLogger

hibernate:
  dialect: org.hibernate.dialect.MySQL5Dialect

18.3. PostgreSQL

Using PostgreSQL with the postgres profile:

built-in-config-postgres.yml
jdbc:
  driver: org.postgresql.Driver
  url: jdbc:postgresql://${jdbc.host}:${jdbc.port}/?${jdbc.options}
  username: postgres
  password: postgres
  port: 5432
  options: loglevel=1

hibernate:
  dialect: org.hibernate.dialect.PostgreSQL9Dialect

19. Rapidoid’s template engine

Starting from v5.1, Rapidoid has its own template engine. Basically, it is very similar to Mustache, with the following differences:

  • Using ${x} instead of {{x}}. This syntax is very common in the Java world, and it also avoids collision of the {{x}} tags with the front-end libraries (e.g. Angular).

  • Supporting additional {{?x}}…​{{/}} tag for if. The traditional {{#x}}…​{{/x}} remains unchanged, having both foreach and if semantics.

  • Providing text alternative if the value if not available e.g. ${x|something else}

20. Reverse proxy and load balancer

Starting from v5.2, Rapidoid can be also used as a HTTP reverse proxy and load balancer. A basic round-robin load balancer is included and configured as a default one.

The reverse proxy can be configured programatically (optionally with custom load balancers).

The reverse proxy can also be configured from the command-line args. The command-line argument syntax is: /path → upstream1,upstream2…​. Examples:

20.1. Configuring

Please see the reverse-proxy configuration section.

21. HTTPS & TLS

It’s easy to enable serving over HTTPS (or TLS for any protocol, in general) with Rapidoid. The server will listen on the same port, but instead of HTTP it will serve HTTPS.

21.1. Self-signed certificates

If configured, Rapidoid will create (untrusted) self-signed certificates, which might be useful for quick demo/testing purposes.

21.2. Configuring HTTPS (TLS)

Main.java
package org.rapidoid.docs.https;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                On.get("/hello").plain("hello!");
        }

}
config.yml
tls:
  enabled: false
  selfSigned: true # shouldn't be used in production
  keystore: '/ssl/test-keystore'
  keystorePassword: 'my-password'
  keyManagerPassword: 'my-password'

22. HTTP Chunked response

22.1. Sending chunked HTTP response

Main.java
package org.rapidoid.docs.httpchunked;

import org.rapidoid.job.Jobs;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                App.run(args);

                On.get("/hello").plain((req, resp) -> {

                        req.async(); // mark asynchronous request processing

                        // send part 1
                        resp.chunk("part 1".getBytes());

                        // after some time, send part 2 and finish
                        Jobs.after(100).milliseconds(() -> {
                                resp.chunk(" & part 2".getBytes());
                                resp.done();
                        });

                        return resp;
                });
        }

}

23. Customization

Many components of Rapidoid can be customized / replaced. Please see the My and Customization classes.

23.1. Customizable components

Here is a list of the customization fields in the Customization class:

String[] staticFilesPath;

ErrorHandler errorHandler;

ViewResolver viewResolver;

PageDecorator pageDecorator;

JsonResponseRenderer jsonResponseRenderer;

JsonRequestBodyParser jsonRequestBodyParser;

BeanParameterFactory beanParameterFactory;

LoginProvider loginProvider;

RolesProvider rolesProvider;

BeanValidator validator;

ObjectMapper jackson;

EntityManagerProvider entityManagerProvider;

EntityManagerFactoryProvider entityManagerFactoryProvider;

SessionManager sessionManager;

StaticFilesSecurity staticFilesSecurity;

HttpWrapper[] wrappers;

ResourceLoader templateLoader;

E.g. All of them can be customized using My.* API:

  • My.staticFilesPath(…​)

  • My.errorHandler(…​)

  • My.viewResolver(…​)

  • etc.

Please see the examples.

24. Logging

Please see the Log class.

24.1. Configuring

Please see the log configuration section.

25. Basic command-line arguments

Name Desc Default value
config configuration filename prefix config
dev run in DEV mode auto-detected
production run in PRODUCTION mode auto-detected
test run in TEST mode auto-detected
secret=<SECRET> configure secret key for cryptography random
profiles=<P1,P2...> comma-separated list of application profiles (e.g. mysql,prod) the 'default' profile
on.port=<P> the default App server will listen at port P 8888
on.address=<ADDR> the default App server will listen at address ADDR 0.0.0.0
admin.port=<P> the Admin server will listen at port P same as on.port
admin.address=<ADDR> the Admin server will listen at address ADDR on.address
app.services=<S1,S2...> comma-separated list of services to bootstrap on the App server none
admin.services=<S1,S2...> comma-separated list of services to bootstrap on the Admin server none

26. Rapidoid Modules

26.1. Rapidoid GUI module

  • Consists of Web GUI components that generate HTML (grids, forms and other widgets)

  • Most of the GUI components are based on the Twitter Bootstrap framework

26.2. Rapidoid Fluent module

  • Lightweight fluent DSL for elegant manipulation of collections and streams

26.3. Rapidoid Essentials module

  • The must-have utilities for every Java project!

  • Simple and straight to the point!

26.4. Rapidoid Web module

  • The high-level API is built on top of Rapidoid HTTP server, provides many advanced features for building modern POJO-based web applications

27. The HTTP API

The HTTP Request and Response API (in interface Req):

HTTP REQUEST DATA
String verb()
Gets the verb of the HTTP request.
String uri()
Gets the uri of the HTTP request.
String path()
Gets the path of the HTTP request.
String query()
Gets the query of the HTTP request.
byte[] body()
Gets the raw body data of the HTTP request.
String host()
Gets the value of the Host header of the HTTP request.
String zone()
Gets the name of the application zone handling the request.
The default zone name is main for the On API, and admin for the Admin API.
String contextPath()
Gets the context path of the application zone handling the request.
The default context path is / for the On API, and /_ for the Admin API.
String clientIpAddress()
Gets the IP address of the HTTP client directly sending the request.
This can be the address of a real user, or a HTTP proxy (if the user uses such), or a reverse proxy (if the application/server uses such).
String realIpAddress()
A best-effort attempt to infer the real IP address of the end user/client/client proxy.
If a reverse proxy is detected with high confidence (or configured), its headers will be used to get the real IP address of the user.
Otherwise, the value of Req#clientIpAddress() is returned.
long connectionId()
Gets the HTTP connection ID, which is unique per HTTP server instance.
long requestId()
Gets the HTTP request ID, which is unique per HTTP server instance.
URL PARAMETERS:
Map<String, String> params()
Gets the URL parameters of the HTTP request.
String param(String name)
Returns the value of the specified mandatory URL parameter from the HTTP request, or throws a runtime exception if it is not found.
String param(String name, String defaultValue)
Returns the value of the specified optional URL parameter from the HTTP request, or the specified default value, if not found.
T param(Class<T> beanType)
Returns a new instance of the specified bean type, with properties initialized from the URL parameters of the HTTP request.
POSTED PARAMETERS IN THE REQUEST BODY:
Map<String, Object> posted()
Gets the posted parameters of the HTTP request body.
T posted(String name)
Returns the value of the specified posted parameter from the HTTP request body, or throws a runtime exception if it is not found.
T posted(String name, T defaultValue)
Returns the value of the specified posted parameter from the HTTP request body, or the specified default value, if it is not found.
T posted(Class<T> beanType)
Returns a new instance of the specified bean type, with properties initialized from the posted parameters of the HTTP request.
UPLOADED FILES IN THE REQUEST BODY:
Map<String, List<Upload>> files()
Gets the uploaded files from the HTTP request body.
List<Upload> files(String name)
Returns the uploaded files with the specified form parameter name (not filename) from the HTTP request body, or throws a runtime exception if not found.
Upload file(String name)
Returns exactly one posted file with the specified form parameter name (not filename) from the HTTP request body, or throws a runtime exception if not found.
REQUEST DATA PARAMETERS (URL PARAMETERS + POSTED PARAMETERS + UPLOADED FILES):
Map<String, Object> data()
Gets the data parameters (URL parameters + posted parameters + uploaded files) of the HTTP request.
T data(String name)
Returns the value of the specified data parameter from the HTTP request, or throws a runtime exception if it is not found.
T data(String name, T defaultValue)
Returns the value of the specified data parameter from the HTTP request, or the specified default value, if it is not found.
T data(Class<T> beanType)
Returns a new instance of the specified bean type, with properties initialized from the data parameters of the HTTP request.
EXTRA ATTRIBUTES ATTACHED TO THE REQUEST:
Map<String, Object> attrs()
Gets the extra attributes of the HTTP request.
T attr(String name)
Returns the value of an extra attribute from the HTTP request, or throws a runtime exception if it is not found.
T attr(String name, T defaultValue)
Returns the value of the specified extra attribute from the HTTP request, or the specified default value, if it is not found.
SERVER-SIDE SESSION:
String sessionId()
Returns the ID of the session (the value of the "JSESSIONID" cookie). If a session doesn't exist, a new session is created.
boolean hasSession()
Does the HTTP request have a server-side session attached?
Map<String, Serializable> session()
Provides read/write access to the server-side session attributes of the HTTP request/response.
T session(String name)
Returns the value of the specified server-side session attribute from the HTTP request/response, or throws a runtime exception if it is not found.
T session(String name, T defaultValue)
Returns the value of the specified server-side session attribute from the HTTP request/response, or the specified default value, if it is not found.
TOKEN DATA:
boolean hasToken()
Does the HTTP request have a token attached?
Map<String, Serializable> token()
Provides read/write access to the token attributes of the HTTP request/response.
T token(String name)
Returns the value of the specified token attribute from the HTTP request/response, or throws a runtime exception if it is not found.
T token(String name, T defaultValue)
Returns the value of the specified token attribute from the HTTP request/response, or the specified default value, if it is not found.
REQUEST HEADERS:
Map<String, String> headers()
Gets the headers of the HTTP request.
String header(String name)
Returns the value of the specified header from the HTTP request, or throws a runtime exception if it is not found.
String header(String name, String defaultValue)
Returns the value of the specified header from the HTTP request, or the specified default value, if it is not found.
REQUEST COOKIES:
Map<String, String> cookies()
Gets the cookies of the HTTP request.
String cookie(String name)
Returns the value of the specified cookie from the HTTP request, or throws a runtime exception if it is not found.
String cookie(String name, String defaultValue)
Returns the value of the specified cookie from the HTTP request, or the specified default value, if it is not found.
RESPONSE:
Resp response()
Gets the reference to the response object.
ASYNCHRONOUS REQUEST HANDLING:
Req async()
Informs the HTTP server that the request will be handled asynchronously (typically on another thread). When the response is complete, the Req#done() or Resp#done() method must be called, to inform the server.
boolean isAsync()
Is/was the request being handled in asynchronous mode?
Req done()
Informs the HTTP server that the asynchronous handling has finished and the response is complete.
boolean isDone()
Has the request handling and response construction finished?
WEB APPLICATION SETUP:
HttpRoutes routes()
Provides access to the HTTP routes of the web application setup.
Route route()
Provides access to the matching HTTP route (if any) of the web application setup.
In case a generic handler handles the request, or no matching route was found, null is returned.
Customization custom()
Provides access to the customization of the web application setup.
void revert()
Reverts the previous processing of the request, usually with intention to process the same request again.
OutputStream out()
First renders the response headers, then returns an OutputStream representing the response body. The response body will be constructed by writing to the OutputStream.
MediaType contentType()
Gets the Content-Type header of the HTTP response if it has been assigned, or the default value as configured in the HTTP route.
long handle()
Returns the request handle, which is used when resuming the request handling in asynchronous way.
See Resp#resume.

The HTTP Request and Response API (in interface Resp):

Resp result(Object content)
Sets the content to be serialized into a body when the HTTP response is rendered.
Object result()
Gets the content to be serialized into a body when the HTTP response is rendered.
Resp body(byte[] body)
Sets the HTTP response body from a byte[] data that is written as a HTTP response body when rendered.
Resp body(ByteBuffer body)
Sets the HTTP response body from a ByteBuffer data that is written as a HTTP response body when rendered.
Object body()
Gets the HTTP response body data (of type byte[] or ByteBuffer) that is written as a HTTP response body when rendered.
Resp raw(byte[] raw)
Sets the raw HTTP response (headers and body) from a byte[] data that is written as a HTTP response when rendered.
Resp raw(ByteBuffer raw)
Sets the raw HTTP response (headers and body) from a ByteBuffer data that is written as a HTTP response when rendered.
Object raw()
Gets the raw HTTP response (headers and body) data (of type byte[] or ByteBuffer) that is written as a HTTP response when rendered.
Resp code(int code)
Sets the status code (e.g. 200, 404, 500) of the HTTP response.
int code()
Gets the status code (e.g. 200, 404, 500) of the HTTP response.
Resp contentType(MediaType contentType)
Sets the Content-Type header to be rendered in the HTTP response.
MediaType contentType()
Gets the Content-Type header to be rendered in the HTTP response.
Resp redirect(String redirectURI)
Sets the redirect URI of the HTTP response.
Setting this will cause a HTTP 30x redirect response.
String redirect()
Gets the redirect URI of the HTTP response.
Resp filename(String filename)
Sets the filename when serving a file in the HTTP response.
String filename()
Gets the filename when serving a file in the HTTP response.
Resp view(String viewName)
Sets a custom name of the view (V from MVC) of the HTTP response.
This also sets mvc to true.
The default view name equals the request path without the "/" prefix, except for the "/" path, where the view name is "index".
E.g. "/abc" -> "abc", "/" -> "index", "/my/books" -> "my/books".
String view()
Gets the (default or customized) name of the view (V from MVC) of the HTTP response.
The default view name equals the request path without the "/" prefix, except for the "/" path, where the view name is "index".
E.g. "/abc" -> "abc", "/" -> "index", "/my/books" -> "my/books".
Resp noView()
Disables the view rendering for the target MVC route. The page decorator remains enabled.
Resp file(File file)
Sets the file to be served when the HTTP response is rendered.
File file()
Gets the file to be served when the HTTP response is rendered.
Map<String, String> headers()
Provides read/write access to the headers of the HTTP response.
Resp header(String name, String value)
Sets a header of the HTTP response.
Map<String, String> cookies()
Provides read/write access to the cookies of the HTTP response.
Resp cookie(String name, String value, String... extras)
Sets a cookie of the HTTP response.
Map<String, Serializable> session()
Provides read/write access to the server-side session attributes of the HTTP request/response.
Resp session(String name, Serializable value)
Sets a session attribute of the HTTP response.
Map<String, Serializable> token()
Provides read/write access to the token attributes of the HTTP request/response.
Resp token(String name, Serializable value)
Sets a token attribute of the HTTP response.
Map<String, Object> model()
Provides read/write access to the model (M from MVC) that will be rendered by the view renderer.
Resp model(String name, Object value)
Sets an attribute of the model (M from MVC) that will be rendered by the view renderer.
Resp done()
Informs the HTTP server that the asynchronous handling has finished and the response is complete.
Alias to request().done().
Resp plain(Object content)
Sets the Content-Type: text/plain; charset=utf-8 header and the content of the HTTP response.
Alias to contentType(MediaType.PLAIN_TEXT_UTF_8).body(content).
Resp html(Object content)
Sets the Content-Type: text/html; charset=utf-8 header and the content of the HTTP response.
Alias to contentType(MediaType.HTML_UTF_8).body(content).
Resp json(Object content)
Sets the Content-Type: application/json header and the content of the HTTP response.
Alias to contentType(MediaType.JSON).body(content).
Resp binary(Object content)
Sets the Content-Type: application/octet-stream header and the content of the HTTP response.
Alias to contentType(MediaType.BINARY).body(content).
boolean mvc()
Checks whether the response model and view will be rendered in a MVC fashion.
A typical renderer would use Resp#view to get the view name, and Resp#model to get the model. A custom view renderer can be configured/implemented via the On.custom().viewResolver(...) method.
Resp mvc(boolean mvc)
Sets whether the response model and view will be rendered in a MVC fashion.
A typical renderer would use Resp#view to get the view name, and Resp#model to get the model. A custom view renderer can be configured/implemented via the On.custom().viewResolver(...) method.
OutputStream out()
First renders the response headers, then returns an OutputStream representing the response body. The response body will be constructed by writing to the OutputStream.
Req request()
Gets the reference to the request object.
boolean login(String username, String password)
Initiates a user login process with the specified username and password.
After a successful login, the username will be persisted in the token.
Returns information whether the login was successful
void logout()
Initiates a user logout process, clearing the login information (username) from the token.
Screen screen()
Provides access to the screen model for custom (MVC) page rendering.
void resume(AsyncLogic asyncLogic)
Resumes the asynchronous request handling.

28. Examples

28.1. Automatic Construction and Serialization of JavaBeans

Book.java
package org.rapidoid.docs.beanjson;

public class Book {

        public String title = "Untitled";

        private int year;

        public int getYear() {
                return year;
        }

        public void setYear(int year) {
                this.year = year;
        }
}
EasyBeans.java
package org.rapidoid.docs.beanjson;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;

@Controller
public class EasyBeans {

        @GET
        public Book echo(Book book) {
                return book;
        }

}
Main.java
package org.rapidoid.docs.beanjson;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}

28.2. The default configuration

Main.java
package org.rapidoid.docs.defaultcfg;

import org.rapidoid.goodies.ConfigHandler;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* Let's take a look at the default configuration in Rapidoid */
                On.get("/").mvc(new ConfigHandler());
        }

}

28.3. Edit bean properties

Main.java
package org.rapidoid.docs.editmovie;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
Movie.java
package org.rapidoid.docs.editmovie;

public class Movie {
        public String title;
        public int year;
}
Movies.java
package org.rapidoid.docs.editmovie;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;
import org.rapidoid.docs.showmovie.Movie;
import org.rapidoid.gui.Btn;
import org.rapidoid.gui.GUI;
import org.rapidoid.gui.input.Form;

@Controller
public class Movies {

        @Page("/")
        public Object movie() {
                org.rapidoid.docs.showmovie.Movie movie = new Movie();
                movie.title = "Chappie";
                movie.year = 2015;

                Btn save = GUI.btn("Save").primary();
                Form form = GUI.edit(movie).buttons(save);
                return GUI.page(form).brand("Edit movie details");
        }

}

28.4. Exception handlers

Main.java
package org.rapidoid.docs.errhandling;

import org.rapidoid.http.Req;
import org.rapidoid.setup.App;
import org.rapidoid.setup.My;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                App.run(args);

                My.errorHandler((req, resp, error) -> {
                        return resp.code(200).result("Error: " + error.getMessage());
                });

                On.get("/hi").html((Req req) -> {
                        throw new RuntimeException("problem!");
                });
        }

}

28.5. RESTful services with Lambda handlers, JPA, Jackson and Bean Validation

Book.java
package org.rapidoid.docs.gettingstarted;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;

@Entity
public class Book {

        @Id
        @GeneratedValue
        public Long id;

        @NotNull
        public String title;

        public int year;

}
Main.java
package org.rapidoid.docs.gettingstarted;

import org.rapidoid.annotation.Valid;
import org.rapidoid.jpa.JPA;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args).jpa(); // bootstrap JPA

                On.get("/books").json(() -> JPA.of(Book.class).all()); // get all books

                On.post("/books").json((@Valid Book b) -> JPA.save(b)); // insert new book if valid
        }

}

28.6. Configuring Rapidoid

Book.java
package org.rapidoid.docs.gettingstarted2;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;

@Entity
public class Book {

        @Id
        @GeneratedValue
        public Long id;

        @NotNull
        public String title;

        public int year;

}
Main.java
package org.rapidoid.docs.gettingstarted2;

import org.rapidoid.annotation.Valid;
import org.rapidoid.jpa.JPA;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args).jpa(); // bootstrap JPA

                On.get("/books").json(() -> JPA.of(Book.class).all()); // get all books

                On.post("/books").json((@Valid Book b) -> JPA.save(b)); // insert new book if valid
        }

}
config-default.yml
jdbc:
  driver: org.hsqldb.jdbc.JDBCDriver
  url: jdbc:hsqldb:mem:public
  username: sa
  password: ''

hibernate:
  dialect: org.hibernate.dialect.HSQLDialect
config-dev.yml
hibernate:
  format_sql: false
  show_sql: true
  hbm2ddl:
    auto: update

c3p0:
  debug: true
config-mysql.yml
jdbc:
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/rapidoid
  username: root
  password: root

hibernate:
  dialect: org.hibernate.dialect.MySQL5Dialect
config.yml
on:
  port: 8888
  address: 0.0.0.0

admin:
  port: same
  address: 0.0.0.0

app:
  home: /
  contextPath: /

gui:
  domain: ''
  brand: App
  search: false
  navbar: true
  fluid: false
  cdn: auto # in DEV mode is false, in PRODUCTION is true
  menu: {}

users:
  root:
    roles:
      - administrator
      - owner
    password: root # PLEASE change this

28.7. Hello, web pages!

Hello.java
package org.rapidoid.docs.hello;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;

@Controller
public class Hello {

        @Page("/")
        public String hello() {
                return "Hello, world!";
        }

}
Main.java
package org.rapidoid.docs.hello;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}

28.8. Hello RESTful services!

Main.java
package org.rapidoid.docs.hellorest;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
MyCtrl.java
package org.rapidoid.docs.hellorest;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;

@Controller
public class MyCtrl {

        @GET
        public String hello() {
                return "Hello, world!";
        }

}

28.9. Building HTML pages with Java

Main.java
package org.rapidoid.docs.hi;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
YourName.java
package org.rapidoid.docs.hi;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;
import org.rapidoid.gui.GUI;
import org.rapidoid.html.Tag;
import org.rapidoid.web.Screen;

@Controller
public class YourName {

        @Page("/hi/{name}")
        public Screen hi(String name) {
                Tag msg = GUI.h4("Hi, ", GUI.i(name), "!");
                return GUI.page(msg).brand("What is your name?");
        }

}

28.10. Highlighting Text by Regular Expression Match

Highlighting.java
package org.rapidoid.docs.highlighting;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;
import org.rapidoid.gui.GUI;

@Controller
public class Highlighting extends GUI {

        @Page("/")
        public Object letters() {
                return highlight("ab-cd-efg", "\\w+");
        }

}
Main.java
package org.rapidoid.docs.highlighting;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}

28.11. Server Configuration

Main.java
package org.rapidoid.docs.httpargs;

import org.rapidoid.http.Req;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* Initialize the configuration */
                App.run(args);

                On.get("/hi").json((Req req) -> req.data("name", "unknown"));
        }

}

28.12. Asynchronous request processing

Main.java
package org.rapidoid.docs.httpasync;

import org.rapidoid.http.Req;
import org.rapidoid.job.Jobs;
import org.rapidoid.setup.On;

import java.util.concurrent.TimeUnit;

public class Main {

        public static void main(String[] args) {
                /* Wait 1 second before returning a response */

                On.get("/").json((Req req) -> Jobs.schedule(() -> {

                        req.response().result("OK").done();

                }, 1, TimeUnit.SECONDS));
        }

}

28.13. More control over the HTTP server setup

Main.java
package org.rapidoid.docs.httpcustom;

import org.rapidoid.config.Conf;
import org.rapidoid.setup.Admin;
import org.rapidoid.setup.App;
import org.rapidoid.setup.AppBootstrap;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {

                // first thing to do - initializing Rapidoid, without bootstrapping anything at the moment
                AppBootstrap bootstrap = App.run(args); // instead of App.bootstrap(args), which might start the server

                // customizing the server address and port - before the server is bootstrapped
                On.address("0.0.0.0").port(9998);
                Admin.address("127.0.0.1").port(9999);

                // fine-tuning the HTTP server
                Conf.HTTP.set("maxPipeline", 32);
                Conf.NET.set("bufSizeKB", 16);

                // now bootstrap some components, e.g. built-in services, classpath scanning (beans), JMX, Admin Center
                bootstrap.services().beans().jmx().adminCenter();

                // continue with normal setup
                On.get("/x").json("x");
        }

}

28.14. Generic handlers match any request

Main.java
package org.rapidoid.docs.httpgeneric;

import org.rapidoid.setup.On;
import org.rapidoid.u.U;

public class Main {

        public static void main(String[] args) {
                /* Generic handlers match any request (in the declaration order) */

                On.req(req -> req.data().isEmpty() ? "Simple: " + req.uri() : null);

                /* The next handler is executed if the previous returns [NOT FOUND] */

                On.req(req -> U.list(req.verb(), req.uri(), req.data()));
        }

}

28.15. Returning a "Not Found" result

Main.java
package org.rapidoid.docs.httpnotfound;

import org.rapidoid.http.Req;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* Returning a [null] means [NOT FOUND] */

                On.get("/").json((Req req) -> {
                        return req.params().size() == 1 ? req.params() : null;
                });
        }

}

28.16. HTTP redirect

Main.java
package org.rapidoid.docs.httpredir;

import org.rapidoid.http.Req;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                On.get("/").html((Req x) -> x.response().redirect("/hi"));
                On.get("/hi").html("Hi!");
        }

}

28.17. All the request data is in the (Req req) parameter

Main.java
package org.rapidoid.docs.httpreq1;

import org.rapidoid.http.Req;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* Retrieving request info from the Req parameter */

                On.get("/showVerb").json((Req req) -> req.verb());

                On.get("/showPath").json((Req req) -> req.path());

                On.get("/showUri").json((Req req) -> req.uri());

                On.get("/showData").json((Req req) -> req.data());
        }

}

28.18. Rendering a HTTP response

Main.java
package org.rapidoid.docs.httpresp;

import org.rapidoid.http.MediaType;
import org.rapidoid.http.Req;
import org.rapidoid.http.Resp;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {

            /* Returning the request or response object means the response was constructed */

                On.get("/").html((Req req) -> {
                        Resp resp = req.response();
                        resp.contentType(MediaType.JSON);
                        resp.result("hello");
                        return resp;
                });
        }

}

28.19. Manipulating the response code

Main.java
package org.rapidoid.docs.httprespcode;

import org.rapidoid.http.Req;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                On.get("/").html((Req req) -> req.response().result("").code(404));
        }

}

28.20. Manipulating the response content type

Main.java
package org.rapidoid.docs.httpresptype;

import org.rapidoid.http.MediaType;
import org.rapidoid.http.Req;
import org.rapidoid.http.Resp;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* The response type will be JSON, instead of HTML */

                On.get("/").html((Req req) -> {
                        Resp resp = req.response();
                        resp.contentType(MediaType.JSON);
                        resp.result("abc");
                        return resp;
                });
        }

}

28.21. HTTP handlers and routing

Main.java
package org.rapidoid.docs.httproute;

import org.rapidoid.http.Req;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* Request handlers should match both the verb and the path: */

                On.get("/").json("Hi!");

                On.get("/x").html("Getting X");

                On.post("/x").json((Req req) -> "Posting X");

                On.delete("/x").html((Req req) -> "<b>Deleting X</b>");
        }

}

28.22. Server-side session (a.k.a. Session)

Main.java
package org.rapidoid.docs.httpsessionmem;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                On.req(req -> {
                        int counter = req.session("n", 0) + 1;
                        req.session().put("n", counter);
                        return counter;
                });
        }

}

28.23. RESTful service one-liner

Main.java
package org.rapidoid.docs.httpsimple;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* On [GET /size] return the length of the "msg" parameter */

                On.get("/size").json((String msg) -> msg.length());
        }

}

28.24. Instant web application

Main.java
package org.rapidoid.docs.httpsimplegui;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* On [GET /hi] or [POST /hi] return a "Hello World" web page */

                On.page("/hi").mvc("Hello <b>world</b>!");
        }

}
config.yml
gui:
  brand: 'Cool app!'
  title: 'the head title'
  search: true
  menu:
    Home: /
    Portfolio: /portfolio
    About:
      About Us: /about
      About You: /

28.25. Stopping the server

Main.java
package org.rapidoid.docs.httpstop;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                On.setup().shutdown();
        }

}

28.26. Client-side Token as a session

Main.java
package org.rapidoid.docs.httptoken;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                On.req(req -> {
                        int counter = req.token("n", 0) + 1;
                        req.token().put("n", counter);
                        return counter;
                });
        }

}

28.27. Request wrappers (interceptors)

Main.java
package org.rapidoid.docs.httpwrap;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                /* A wrapper executes before the handler */

                On.defaults().wrappers((req, next) -> {
                        return next.invokeAndTransformResult(result -> "Hey: " + result);
                });

                /* and provides transformation for the result */

                On.get("/size").json((String s) -> s.length());
                On.get("/upper").json((String s) -> s.toUpperCase());
        }

}

28.28. Dependency injection of singletons

Bar.java
package org.rapidoid.docs.injection;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;

import javax.inject.Inject;

@Controller("/bar")
public class Bar {

        @Inject
        public Foo foo;

        @GET("/hi")
        public String hello() {
                return foo.msg();
        }

        public String msg() {
                return "Hello from Bar!";
        }

}
Foo.java
package org.rapidoid.docs.injection;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;

import javax.inject.Inject;

@Controller("/foo")
public class Foo {

        @Inject
        public Bar bar;

        private int count;

        @GET("/hi")
        public String hello() {
                return ++count + ": " + bar.msg();
        }

        public String msg() {
                return "Hello from Foo!";
        }

}
Main.java
package org.rapidoid.docs.injection;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}

28.29. RESTful services with JPA CRUD

Book.java
package org.rapidoid.docs.jpacrud;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;

@Entity
public class Book {

        @Id
        @GeneratedValue
        public Long id;

        @NotNull
        public String title;

        public int year;

}
Main.java
package org.rapidoid.docs.jpacrud;

import org.rapidoid.jpa.JPA;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

import javax.validation.Valid;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args).jpa(); // bootstrap JPA

                On.get("/books").json(() -> JPA.of(Book.class).all());
                On.get("/books/{id}").json((Integer id) -> JPA.get(Book.class, id));

                On.post("/books").json((@Valid Book b) -> JPA.save(b));
                On.put("/books").json((@Valid Book b) -> JPA.update(b));
        }

}

28.30. JPA Scaffolding

Book.java
package org.rapidoid.docs.jpascaffold;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Book {

        @Id
        @GeneratedValue
        public Long id;

        public String title;

        public int year;

}
Main.java
package org.rapidoid.docs.jpascaffold;

import org.rapidoid.goodies.X;
import org.rapidoid.gui.GUI;
import org.rapidoid.jpa.JPA;
import org.rapidoid.setup.On;
import org.rapidoid.u.U;

import java.util.List;

public class Main extends GUI {

        public static void main(String[] args) {

                On.page("/").mvc("Welcome!");
                X.scaffold(Book.class);

                String search = "FROM Book b WHERE b.title LIKE ?1";
                On.page("/search").mvc((String q) -> {
                        List<Book> records = JPA.jpql(search, "%" + q + "%").all();
                        return U.list(h2("Searching for: ", q), grid(records));
                });
        }

}
config.yml
gui:
  brand: 'Cool app'
  search: true

28.31. Bootstrap-based GUI layout

BootstrapLayout.java
package org.rapidoid.docs.layout;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;
import org.rapidoid.gui.GUI;
import org.rapidoid.html.Tag;

@Controller
public class BootstrapLayout extends GUI {

        @Page("/")
        public Object layout() {
                Tag r1 = row(col4("A"), col4("B"), col4("C"));
                Tag r2 = row(col1("2/12"), col7("7/12"), col4("3/12"));
                Tag r3 = mid4("4/12 in the middle");
                return multi(r1, r2, r3);
        }

}
Main.java
package org.rapidoid.docs.layout;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}

28.32. MVC architecture with Rapidoid’s built-in template engine

Main.java
package org.rapidoid.docs.mvc;

import org.rapidoid.gui.GUI;
import org.rapidoid.http.Req;
import org.rapidoid.http.Resp;
import org.rapidoid.setup.On;
import org.rapidoid.u.U;

public class Main {

        public static void main(String[] args) {

                /* The handler for [/msg] returns the model */
                /* The default view name is [msg] */
                /* So the corresponding template is [templates/msg.html] */

                On.page("/msg").mvc(() -> {
                        return U.map("count", 12, "oki", GUI.btn("OK"));
                });

                /* A custom view name can be assigned. */
                /* In this case the default view name is [abc], */
                /* but a custom view name [msg] was specified */

                On.get("/abc").view("msg").mvc((Req req, Resp resp) -> {
                        return U.map("count", 100, "oki", "");
                });
        }

}
templates/msg.html
<p>
    You have <span class="badge">${count}</span> new messages.
</p>

@{oki}

28.33. Custom EntityManager provider

Main.java
package org.rapidoid.docs.myem;

import org.rapidoid.jpa.JPA;
import org.rapidoid.setup.My;

import javax.persistence.EntityManager;

public class Main {

        public static void main(String[] args) {

                /* Use the built-in entity manager, and decorate it */

                My.entityManagerProvider(req -> {
                        EntityManager em = JPA.em();
                        /// em = new SomeEntityManagerDecorator(em);
                        return em;
                });

        }

}

28.34. Custom EntityManagerFactory provider

Main.java
package org.rapidoid.docs.myemf;

import org.rapidoid.setup.My;

import javax.persistence.EntityManagerFactory;

public class Main {

        public static void main(String[] args) {

                /* The EntityManagerFactory's should be properly initialized */

                EntityManagerFactory emf1 = null; // FIXME
                EntityManagerFactory emf2 = null; // FIXME

                My.entityManagerFactoryProvider(req -> {
                        return req.path().startsWith("/db1/") ? emf1 : emf2;
                });

        }

}

28.35. Custom template loader

Main.java
package org.rapidoid.docs.mytemplatesloader;

import org.rapidoid.setup.My;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {

                /* Dummy template loader - constructs templates on-the-fly */

                My.templateLoader(filename -> {
                        String tmpl = "In " + filename + ": x = <b>${x}</b>";
                        return tmpl.getBytes();
                });

                // The URL parameters will be the MVC model

                On.get("/showx").mvc((req) -> req.params());
        }

}

28.36. Create key-value data grid from a Map

Main.java
package org.rapidoid.docs.paramgrid;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
ParamGrid.java
package org.rapidoid.docs.paramgrid;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;
import org.rapidoid.gui.GUI;
import org.rapidoid.http.Req;

@Controller
public class ParamGrid {

        @Page
        public Object table(Req req) {
                return GUI.page(GUI.grid(req.params())).brand("Request parameters");
        }

}

28.37. High-level annotation-based POJO controllers

Main.java
package org.rapidoid.docs.pojoctrl;

import org.rapidoid.annotation.GET;
import org.rapidoid.annotation.POST;
import org.rapidoid.annotation.Param;
import org.rapidoid.http.Req;
import org.rapidoid.http.Resp;
import org.rapidoid.setup.App;

public class Main {

        /**
         * Any object can be a POJO controller.<br>
         * Just annotate the request handler methods with: <b>@GET</b>, <b>@POST</b>
         * , <b>@PUT</b>, <b>@DELETE</b> etc.
         */
        public static void main(String[] args) {
                App.run(args);

                App.beans(new Object() {

                        @GET
                        public String upper(@Param("s") String s) {
                                return s.toUpperCase();
                        }

                        @POST
                        public String lower(Req req, Resp resp, @Param("x") String s) {
                                return s.toLowerCase();
                        }

                });
        }

}

28.38. Application Profiles

FooCtrl.java
package org.rapidoid.docs.profiles;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;
import org.rapidoid.annotation.Profiles;

@Controller
@Profiles("foo")
public class FooCtrl {

        @GET
        public String hi() {
                return "hi, FOO controller!";
        }

}
Main.java
package org.rapidoid.docs.profiles;

import org.rapidoid.config.Conf;
import org.rapidoid.env.Env;
import org.rapidoid.gui.GUI;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

import java.util.Map;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args, "profiles=mysql,foo");

                On.get("/profiles").mvc(() -> GUI.display(Env.profiles()));

                Map<String, Object> myConfig = Conf.section("my").toMap();
                On.get("/my").mvc(() -> GUI.grid(myConfig));
        }

}
OtherCtrl.java
package org.rapidoid.docs.profiles;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;
import org.rapidoid.annotation.Profiles;

@Controller
@Profiles("default")
public class OtherCtrl {

        @GET
        public String hi() {
                return "hi, OTHER controller!";
        }

}
config-foo.yml
my:
  msg: 'hello from Foo!'
config.yml
gui:
  navbar: false

my:
  msg: 'hello!'
  desc: 'simple example'

28.39. Raw HTML pages

Main.java
package org.rapidoid.docs.raw;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
Simple.java
package org.rapidoid.docs.raw;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;

@Controller
public class Simple {

        @Page
        public Object simple() {
                return "<p><b>RAW</b> HTML!<p>";
        }

}

28.40. Automatic JSON Serialization of Data Structures

Main.java
package org.rapidoid.docs.restjson;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
TextTools.java
package org.rapidoid.docs.restjson;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;
import org.rapidoid.annotation.POST;
import org.rapidoid.u.U;

import java.util.Map;

@Controller
public class TextTools {

        @GET("/upper/{s}")
        public Map<String, String> upper(String s) {
                String big = s.toUpperCase();
                return U.map("normal", s, "big", big);
        }

        @POST
        public String[] parts(String text) {
                return text.split("-");
        }

}

28.41. Named URL Parameters

Main.java
package org.rapidoid.docs.restnamedparams;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
NamedParams.java
package org.rapidoid.docs.restnamedparams;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;
import org.rapidoid.annotation.Param;

@Controller
public class NamedParams {

        @GET
        public int sum(int x, @Param("y") int z) {
                return x + z;
        }

}

28.42. URL pattern matching

Main.java
package org.rapidoid.docs.restparams;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
SubUrlParams.java
package org.rapidoid.docs.restparams;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;
import org.rapidoid.annotation.POST;
import org.rapidoid.u.U;

@Controller
public class SubUrlParams {

        @GET("/hey/{name}/{age:\\d+}")
        public String hey(String name, int age) {
                return U.frmt("Hey %s (%s)", name, age);
        }

        @POST("/size/{s}")
        public int size(String s) {
                return s.length();
        }

}

28.43. Programmatic setup of reverse proxy and load balancer

Main.java
package org.rapidoid.docs.revproxy;

import org.rapidoid.http.Req;
import org.rapidoid.reverseproxy.Reverse;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);

                On.get("/a*").json(Req::uri);

                Reverse.proxy("/g")
                        .roles("administrator")
                        .cacheTTL(1000)
                        .to("http://upstream1:8080", "http://upstream2:8080")
                        .add();
        }

}

28.44. Custom load balancer

Main.java
package org.rapidoid.docs.revproxylb;

import org.rapidoid.http.Req;
import org.rapidoid.reverseproxy.LoadBalancer;
import org.rapidoid.reverseproxy.ProxyUpstream;
import org.rapidoid.reverseproxy.Reverse;
import org.rapidoid.setup.App;

import java.util.List;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);

                LoadBalancer lb = (Req req, List<ProxyUpstream> candidates) -> {
                        int index = 0; // FIXME implement load balancing strategy
                        return candidates.get(index);
                };

                Reverse.proxy("/")
                        .to("http://upstream1:8080", "http://upstream2:8080")
                        .loadBalancer(lb)
                        .add();
        }

}

28.45. Reverse proxy matching by URI prefix

Main.java
package org.rapidoid.docs.revproxyself;

import org.rapidoid.reverseproxy.Reverse;
import org.rapidoid.setup.App;
import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);

                String fooUpstream = "localhost:8080/foo";

                Reverse.proxy("/bar").to(fooUpstream).add();
                Reverse.proxy("/").to(fooUpstream).add();

                On.get("/foo").html("FOO");
                On.get("/foo/hi").html("FOO HI");
                On.get("/foo/hello").html("FOO HELLO");
                On.get("/bar/hi").html("BAR HI");
        }

}

28.46. Role-based security

Main.java
package org.rapidoid.docs.security;

import org.rapidoid.setup.App;
import org.rapidoid.setup.My;
import org.rapidoid.setup.On;
import org.rapidoid.u.U;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args).auth();

                On.get("/").html((req, resp) -> "this is public!");

                On.get("/manage").roles("manager").html((req, resp) -> "this is private!");

                /* Dummy login: successful if the username is the same as the password */

                My.loginProvider((req, username, password) -> username.equals(password));

                /* Gives the 'manager' role to every logged-in user */

                My.rolesProvider((req, username) -> U.set("manager"));
        }

}
MyCtrl.java
package org.rapidoid.docs.security;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.GET;
import org.rapidoid.security.Role;
import org.rapidoid.security.annotation.Administrator;
import org.rapidoid.security.annotation.Roles;
import org.rapidoid.u.U;

@Controller
public class MyCtrl {

        @GET
        @Administrator
        @Roles({"manager", Role.MODERATOR})
        public Object hi() {
                return U.map("msg", "hi!");
        }

}

28.47. Display bean properties

Main.java
package org.rapidoid.docs.showmovie;

import org.rapidoid.setup.App;

public class Main {

        public static void main(String[] args) {
                App.bootstrap(args);
        }

}
Movie.java
package org.rapidoid.docs.showmovie;

public class Movie {
        public String title;
        public int year;
}
Movies.java
package org.rapidoid.docs.showmovie;

import org.rapidoid.annotation.Controller;
import org.rapidoid.annotation.Page;
import org.rapidoid.gui.GUI;
import org.rapidoid.gui.input.Form;

@Controller
public class Movies {

        @Page("/")
        public Object movie() {
                Movie movie = new Movie();
                movie.title = "Chappie";
                movie.year = 2015;

                Form form = GUI.show(movie).buttons(GUI.btn("OK"));
                return GUI.page(form).brand("Movie details");
        }

}

28.48. Serving static files from the default locations

Main.java
package org.rapidoid.docs.staticfiles;

import org.rapidoid.setup.On;

public class Main {

        public static void main(String[] args) {
                On.get("/").html("Home");
        }

}
c.html
Hello from C!
static/a.html
Hello from A!
static/b.html
Hello from B!