This is the new documentation for Rapidoid (v5.5).
For older versions of Rapidoid, please see v5.3, v5.4 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.
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:
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 containingusername
andpassword
will execute the (custom-configured or default) login handler. -
If the login handler returns
true
, the user was authenticated and the authentication data (username
andlogin 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 theauth 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.
12.4. Custom authentication tokens
package org.rapidoid.docs.httpcustomauth;
import org.rapidoid.http.Self;
import org.rapidoid.setup.App;
import org.rapidoid.setup.My;
import org.rapidoid.setup.On;
import org.rapidoid.u.U;
import org.rapidoid.util.Tokens;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
My.rolesProvider((req, username) -> username.equals("bob") ? U.set("manager") : U.set());
On.get("/hey").roles("manager").json(() -> U.map("msg", "ok"));
// generate a token
String token = Tokens.serialize(U.map("_user", "bob"));
// demo request, prints {"msg":"ok"}
Self.get("/hey?_token=" + token).print();
}
}
13. Configuration
13.1. The built-in configuration
Rapidoid has built-in default configuration which provides sensible defaults for many components of the framework.
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: {}
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
maxPipeline: 0
syncBufs: true
blockingAccept: false
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: 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
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();
}
}
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!";
}
}
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):
jdbc:
driver: org.hsqldb.jdbc.JDBCDriver
url: jdbc:hsqldb:mem:public
username: sa
password: ''
hibernate:
dialect: org.hibernate.dialect.HSQLDialect
center-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
18.2. MySQL
Using MySQL with the mysql
profile:
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:
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 forif
. The traditional{{#x}}…{{/x}}
remains unchanged, having bothforeach
andif
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)
package org.rapidoid.docs.https;
import org.rapidoid.setup.On;
public class Main {
public static void main(String[] args) {
On.get("/hello").plain("hello!");
}
}
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
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
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;
}
}
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;
}
}
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
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
package org.rapidoid.docs.editmovie;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
package org.rapidoid.docs.editmovie;
public class Movie {
public String title;
public int year;
}
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
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
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;
}
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
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;
}
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
}
}
jdbc:
driver: org.hsqldb.jdbc.JDBCDriver
url: jdbc:hsqldb:mem:public
username: sa
password: ''
hibernate:
dialect: org.hibernate.dialect.HSQLDialect
hibernate:
format_sql: false
show_sql: true
hbm2ddl:
auto: update
c3p0:
debug: true
jdbc:
driver: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/rapidoid
username: root
password: root
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
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!
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!";
}
}
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!
package org.rapidoid.docs.hellorest;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
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
package org.rapidoid.docs.hi;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
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
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+");
}
}
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
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
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
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
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
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
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
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
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
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
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
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)
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
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
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>!");
}
}
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
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
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)
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
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!";
}
}
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!";
}
}
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
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;
}
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
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;
}
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 {
@SuppressWarnings("unchecked")
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));
});
}
}
gui:
brand: 'Cool app'
search: true
28.31. Bootstrap-based GUI layout
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);
}
}
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
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", "");
});
}
}
<p>
You have <span class="badge">${count}</span> new messages.
</p>
@{oki}
28.33. Custom EntityManager provider
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
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
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
package org.rapidoid.docs.paramgrid;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
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
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
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!";
}
}
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));
}
}
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!";
}
}
my:
msg: 'hello from Foo!'
gui:
navbar: false
my:
msg: 'hello!'
desc: 'simple example'
28.39. Raw HTML pages
package org.rapidoid.docs.raw;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
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
package org.rapidoid.docs.restjson;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
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
package org.rapidoid.docs.restnamedparams;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
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
package org.rapidoid.docs.restparams;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
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
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
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
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
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"));
}
}
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
package org.rapidoid.docs.showmovie;
import org.rapidoid.setup.App;
public class Main {
public static void main(String[] args) {
App.bootstrap(args);
}
}
package org.rapidoid.docs.showmovie;
public class Movie {
public String title;
public int year;
}
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
package org.rapidoid.docs.staticfiles;
import org.rapidoid.setup.On;
public class Main {
public static void main(String[] args) {
On.get("/").html("Home");
}
}
Hello from C!
Hello from A!
Hello from B!