Documentation

do more, more easily

  • Simple and effective programming model for building small and large scale web applications
  • Built with developer productivity in mind
  • Plain Java DSL for routes (no xml, or similar)
  • Reflection, annotations and dependency injection are kept to a minimum and in some cases is completely optional
  • No classpath hell, the default deployment model uses a normal JVM bootstrap
  • Modules are easy to use and do as little as possible

Jooby keeps it simple yet powerful.

application

A Jooby app looks like:

public class App extends Jooby { // 1.

  {
    // 2. add a route
    get("/", () -> "Hello");
  }

  public static void main(String[] args) {
    // 3. run my app
    run(App::new, args);
  }
}

1) Create a new App extending Jooby

2) Define your application in the instance initializer

3) Run the application.

life cycle

Application provides start and stop events. These events are useful for starting/stopping services.

onStart/onStop events

Start/stop callbacks are accessible via application:

{
   onStart(() -> {
     log.info("starting app");
   });

   onStop(() -> {
     log.info("stopping app");
   });

   onStarted(() -> {
     log.info("app started");
   });
}

Or via module:

public class MyModule implements Jooby.Module {

  public void configure(Env env, Config conf, Binder binder) {

    env.onStart(() -> {
      log.info("starting module");
    });

    env.onStop(() -> {
      log.info("stopping module");
    });

    env.onStarted(() -> {
      log.info("app started");
    });
  }

}

The onStart callbacks are part of bootstrap and executed before the server is ready. The onStarted callbacks are executed when the server is ready.

Modules are covered later all you need to know now is that you can start/stop module as you usually do from your application.

order

Callback order is preserved:

{
  onStart(() -> {
    log.info("first");
  });

  onStart(() -> {
    log.info("second");
  });

  onStart(() -> {
    log.info("third");
  });

}

Order is useful for service dependencies, for example if ServiceB should be started after ServiceA.

service registry

You have access to the the service registry from start/stop events:

{
  onStart(registry -> {
    MyService service = registry.require(MyService.class);
    service.start();
  });

  onStop(registry -> {
    MyService service = registry.require(MyService.class);
    service.stop();
  });
}

PostConstruct/PreDestroy annotations

If you prefer annotations you can do:

@Singleton
public class MyService {

  @PostConstruct
  public void start() {
   // ...
  }

  @PreDestroy
  public void stop() {
    // ...
  }

}

App.java:
{
  lifeCycle(MyService.class);
}

Service must be a Singleton object.

conf, env and logging

Jooby is configurable via Lightbend’s Config library.

By default, Jooby expects to find an application.conf file at the root of the classpath. You can find the .conf file under the conf classpath directory.

getting properties

via script:

{
  get("/", req -> {
    Config conf = require(Config.class);
    String myprop = conf.getString("myprop");
    ...
  });
}

via @Named annotation:

public class Controller {

  @Inject
  public Controller(@Named("myprop") String myprop) {
    ...
  }
}

via Module:


public class MyModule implements Jooby.Module {

  public void configure(Env env, Config conf, Binder binder) {
    String myprop = conf.getString("myprop");
    ...
  }
}

type conversion

Automatic type conversion is provided when a type:

1) Is a primitive, primitive wrapper or String

2) Is an enum

3) Has a public constructor that accepts a single String argument

4) Has a static method valueOf that accepts a single String argument

5) Has a static method fromString that accepts a single String argument. Like java.util.UUID

6) Has a static method forName that accepts a single String argument. Like java.nio.charset.Charset

You’re free to inject the entire com.typesafe.config.Config object or sub-path/tree of it.

environment

Jooby internals and the module system rely on the application.env property. By default, this property is set to: dev.

This special property is represented at runtime with the Env class.

For example: a module might decided to create a connection pool, cache, etc. when application.env isn’t set to dev.

The application.env property can be set as command line argument as well:

Using a fat jar:

java -jar myfat.jar prod

Using stork:

bin/myapp --start prod

turning features on or off

As described before, the application.env property defines the environment where the application is being executed. It’s possible to turn on/off specific features based on the application environment:

{
  on("dev", () -> {
    use(new DevModule());
  });

  on("prod", () -> {
    use(new ProdModule());
  });
}

There is a ~ (complement) operator:

{
  on("dev", () -> {
    use(new DevModule());
  }).orElse(() -> {
    use(new ProdModule());
  });
}

The environment callback has access to the config object, see:

{
  on("dev", conf -> {
    use(new DevModule(conf.getString("myprop")));
  });
}

special properties

Here is the list of special properties available in Jooby:

  • pid: application process ID.
  • application.name: describes the name of your application. Default is: single package name of where you define your bootstrap class, for example for com.foo.App application’s name is foo.
  • application.version: application version. Default is: getClass().getPackage().getImplementationVersion() (automatically set by Maven).
  • application.class: fully qualified name of the bootstrap class.
  • application.secret: If present, the session cookie will be signed with the application.secret.
  • application.tmpdir: location of the application temporary directory. Default is: ${java.io.tmpdir}/${application.name}.
  • application.charset: charset to use. Default is: UTF-8.
  • application.lang: locale to use. Default is: Locale.getDefault().
  • application.dateFormat: date format to use. Default is: dd-MM-yyyy.
  • application.numberFormat: number format to use. Default is: DecimalFormat.getInstance("application.lang").
  • application.tz: time zone to use. Default is: ZoneId.systemDefault().getId().
  • runtime.processors: number of available processors.
  • runtime.processors-plus1: number of processors.
  • runtime.processors-plus2: number of processors + 2.
  • runtime.processors-x2: number of processors * 2.

precedence

Configuration files are loaded in the following order:

  • system properties
  • arguments properties
  • (file://[application].[env].[conf])?
  • (cp://[application].[env].[conf])?
  • ([application].[conf])?
  • [module].[conf]*

The first occurence of a property will take precedence.

system properties

System properties can override any other property. A system property is set at startup time, like:

java -Dapplication.env=prod -jar myapp.jar

arguments properties

Command line arguments has precedence over file system or classpath configuration files:

java -jar myapp.jar application.env=prod

Or using the shortcut for application.env:

java -jar myapp.jar prod

Unqualified properties are bound to application, so:

java -jar myapp.jar port=8888 path=/myapp

automatically translate to:

java -jar myapp.jar application.port=8888 application.path=/myapp

file://[application].[env].[conf]

A file system configuration file has precedence over classpath configuration file. Usefult to override a classpath configuration file.

cp://[application].[env].[conf]

A classpath system conf file has precedence over default conf file.

application.conf

The default classpath conf file: application.conf

[module].[conf]

A Module might have defined its own set of (default) properties via the Module.config method.

{
  use(new Jdbc());
}

custom .conf

As mentioned earlier, the default conf file is application.conf, but you can use whatever name you prefer:

{
  conf("myapp.conf");
}

example

.
└── conf
    ├── application.conf
    ├── application.uat.conf
    ├── application.prod.conf
    ├── logback.xml
    ├── logback.uat.xml
    └── logback.prod.xml
{
  // import Foo and Bar modules:
  use(new Foo());

  use(new Bar());
}
  • Starting the application in dev produces a conf tree similar to:
.
└── system properties
    ├── command line
    ├── application.conf
    ├── foo.conf
    ├── bar.conf
    ├── ...
    └── ...
  • Starting the application in prod produces a conf tree similar to:
.
└── system properties
    ├── command line
    ├── application.prod.conf
    ├── application.conf
    ├── foo.conf
    ├── bar.conf
    ├── ...
    └── ...

First-listed are higher priority.

For more details, please refer to the config documentation.

logging

Logging is done via logback. Logback bootstrap and configuration is described in detail here.

You will usually find the logback.xml file inside the conf directory. Also, you can define a logback.xml file per application.env by appending the env. See some examples:

  • logback.uat.xml when application.env = uat
  • logback.prod.xml when application.env = prod

If the logback[.env].xml file isn’t present, logback.xml will be used as a fallback.

modules

Modules are a key concept for building reusable and configurable pieces of software.

Modules (unlike in other frameworks) are thin and do a lot of work to bootstrap and configure an external library, but they DO NOT provide a new level of abstraction nor [do] they provide a custom API to access functionality in that library. Instead they expose the library components as they are.

Modules like in Guice are used to wire services, connect data, etc…

There is an extensive module ecosystem which makes Jooby a full stack framework (when needed).

do less and be flexible

Do less might sounds confusing, but is the key to flexibility.

A module does as little as possible (a key difference with other frameworks). A module for a library X should:

  • Bootstrap X
  • Configure X
  • export the API of X

This means that a module should NOT create a wrapper for a library. Instead it should provide a way to extend, configure and use the external library.

This principle keeps modules small, maintainable and flexible.

creating modules

A module is represented by the Jooby.Module class. The configure callback looks like this:

public class M1 implements Jooby.Module {

    public void configure(Env env, Config conf, Binder binder) {
      binder.bind(...).to(...);
    }

}

The configure callback is similar to a Guice module, except you can access to the Env and config objects.

properties

In addition to the configure callback, a module in Jooby has one additional method: Module.config. The config method allows a module to set default properties.

public class M1 implements Jooby.Module {
  public void configure(Env env, Config config, Binder binder) {
    binder.bind(...).to(...);
  }

  public Config config() {
    return ConfigFactory.parseResources(getClass(), "m1.properties");
  }
}

usage

A module must be registered at startup time:


import org.jooby.Jooby;

public class MyApp extends Jooby {

  {
     // as lambda
     use((env, config, binder) -> {
        binder.bind(...).to(...);
     });
     // as instance
     use(new M1());
     use(new M2());
  }

}

You can start or stop services from a module:

public class M1 implements Jooby.Module {
    public void configure(Env env, Config config, Binder binder) {
      env.onStart(() -> {
        // Start services
      });
      env.onStop(() -> {
        // Stop services
      });
    }
}

Or export routes:

public class M1 implements Jooby.Module {
    public void configure(Env env, Config config, Binder binder) {
      Router router = env.router();
      router.get("/m1", () -> "I'm a module!");
    }
}

routes

A route describes the interface for making requests to your application. It combines an HTTP method and a path pattern.

A route has an associated handler, which does some job and produces some kind of output (HTTP response).

Jooby offers two programming models for writing routes:

  • Script routes via lambdas, like Sinatra or expressjs
  • MVC routes via annotations, like Jersey or Spring (covered later)

creating routes

A script route definition looks like this:

{
  get("/", () -> "hey jooby");
}

while an MVC route definition looks like this:


import org.jooby.mvc.GET;
import org.jooby.mvc.Path;

@Path("/")
public class Controller {

  @GET
  public String salute() {
    return "hey jooby";
  }
}

MVC routes are covered later in detail. For simplicity, all the examples use script routes, but keep in mind you can do the same with MVC routes.

A route was created to handle GET requests at the root of our application. Any other HTTP method can be handled the same way.

If you need a POST:

post("/", () -> "hey jooby");

or need to listen to any HTTP method:

use("*", "/", () -> "hey jooby");

It’s also possible to name a route explicitly:

get("/", () -> "hey jooby")
   .name("salute");

The default route name is anonymous. Naming a route is useful for debugging purposes (if you have two or more routes mounted on the same path) and for dynamic and advanced routing.

route handler

Jooby offers several flavors for routes:

functional handler: ()

get("/", () -> "hey jooby");

This handler usually produces a constant value. The returned value will be sent to the client.

functional handler: req

get("/", req -> "hey " + req.param("name").value());

This handler depends on some external attribute which is available via the Request object. The return value will be sent to the client.

handler: (req, rsp)

get("/", (req, rsp) -> rsp.send("hey " + req.param("name").value());

This handler depends on some external attribute which is available to the Request object and the response is explicitly sent via the response.send method.

filter: (req, rsp, chain)

get("/", (req, rsp, chain) -> {
  // do something
  chain.next(req, rsp);
});

This is the most advanced handler. You have access to the Request, the Response and the Route.Chain objects.

From a handler like this, it’s possible to end a response by calling the response.send method, abort the request by throwing an Err or allow the request to proceed to the next handler in the pipeline by calling chain.next(req, rsp).

path patterns

static patterns

get("/", () -> "hey jooby");

get("/help", () -> "hey jooby");

get("/mail/inbox", () -> "hey jooby");

var/regex patterns

get("/user/:id", req -> "hey " + req.param("id").value());

// alternative syntax
get("/user/{id}", req -> "hey " + req.param("id").value());

// matches a path element with the prefix "uid"
get("/user/uid{id}", req -> "hey " + req.param("id").value());

// regex
get("/user/{id:\\d+}", req -> "hey " + req.param("id").intValue());

Request parameters will be covered later. For now all you need to know is that you can access a path parameter using Request.param(String).

ant style patterns

com/t?st.html - matches com/test.html but also com/tast.html and com/txst.html

com/*.html - matches all .html files in the com directory

com/**/test.html - matches all test.html files underneath the com path

** - matches any path at any level

* - matches any path at any level, shortcut for **

**:name or {name:**} - matches any path at any level and binds the match to the request parameter name

static files

Static files are located inside the public directory.

├── public
    ├── assets
    |   ├── js
    |   |   └── index.js
    |   ├── css
    |   |   └── style.css
    |   └── images
    |       └── logo.png
    └── welcome.html

The assets method let you expose all the content from a folder:

{
  assets("/assets/**");
}

The asset route handler resolves requests like:

GET /assets/js/index.js
GET /assets/css/style.css

It’s possible to map a single static file to a path:

{
  assets("/", "welcome.html");
}

A GET / will display the static file welcome.html.

Here is another example with webjars:

{
  assets("/assets/**", "/META-INF/resources/webjars/{0}");
}

which would respond to the following requests:

GET /assets/jquery/2.1.3/jquery.js
GET /assets/bootstrap/3.3.4/css/bootstrap.css

file system location

By default the asset handler is able to read files from the public folder, which is a classpath folder.

It’s possible to specify an external file system location as well:

{ assets("/static/**", Paths.get("/www")); } 

A request to /static/images/logo.png is translated to the /www/images/logo.png file.

ETag, Last-Modified and Cache-Control

The assets.etag and assets.lastModified are two boolean properties that control the ETag and Last-Modified headers. Both are enabled by default.

The assets.cache.maxAge controls the Cache-Control header. Allowed value includes: 60, 1h, 365d, etc. This property is off by default: -1.

using a CDN

The asset handler goes one step forward and adds support for serving files from a CDN out of the box.

All you have to do is to define a assets.cdn property:

assets.cdn = "http://d7471vfo50fqt.cloudfront.net"
{
  assets("/assets/**");
}

A GET to /assets/js/index.js will be redirected to: http://d7471vfo50fqt.cloudfront.net/assets/js/index.js

assets module

There is also a powerful and all-round awesome assets module. This module can validate, concatenate, minify or compress JavaScript and CSS assets.

precedence and order

Routes are executed in the order they are defined. That means that the ordering of routes is crucial to the behavior of an application. Let’s examine how this works with some examples:

get("/abc", req -> "first");

get("/abc", req -> "second");

A call to /abc produces a response of first. If we revert the order:

get("/abc", req -> "second");

get("/abc", req -> "first");

It produces a response of second.

As you can see ORDER IS VERY IMPORTANT.

How come it’s legal with two or more routes on the same path?

Because this is how filters for routes are enabled.

A route handler accepts a third parameter, commonly named chain, which refers to the next route handler in line:

get("/abc", (req, rsp, chain) -> {
  System.out.println("first");
  chain.next(req, rsp);
});

get("/abc", (req, rsp) -> {
  rsp.send("second");
});

Again the order of route definition is important. Forgetting this will cause your application behave unpredictably. You will learn more about this behavior in the examples in the next section.

request handling

When a request matching a route definition is made to the server, the associated callback functions kick in to process the request and send a response. This is called a route pipe or stack.

Routes are like a plumbing pipe, with requests starting at the first route and then working their way “down” the route stack processing for each path they match.

Each route handler has the capability to send a response or pass the request on to the next route handler in the current stack.

Route handlers, also have access to the chain object, which happens to be the next callback function in the pipe. To make the chain object available to the callback function, pass it as a method parameter:

get("/", (req, rsp, chain) -> {
  chain.next(req, rsp);
});

If there is no matching callback function after the current callback function, next refers to the built-in 404 error handler, and will be triggered when you call it.

Try to guess the output of:

get("/", (req, rsp, chain) -> rsp.send("first"));

get("/", (req, rsp, chain) -> rsp.send("second"));

get("/", (req, rsp) -> rsp.send("third"));

Will the server print all of them? “first”? “third”?

It prints “first”. The act of doing a rsp.send() will terminate the flow of the request then and there; the request is not passed on to any other route handler.

So, how can you specify multiple handlers for a route, and use them all at the same time? Call the chain.next(req, rsp) function from the callback, without calling rsp.send() (as that would terminate the request). Here is an example:

get("/", (req, rsp, chain) -> {
  System.out.println("first");
  chain.next(req, rsp);
});

get("/", (req, rsp, chain) -> {
  System.out.println("second");
  chain.next(req, rsp);
});


get("/", (req, rsp) -> {
  rsp.send("third");
});

Alternatively, if you always call chain.next(req, rsp) just use the (req, rsp handler:

get("/", (req, rsp) -> {
  System.out.println("first");
});

get("/", (req, rsp) -> {
  System.out.println("second");
});


get("/", (req, rsp) -> {
  rsp.send("third");
});

The third argument is required if you need to decide whether the next route needs to be executed or not. If you always call chain.next(req, rsp) the third argument isn’t required and does exactly what the second argument handler does: it always calls chain.next(req, rsp).

A good example for a filter is to handle e.g. authentication:

get("/", (req, rsp, chain) -> {
  if (condition) {
    // It is OK
    chain.next(req, rsp);
  } else {
    throw new Route.Err(403);
  }
});

content negotiation

A route can produce different results based on the Accept header:

get("/", () ->
  Results
    .when("text/html", ()  -> Results.html("viewname").put("model", model))
    .when("application/json", ()  -> model)
    .when("*", ()  -> Status.NOT_ACCEPTABLE)
);

This will perform content-negotiation on the Accept HTTP header of the request object. It selects a handler for the request, based on the acceptable types ordered by their quality factor. If the header is not specified, the first callback is invoked. When no match is found, the server responds with 406 Not Acceptable, or invokes the default callback: **/*.

interceptors

An interceptor allows to customize an HTTP request and response at three stages:

  • before the actual handler is invoked
  • after the handler was executed, but before we send the response
  • when the response is complete and sent

before

Allows for customized handler execution chains. It will be invoked before the actual handler.

{
  before((req, rsp) -> {
    // your code goes here
  });

}

You are allowed to modify the request and response objects. Please note that the before handler is just syntactic sugar for filter. For example, the before handler was implemented as:

{
  use("*", "*", (req, rsp, chain) -> {
    before(req, rsp);
    chain.next(req, rsp);
  });
}

A before handler must be registered before the actual handler you want to intercept.

{
  before("/path", (req, rsp) -> {
    // your code goes here
  });

  get("/path", req -> {
    // your code goes here
    return ...;
  });

}

If you reverse the order, it won’t work.

Remember: routes are executed in the order they are defined and the pipeline is executed as long as you don’t generate a response.

after

Allows for customization of the response before sending it. It will be invoked at the time a response need to be sent.

{
  after((req, rsp, result) -> {
    // your code goes here
    return result;
  });

}

You are allowed to modify the request, response and result object. The handler returns a result which can be the same or an entirely new result. Please note that the after handler is just syntactical sugar for filter. For example, the after handler was implemented as:

{
  use("*", (req, rsp, chain) -> {
    chain.next(req, new Response.Forwarding(rsp) {
      public void send(Result result) {
        rsp.send(after(req, rsp, result);
      }
    });
  });

}

Since after is implemented by wrapping the response object. An after handler must be registered before the actual handler you want to intercept.

{
  after((req, rsp, result) -> {
    // your code goes here
    return result;
  });

  get("/path", req -> {
    return "hello";
  });

}

If you reverse the order, it won’t work.

Remember: routes are executed in the order they are defined and the pipeline is executed as long you don’t generate a response.

complete

Allows for logging and cleaning up of a request. Invoked after the response is sent.

{
  complete("*", (req, rsp, cause) -> {
    // your code goes here
  });

}

You are NOT allowed to modify the request and response objects. The cause is an Optional with a Throwable useful to identify problems. A common use case for the complete handler is to clean up the request object and to log responses. Please note that the complete handler is just syntactical sugar for filter. For example, the complete handler was implemented as:

{
  use("*", "*", (req, rsp, chain) -> {
    Optional<Throwable> err = Optional.empty();
    try {
      chain.next(req, rsp);
    } catch (Throwable cause) {
      err = Optional.of(cause);
      throw cause;
    } finally {
      complete(req, rsp, err);
    }
  });
}

A complete handler must to be registered before the actual handler you want to intercept.

{
  complete("/path", (req, rsp, cause) -> {
    // your code goes here
  });

  get("/path", req -> {
    return "hello";
  });
}

If you reverse the order, it won’t work.

Remember: routes are executed in the order they are defined and the pipeline is executed as long you don’t generate a response.

example

Suppose you have a transactional resource, like a database connection. The next example shows you how to implement a simple and effective transaction-per-request pattern:

{
  // start transaction
  before("/api/*", (req, rsp) -> {
    DataSource ds = require(DataSource.class);
    Connection connection = ds.getConnection();
    Transaction trx = connection.getTransaction();
    trx.begin();
    req.set("connection", connection);
    return true;
  });

  // commit/rollback transaction
  complete("/api/*", (req, rsp, cause) -> {
    // unbind connection from request
    try(Connection connection = req.unset("connection").get()) {
      Transaction trx = connection.getTransaction();
      if (cause.ifPresent()) {
        trx.rollback();
      } else {
        trx.commit();
      }
    }
  });

  // your transactional routes goes here
  get("/api/something", req -> {
    Connection connection = req.get("connection");
    // work with connection
  });

}

mvc routes

Mvc routes are similar to controllers in Spring and resources in Jersey with some minor enhancements and simplifications.

@Path("/")
public class MyRoutes {

  @GET
  public Result home() {
    return Results.html("home").put("model", model);
  }
}

Annotations are identical to Jersey/JAX-RS and they can be found under the package org.jooby.mvc.

NOTE: Jooby doesn’t implement the JAX-RS specification. That is why it has its own version of the annotations.

An mvc route can be injected by Guice:

@Path("/")
public class MyRoutes {

  @Inject
  public MyRoutes(DepA a, DepB) {
   ...
  }

  @GET
  public Result home() {
    return Results.html("home").put("model", model);
  }

  @GET
  @Path("/search")
  public List<SearchResult> search() {
    List<SearchResult> result = ...;
    return result;
  }

  @POST
  @Path("/form")
  public MyObject submit(MyObject form) {
    ...
    return Results.html("success");
  }
}

NOTE: MVC routes are NOT singleton, unless you explicitly annotated the route as a Singleton:


import javax.inject.Singleton;

@Singleton
@Path("/")
public class MyRoutes {

  @Inject
  public MyRoutes(DepA a, DepB) {
   ...
  }

  @GET
  public Result home() {
    return Results.html("home").put("model", model);
  }

  @GET
  @Path("/search")
  public List<SearchResult> search() {
    List<SearchResult> result = ...;
    return result;
  }

  @POST
  @Path("/form")
  public MyObject submit(MyObject form) {
    ...
    return Results.html("success");
  }
}

registering an mvc route

Mvc routes must be registered, there is no auto-discover feature, no classpath scanning, …, etc.

The order in which you define your routes is very important and it defines how your app will work.

This is one of the reason why mvc routes need to be explicitly registered.

The other reason is that declaring the route explicitly helps to reduce bootstrap time.

So, how do I register an mvc route?

In the same way everything else is registered in Jooby, from your application class:

public class App extends Jooby {
  {
     use(MyRoutes.class);
  }
}

Again, handlers are registered in the order they are declared, so:

@Path("/routes")
public class MyRoutes {
  
  @GET
  public void first() {
     log.info("first");
  }

  @GET
  public void second() {
     log.info("second");
  }

  @GET
  public String third() {
     return "third";
  }
}

A call to /routes will print: first, second and produces a response of third.

If you find the explicit registration odd or have too many MVC routes, checkout the classpath scanner module which automatically find and register MVC routes.

request parameters

A method parameter represents a HTTP parameter:

   @GET
   public List<Object> search(String q) {
    return searcher.doSearch(q);
   }

Here q can be any of the available parameter types and it will resolved as described in the request parameters section.

Optional parameters work in the same way, all you have to do is to declare them as java.util.Optional:

   @GET
   public List<Object> search(Optional<String> q) {
    return searcher.doSearch(q.orElse("*:*"));
   }

Same for multi-value parameters, just declare them as java.util.List, java.util.Set or java.util.SortedSet:

   @GET
   public List<Object> search(List<String> q) {
    return searcher.doSearch(q);
   }

NOTE: The injected collection is immutable.

Same for file upload

   @POST
   public Object formPost(Upload file) {
    ...
   }

Jooby uses the method parameter name and binds that name to a request parameter. If you want an explicit mapping or if the request parameter isn’t a valid Java identifier:

   @GET
   public List<Object> search(@Named("req-param") String reqParam) {
    ...
   }

form submit

A form submitted as application/x-www-form-urlencoded or multipart/form-data doesn’t require anything:

  @POST
  public Result create(MyObject form) {
    ...
  }

request body

Annotate the method parameter with the @Body annotation:

  @POST
  public MyObject create(@Body MyObject object) {
    ... do something with my object
  }

request headers

Annotate the method parameter with the @Header annotation:

   @GET
   public List<Object> search(@Header String myHeader) {
    ...
   }

Or, if the header name isn’t a valid Java identifier:

   @GET
   public List<Object> search(@Header("Last-Modified-Since") long lastModifedSince) {
    ...
   }

response

A methods return type is sent to the client. Some examples:


@GET
public String sayHi(String name) {
  // OK(200)
  return "Hi " + name;
}

@GET
public Result dontSayGoodbye(String name) {
  // NO_CONTENT(204)
  return Results.noContent();
}

If you want to render a view, just return a view instance:

@GET
public Result home() {
  return Results.html("home").put("model", model);
}

If you need to deal with HTTP metadata like: status code, headers, etc… use a [result] as the return type:

@GET
public Result handler() {
  // 201 = created
  return Results.with(model, 201);
}

dynamic / advanced routing

Dynamic routing allows you to filter a pipeline execution chain and produces a custom or even completely different response.

For example, suppose you need to serve different content based on the hostname:

{
   use("*", (req, rsp, chain) -> {
     if (req.hostname().equals("foo.com")) {
       chain.next("/foo-branding", req, rsp);
     } else {
       chain.next("/bar-branding", req, rsp);
     }
   });

   get("/", () -> Results.html("foo")).name("foo-branding");

   get("/", () -> Results.html("bar")).name("bar-branding");
}

This application has two routes under root path: /. Each of these route has a name: foo-branding and bar-branding.

If you load the application from http://foo.com the foo-branding route will be executed, otherwise the bar-branding route.

Dynamic routing is done over route.name(). From filter you provide a name filter via chain.next(name, req, rsp) method.

You can group routes via use(path) method and globally set a name to all the child routes:

{
  use("/")
    .get(() -> Results.html("foo"))
    .get("/api", () -> ...)
    // apply name to all routes
    .name("foo-branding");

  use("/")
    Results.html("bar")
    .get(() -> Results.html("bar"))
    .get("/api", () -> ...)
    // apply name to all routes
    .name("bar-branding");
}

Or group routes via with(Runnable):

{
  with(() -> 
    get("/", () -> Results.html("foo"));

    get("/api", () -> ...);

  ).name("foo-branding");

  with(() -> 
    get("/", () -> Results.html("bar"));

    get("/api", () -> ...);

  ).name("bar-branding");
}

Or group routes in their own application and then merge them into the main application:

public class FooBranding extends Jooby {

  public FooBranding() {
    super("foo-branding");
  }

  {
    get("/", () -> Results.html("foo"));
    ...
  }
}

public class BarBranding extends Jooby {

  public BarBranding() {
    super("bar-branding");
  }

  {
    get("/", () -> Results.html("bar"));
    ...
  }
}

/**
 * Merge everything .
 */
public class App extends Jooby {
  {
     use("*", (req, rsp) -> {
       if (req.hostname().equals("foo.com")) {
         chain.next("/foo-branding", req, rsp);
       } else {
         chain.next("/bar-branding", req, rsp);
       }
     });

     use(new FooBranding());
     use(new BarBranding());
  }
}

Routes and routing in Jooby are so powerful!

properties

Routes have a few properties that let you extend basic functionality in one way or another including:

  • attributes
  • with, map and excludes operators
  • consumes/produces types

attributes

Attributes let you annotate a route at application bootstrap time. It functions like static metadata available at runtime:

{
  get("/path", ..)
    .attr("foo", "bar");
}

An attribute consist of a name and value. Allowed values are primitives, String, enum, class or an array of these types.

Attributes can be accessed at runtime in a request/response cycle. For example, a security module might check for a role attribute, a sitemap generator might check for a priority attribute, etc.

{
  use((req, rsp, chain) -> {
    User user = ...;
    String role = req.route().attr("role");
    if (user.hasRole(role)) {
      chain.next(req, rsp);
    }
    throw new Err(403);
  });
}

In MVC routes you can set attributes for all the web methods:

{
   use(Controller.class)
     .attr("foo", "bar");
}

Or via annotations:

@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public static @interface Role {
  String value();
}

@Path("/path")
public class AdminResource {

  @Role("admin")
  public Object doSomething() {
    ...
  }

}

{
  use("*", (req, rsp) -> {
    System.out.println(req.route().attributes())
  });
}

The previous example will print: {role = admin}.

Any runtime annotation is automatically added as route attributes following these rules:

  • If the annotation has a value method, then we use the annotation’s name as the attribute name.
  • Otherwise, we use the method name as the attribute name.

request attributes vs route attributes:

Route attributes are created at bootstrap. They are global, and once set, they won’t change.

On the other hand, request attributes are created in a request/response cycle.

with operator

The with operator sets attributes, consumes/produces types, exclusions, etc. to one or more routes:

{
  with(() -> {

    get("/admin/1", ...);

    get("/admin/2", ...);

  }).attr("role", "admin");
}

map operator

The map operator converts a route output to something else:

{
  // we got bar.. not foo
  get("/foo", () -> "foo")
    .map(value -> "bar");

  // we got foo.. not bar
  get("/bar", () -> "bar")
    .map(value -> "foo");
}

If you want to apply a single map to several routes:

{
  with(() -> {
    get("/foo", () -> "foo");

    get("/bar", () -> "bar");

  }).map(v -> "foo or bar");
}

You can apply a Mapper to specific types:

{
  with(() -> {
    get("/str", () -> "str");

    get("/int", () -> 1);

  }).map(String v -> "{" + v + "}");
}

A call to /str produces {str}, while /int just 1.

NOTE: You can apply the map operator to routes that produces an output (a.k.a function routes).

For example, the map operator will be silently ignored here:

{
  get("/", (req, rsp) -> {
    rsp.send(...);
  });
}

excludes

The excludes operator ignores what would otherwise have been a route path match:

{
   use("*", (req, rsp) -> {
     // all except /login
   }).excludes("/login");
}

consumes

The consumes operator indicates the type of input the route can handle.

{
  post("/", req -> {
    MyObject json = req.body().to(MyObject.class);

  }).consumes("json");
}

produces

The produces operator indicates the type of output the route can produce.

{
  get("/", req -> {
    return new MyObject();
  }).produces("json");
}

request

The request object contains methods for reading parameters, headers and body (amongst others). In the next section, the most important methods of a request object will be explored. If you need more information, please refer to the javadoc.

parameters

Retrieval of a parameter is done via the: req.param(“name”) method.

The req.param(“name”) method always returns a mutant instance. A mutant has several utility methods for doing type conversion:

get("/", req -> {
  int iparam = req.param("intparam").intValue();

  String str = req.param("str").value();
  String defstr = req.param("str").value("def");

  // custom object type using type conversion
  MyObject object = req.param("object").to(MyObject.class);

  // file upload
  Upload upload = req.file("file");

  // multi value parameter
  List<String> strList = req.param("strList").toList(String.class);

  // custom object type using type conversion
  List<MyObject> listObj = req.param("objList").toList(MyObject.class);

  // custom object type using type conversion
  Set<MyObject> setObj = req.param("objList").toSet(MyObject.class);

  // optional parameter
  Optional<String> optStr = req.param("optional").toOptional();
});

Multiple parameters can be retrieved at once:

GET /search?name=John&age=99&address[country]=AR&address[city]=BA
public class Profile {
  String name;

  int age;

  Address address;

}

public class Address {
  String country;
  String city;
  ...
}
{
  get("/search", req -> {
     Profile profile = req.params(Profile.class);
     System.out.println(profile.getName()); // print John
     System.out.println(profile.getAge()); // print 99
     System.out.println(profile.getAddress()); // print 99
  });
}

Bean classes must have a default constructor or a constructor annotated with javax.inject.Inject.

parameter type and precedence

A request parameter can be present in:

1) path: /user/:id

2) query string: /user?id=...

3) form submit encoded as application/x-www-form-urlencoded or multipart/form-data

(parameters take precedence in the order listed)

For example purposes, let’s consider a poorly constructed API where we have a route handler that accepts an id parameter in all three locations:

A call like:

curl -X POST -d "id=third" http://localhost:8080/user/first?id=second

Produces:

get("/user/:id", req -> {
  // path param at idx = 0
  assertEquals("first", req.param("id").value());
  assertEquals("first", req.param("id").toList().get(0));

  // query param at idx = 1
  assertEquals("second", req.param("id").toList().get(1));

  // form param at idx = 2
  assertEquals("third", req.param("id").toList().get(2));
});

While clearly bad API design, this is a good example of how parameter precedence works.

parameter type conversion

Automatic type conversion is provided when a type:

  • Is a primitive, primitive wrapper or String
  • Is an enum
  • Is an file upload
  • Has a public constructor that accepts a single String argument
  • Has a static method valueOf that accepts a single String argument
  • Has a static method fromString that accepts a single String argument. Like java.util.UUID
  • Has a static method forName that accepts a single String argument. Like java.nio.charset.Charset
  • Is an Optional , List , Set or SortedSet where T satisfies one of the previous rules

Custom type conversion is also possible:


parser((type, ctx) -> {
  if (type.getRawType() == MyType.class) {
    // convert the type here
    return ctx.param(values -> new MyType(values.get(0)));
  }
  // no luck! move to next converter
  return next.next();
});

get("/", req -> {
  MyType myType = req.param("value").to(MyType.class);
});

See parser and renderer.

headers

Retrieval of request headers is done via: req.header(“name”). What goes for the request params applies for the headers as well.

body

Retrieval of the request body is done via req.body() or req.body(Class).

A parser is responsible for either parsing or converting the HTTP request body to something else.

There are a few built-in parsers for reading the body as String, primitives, …, etc.

Parsers are explained later. For now, all you need to know is that they can read or parse the HTTP body.

Script API:

{

  post("/save", req -> {
    MyObject object = req.body(MyObject.class);
    ...
  });
}

MVC API:


public class Controller {

  @POST
  @Path("/save")
  public MyObject save(@Body MyObject object) {
    ...
  }
}

NOTE: Don’t use req.body(), req.body(Class) or @Body for a POST request encoded as application/x-www-form-urlencoded or multipart/form-data, see the next section for such requests.

form submit

Form submit parsing is done via req.params(Class) or the req.form(Class) method:

public class Contact {

  private int id;

  private String name;

  private String email;

  public Contact(int id, String name, String email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }
}
<form enctype="application/x-www-form-urlencoded" action="/save" method="post">
  <input name="id" />
  <input name="name" />
  <input name="email" />
</form>

Script API:

{
  post("/save", req -> {
    Contact contact = req.params(Contact.class);
    // save contact...
  });
}

MVC API:

public class Controller {

  @POST
  @Path("/save")
  public Result submit(Contact contact) {
    ...
  }
}

Nested paths are supported via bracket: [name] or dot: .name notation:

public class Contact {

  private int id;

  private String name;

  private String email;

  // nested path
  private Address address;

  public Contact(int id, String name, String email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }
}

public class Address {
  private String line;

  private String state;

  private String country;

  publuc Address(String line, String state, String country) {
    this.line = line;
    this.state = state;
    this.country = country;
  }
}
<form enctype="application/x-www-form-urlencoded" action="/save" method="post">
  <input name="id" />
  <input name="name" />
  <input name="email" />
  <input name="address[line]" />
  <input name="address.state" />
  <input name="address[country]" />
</form>

Tabular data is supported as well:

public class Contact {

  private int id;

  private String name;

  private String email;

  // nested path
  private List<Address> address;

  public Contact(int id, String name, String email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }
}
<form enctype="application/x-www-form-urlencoded" action="/save" method="post">
  <input name="id" />
  <input name="name" />
  <input name="email" />
  <input name="address[0]line" />
  <input name="address[0]state" />
  <input name="address[0]country" />
  <input name="address[1]line" />
  <input name="address[1]state" />
  <input name="address[1]country" />
</form>

NOTE: Constructor injection of nested or tabular objects isn’t supported. Nested/tabular object are injected via either method or field.

The injection rules are defined as follows (higher priority first):

  • There is a only one constructor (nested/tabular data can’t be injected, just simple values).
  • There are more than a single constructor but only one annotated with javax.inject.Inject (nested/tabular data can’t be injected, just simple values).
  • There is a setter like method that matches the parameter name, like name(String), setName(String), etc…
  • There is a field that matches the parameter name

file upload

File uploads are accessible via the: request.file(name) method:

<form enctype="multipart/form-data" action="/upload" method="post">
  <input name="myfile" type="file"/>
</form>
// Script API

{
   post("/upload", req -> {
     Upload upload = req.file("myfile");
     ...
     upload.close();
   });
}

// MVC API
class Controller {

  @Path("/upload") @POST
  public Object upload(Upload myfile) {
    ...
    myfile.close();
  }
}

You must close a file upload in order to release resources:

locals

Local attributes (a.k.a request attributes) are bound to the current request. They are created every time a new request is received and destroyed at the end of the request cycle.

{
  use("*", (req, rsp) -> {
    Object value = ...;
    req.set("var", value);
  });

  get("/locals", req -> {
    // optional local
    Optional<String> ifValue = rsp.ifGet("var");

    // required local
    String value = rsp.get("var");

    // local with default value
    String defvalue = rsp.get("var", "defvalue");

    // all locals
    Map<String, Object> locals = req.attributes();
  });
}

In mvc routes request locals can be injected via the @Local annotation:


  @GET
  public Result localAttr(@Local String var) {
    ...
  }

  @GET
  public Result ifLocalAttr(@Local Optional<String> var) {
    ...
  }

  @GET
  public Result attributes(@Local Map<String, Object> attributes) {
    ...
  }

flash scope

The flash scope is designed to transport success and error messages between requests. It is similar to a Session but the lifecycle is shorter: data is kept for only one request.

The flash scope is implemented as a client side cookie, keeping the application stateless.

usage


import org.jooby.FlashScope;

...

{
  use(new FlashScope());

  get("/", req -> {
    return req.ifFlash("success").orElse("Welcome!");
  });

  post("/", req -> {
    req.flash("success", "The item has been created");
    return Results.redirect("/");
  });

}

The FlashScope is also available on mvc routes via the @Flash annotation:

@Path("/")
public class Controller {
  
  // Access to the flashScope
  @GET
  public Object flashScope(@Flash Map<String, String> flash) {
    ...
  }

  // Access to a required flash attribute
  @GET
  public Object flashAttr(@Flash String foo) {
    ...
  }

  // Access to an optional flash attribute
  @GET
  public Object optionlFlashAttr(@Flash Optional<String> foo) {
    ... 
  }
} 

Flash attributes are accessible from templates by prefixing the attribute’s name with flash.. Here’s a (handlebars.java)[/doc/hbs] example:

{{#if flash.success}}
  {{flash.success}}

{{else}}
  Welcome!

{{/if}}

require

The request object has access to the application registry which give you access to application services.

Access to the registry is available via: request.require(type) methods:

get("/", req -> {
  Foo foo = require(Foo.class);
  return foo.bar();
});

Of course, the require method doesn’t make sense on MVC routes, because in that case you can inject dependencies:

@Path("/")
public class Controller {

  private Foo foo;

  @Inject
  public Controller(Foo foo) {
    this.foo = foo;
  }

  @GET
  public String doSomething() {
    return foo.bar();
  }
}

access log

Log all matching incoming requests using the NCSA format (a.k.a common log format).

usage

{
  use("*", new RequestLog());

  ...
}

Output looks like:

127.0.0.1 - - [04/Oct/2016:17:51:42 +0000] "GET / HTTP/1.1" 200 2

You probably want to configure the RequestLog logger to save output into a new file:

<appender name="ACCESS" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>access.log</file>

  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>access.%d{yyyy-MM-dd}.log</fileNamePattern>
  </rollingPolicy>

  <encoder>
    <pattern>%msg%n</pattern>
  </encoder>
</appender>

<logger name="org.jooby.RequestLogger" additivity="false">
  <appender-ref ref="ACCESS" />

</logger>

Since authentication is provided via a module or custom filter, there is no concept of a logged in or authenticated user. You can still log the current user by setting a user id provider at construction time:

{
  use("*", (req, rsp) -> {

    // authenticate user and set local attribute
    String userId = ...;
    req.set("userId", userId);
  });

  use("*", new RequestLogger(req -> {
    return req.get("userId");
  }));

}

In this example, an application filter sets a userId request attribute and then that userId is provided to the {@link RequestLogger}.

custom log function

By default all logging uses logback which is why the org.jooby.RequestLogger was configured in logback.xml.

If you want to log somewhere else, or want to use a different logging implementation:

{
  use("*", new ResponseLogger()
    .log(line -> {
      System.out.println(line);
    }));
}

Rather than printing the NCSA line to stdout, it can of course be written to a database, a JMS queue, etc.

latency

{
  use("*", new RequestLogger()
      .latency());
}

This will append an entry at the end of the NCSA output representing the number of ms it took to process the request.

extended

{
  use("*", new RequestLogger()
      .extended());
}

Extend the NCSA by adding the Referer and User-Agent headers to the output.

dateFormatter

{
  use("*", new RequestLogger()
      .dateFormatter(ts -> ...));

  // OR
  use("*", new RequestLogger()
      .dateFormatter(DateTimeFormatter...));
}

Override the default formatter for the request arrival time defined by: Request#timestamp(). You can provide a function or an instance of DateTimeFormatter.

The default formatter uses the default server time zone, provided by ZoneId#systemDefault(). It’s possible to simply override the time zone as well:

{
  use("*", new RequestLogger()
     .dateFormatter(ZoneId.of("UTC"));
}

response

The response object contains methods for reading and setting headers, status code and body (amongst other things). In the next section you will encounter the most important methods of the response object. If you need more information please refer to the javadoc.

send

The rsp.send method is responsible for sending and writing data into the HTTP response.

A renderer is responsible for converting a Java Object into something else (json, html, etc..).

Let’s consider a simple example:

get("/", (req, rsp) -> rsp.send("hey jooby"));

get("/", req -> "hey jooby"); // or just return a value and Jooby will call send for you.

The send method will ask the Renderer API to format an object and write a response.

The resulting Content-Type when not set is text/html.

The resulting Status Code when not set is 200.

Some examples:

get("/", req -> {
   // text/html with 200
   String data = ...;
   return data;
});
get("/", (req, rsp) -> {
   // text/plain with 200 explicitly 
   String data = ...;
   rsp.status(200)
        .type("text/plain")
        .send(data);
});

Alternative:

get("/", req -> {
   // text/plain with 200 explicitly 
   String data = ...;
   return Results.with(data, 200)
        .type("text/plain");
});

headers

Retrieval of response headers is done via rsp.header(“name”). This method returns a Mutant which can be converted to any of the supported types.

Setting a header is pretty straightforward as well:

rsp.header("Header-Name", value).header("Header2", value);

send file

The send file API is available through the rsp.download* methods:

{
  get("/download", (req, rsp) -> {
    rsp.download("myfile", new File(...));
  });
}

The download method sets the following headers:

  • Content-Disposition

  • Content-Length

  • Content-Type

In the next example we explicitly set some of these:

{
  get("/download", (req, rsp) -> {
    rsp
      .type("text/plain")
      .header("Content-Disposition", "attachment; filename=myfile.txt;")
      .download(new File(...));
  });
}

session

Sessions are created on demand via: req.ifSession() or req.session().

Sessions have a lot of uses cases but the most commons are: authentication, storing information about current user, etc.

A session attribute must be a String or a primitive. The session doesn’t allow storing of arbitrary objects. It’s intended as a simple mechanism to store basic data.

usage

{
  get("/", req -> {
    Session session = req.session();

    // set attribute
    session.set("foo", "bar");

    // get attribute
    return session.get("foo").value();
  });
}

The previous example will use an in-memory session. The next example uses a cookie:

{
  cookieSession();

  get("/", req -> {
    Session session = req.session();

    // set attribute
    session.set("foo", "bar");

    // get attribute
    return session.get("foo").value();
  });
}

The cookie session store depends on the application.secret property. The cookie will be signed with the value of this property.

As an alternative to the memory or the cookie stores, you can choose any one of the high performance session stores provided by Jooby. There are provided session stores for redis, memcached, mongodb, cassandra, couchbase, hazelcast and a lot more.

{
  cookieSession();

  get("/", req -> {
    Session session = req.session();

    // set attribute
    session.set("foo", "bar");

    // get attribute
    return session.get("foo").value();
  });
}

no timeout

There is no timeout for sessions from the perspective of the server. By default, a session will expire when the user close the browser (a.k.a session cookie) or the cookie session has expired via the maxAge attribute.

Session store implementations might or might not implement a server timeout.

max-age

The session.cookie.maxAge sets the maximum age in seconds. A positive value indicates that the cookie will expire after that many seconds have passed. Note that the value is the maximum age when the cookie will expire, not the cookie’s current age.

A negative value means that the cookie is not stored persistently and will be deleted when the browser exits.

Default maxAge is: -1.

If the application.secret property has been set, the session cookie will be signed with it.

The session.cookie.name indicates the name of the cookie that hold the session ID, by default: jooby.sid. The cookie’s name can be explicitly set with cookie.name(“name”) on Session.Definition#cookie().

error handling

An error handler in Jooby is represented by the Err.Handler class and allows you to log and render exceptions.

default err handler

The default error handler does content negotiation and optionally displays friendly error pages using a naming convention.

{
  use(new TemplateEngine()); // Hbs, Ftl, etc...
  use(new Json()); // A json renderer

  get("/", () -> {
    ...
    throw new IllegalArgumentException();
    ...
  });
}

html

If a request to / has an Accept: text/html header. Then, the default Err handler will ask to a View.Engine to render the err view.

The default model has these attributes:

  • message: exception string
  • stacktrace: exception stack-trace as an array of string
  • status: status code, like 400
  • reason: status code reason, like BAD REQUEST

Here is a simple public/err.html error page:

<html>
<body>
  {{ "{{status" }}}}:{{ "{{reason" }}}}
</body>
</html>

The HTTP status code of the response will be set as well.

no html

If a request to / has an Accept: application/json header, the default Err handler will use a renderer to render the err model.

{ "message": "...", "stacktrace": [], "status": 500, "reason": "..." } 

In both cases, the error model is the result of err.toMap() which creates a lightweight version of the exception.

The HTTP status code of the response will be set as well.

custom err handler

If the default view resolution and/or err model isn’t enough, you can create your own Err handler:

{
  err((req, rsp, err) -> {
    log.err("err found: ", err);
    // do what ever you want here
    rsp.send(...);
  });
}

The Err handlers are executed in the order they were provided (like routes, parsers and renderers). The first Err handler that send an output wins!

catch a specific exception or status code

{
  err(MyException1.class, (req, rsp, err) -> {
    MyException1 cause = (MyException1) err.getCause();
    // handle MyException1
  });

  err(MyException2.class, (req, rsp, err) -> {
    MyException2 cause = (MyException2) err.getCause();
    // handle MyException2
  });

  err((req, rsp, err) -> {
    // handle any other exception
  });
}

Or you can catch exception base on their response status code (see next section):

{
  err(404, (req, rsp, err) -> {
    // handle 404
  });

  err(503, (req, rsp, err) -> {
    // handle 503
  });

  err((req, rsp, err) -> {
    // handle any other exception
  });
}

status code

The default status code for errors is 500, except for:

| Exception                          | Status Code |
| ---------------------------------- | ----------- |
| java.lang.IllegalArgumentException |     400     |
|                                    |             |
| java.util.NoSuchElementException   |     400     |
|                                    |             |
| java.io.FileNotFoundException      |     404     |

custom status codes

Just throw an Err:

throw new Err(403);

or add a new entry in the application.conf file:

err.com.security.Forbidden = 403

When you now throw a com.security.Forbidden exception, the status code will be 403.

parser and renderer

parser

A Parser is responsible for parsing the HTTP parameters or body to something else.

Automatic type conversion is provided when a type:

  • Is a primitive, primitive wrapper or String
  • Is an enum
  • Is an Upload
  • Has a public constructor that accepts a single String argument
  • Has a static method valueOf that accepts a single String argument
  • Has a static method fromString that accepts a single String argument. Like java.util.UUID
  • Has a static method forName that accepts a single String argument. Like java.nio.charset.Charset
  • Is an Optional , List , Set or SortedSet where T satisfies one of previous rules

custom parser

Suppose you want to write a custom parser to convert a value into an integer. In practice you won’t need a parser like that since it’s already provided, this is just an example.

Let’s see how to create our custom HTTP param parser:


parser((type, ctx) -> {
  // 1
  if (type.getRawType() == int.class) {
    // 2
    return ctx.param(values -> Integer.parseInt(values.get(0));
  }
  // 3
  return ctx.next();
});

get("/", req -> {
   int intValue = req.param("v").intValue();
   ...
});

Let’s have a closer look:

1) Check if current type matches our target type

2) Add a param callback

3) Can’t deal with current type, ask the next parser to resolve it

Now, when asking for the HTTP body:

get("/", req -> {
   int intValue = req.body().intValue();
   ...
});

The custom parser won’t be able to parse the HTTP body, since it only works on HTTP parameters. In order to extend the custom parser and use it for the HTTP body:


parser((type, ctx) -> {
  // 1
  if (type.getRawType() == int.class) {
    // 2
    return ctx.param(values -> Integer.parseInt(values.get(0))
       .body(body -> Integer.parseInt(body.text()));
  }
  // 3
  return ctx.next();
});

Now it’s possible to ask for a HTTP param and/or body:

get("/", req -> {
   int intValue = req.param("v").intValue();
   ...
});

post("/", req -> {
   int intValue = req.body().intValue();
   ...
});

The parser API is very powerful. It lets you apply a parser to a HTTP parameter, set of parameters (like a form post), file uploads or a body. You are also free to choose if your parser applies for a Java Type and/or a Media Type, like the Content-Type header.

For example a generic JSON parser looks like:


parser((type, ctx) -> {
  if (ctx.type().name().equals("application/json")) {
    return ctx.body(body -> fromJSON(body.text()));
  }
  return ctx.next();
});

Parsers are executed in the order they are defined.

If a parameter parser isn’t able to resolve a parameter a BAD REQUEST(400) error will be generated.

If a body parser isn’t able to resolve a parameter an UNSUPPORTED_MEDIA_TYPE(415) error will be generated.

renderer

A Renderer converts a Java Object to a series of bytes or text and writes them to the HTTP response.

There are a few built-in renderers:

  • stream: copy an inputstream to the HTTP response and set a default type of: application/octet-stream
  • bytes: copy bytes to the HTTP response and set a default type of: application/octet-stream
  • byteBuffer: copy bytes to the HTTP response and set a default type of: application/octet-stream
  • readable: copy a readable object to the HTTP response and a default type of: text/html
  • text: copy the toString() result to the HTTP response and set a default type of: text/html

custom renderer

Suppose you want to apply custom rendering for MyObject. The renderer is as simple as:


render((value, ctx) -> {
  if (value instanceOf MyObject) {
     ctx.text(value.toString());
  }
});

get("/", req -> {
   return new MyObject();
});

A generic JSON renderer will look like:


render((value, ctx) -> {
  if (ctx.accepts("json")) {
     ctx
       .type("json")
       .text(toJson(value));
  }
});

get("/", req -> {
   return new MyObject();
});

The renderer API is simple and powerful. Renderers are executed in the order they were defined. The renderer which writes the response first wins!

view engine

A view engine is a specialized renderer that ONLY accepts instances of a view.

A view carries the template name + model data:

{
  use(new MyTemplateEngine());

  get("/", req -> Results.html("viewname").put("model", model);

}

In order to support multiple view engines, a view engine is allowed to throw a java.io.FileNotFoundException when it’s unable to resolve a template. This passes control to the next view resolver giving it a chance to load the template.

There’s not much more to say about views and template engines, further details and documentation is provided in the specific module.

thread model

You can see Jooby as an event loop server thanks to the supported web servers: Netty, Jetty and Undertow. The default web server is Netty.

Jooby isn’t a traditional thread server where a HTTP request is bound to a thread.

In Jooby all the HTTP IO operations are performed in async & non blocking fashion. HTTP IO operations run in an IO thread (a.k.a event loop) while the application logic (your code) always run in a worker thread.

worker threads

The worker thread pool is provided by one of the supported web servers: Netty, Jetty or Undertow. To simplify application programming you can block a worker thread, for example you can safely run a jdbc query in a worker thread:

{
  get("/search-db", () -> {
    try(Connection db = require(Connection.class)) {
      try(Statement stt = db.createStatement()) {
        ...
      } 
    }
  });
}

The web server can accept as many connections it can (as its on non blocking) while the worker thread might block.

The default worker thread pool is 20/100. The optimal size depends on the business and work load your application is supposed to handle. It’s recommended to start with the default setup and then tune the size of the thread pool if the need arises.

Jooby favors simplicity over complexity hence allowing your code to block. However, it also provides you with more advanced options that will allow you to build async and reactive applications.

deferred

Async processing is achieved via: deferred result, with a deferred result an application can produces a result from a thread of its choice:

Script API:

{
  get("/async", deferred(() -> {
    return ...;
  });
}

MVC API:

  @GET
  @Path("/async")
  public Deferred async() {
    return Deferred.deferred(() -> {
      return ...;
    });
  }

The previous examples are just syntactic sugar for:

  return new Deferred(deferred -> {
     try {
       deferred.resolve(...);
     } catch (Throwable x) {
       deferred.reject(x);
     }
  });

You can get more syntactic sugar if you add the AsyncMapper to your application:

Script API:

{
  map(new AsyncMapper());
  
   get("/async", () -> {
     Callable<String> callable = () -> {
       return ...;
     }; 
    return callable;
  });
}

MVC API:

@GET
  @Path("/async")
  public Callable<String> async() {
    return () -> {
      return ...;
    };
  }

The AsyncMapper converts java.util.concurrent.Callable and java.util.concurrent.CompletableFuture objects to deferred objects.

Another important thing to notice is that the deferred will run in the caller thread (i.e. worker thread), so by default there is no context switch involved in obtaining a deferred result:

{
  get("/async", () -> {
    String callerThread = Thread.current().getName();
    return Deferred.deferred(() -> {
      assertEquals(callerThread, Thread.current().getName());
      return ...;
    });
  });
}

This might not seem optimal at first, but there are some benefits to this:

  • It makes it very easy to set up a default executor (this will be explained shortly)

  • It provides better integration with async & reactive libraries. A direct executor avoids the need of switching to a new thread and then probably dispatch (again) to a different thread provided by a library.

executor

As previously mentioned, the default executor runs in the caller thread (a.k.a direct executor). Let’s see how to override the default executor:

{
  executor(new ForkJoinPool());

  get("/async", deferred(() -> {
    return ...;
  });
}

Done! Now all our deferred results run in a ForkJoinPool. It’s also possible to specify an alternative executor:

Script API:

{
  executor(new ForkJoinPool());

  executor("jdbc", Executors.newFixedThreadPool(10));

  get("/", deferred(() -> {
    return ...;
  });

  get("/db", deferred("jdbc", () -> {
    return ...;
  });
}

MVC API:


import static org.jooby.Deferred.deferred;
...

  @GET
  @Path("/")
  public Deferred home() {
    return deferred(() -> {
      return ...;
    });
  }

  @GET
  @Path("/db")
  public Deferred db() {
    return deferred("jdbc", () -> {
      return ...;
    });
  }

It’s worth mentioning that the executor(ExecutorService) methods automatically shutdown at application shutdown time.

promise

The deferred contains two useful methods:

These two methods allow you to use a deferred object as a promise:

Script API:

{
  get("/", promise(deferred -> {
    try {
      deferred.resolve(...);
    } catch (Throwable x) {
      deferred.reject(x);
    }
  });
}

MVC API:

  @Path("/")
  @GET
  public Deferred promise() {
    return new Deferred(deferred -> {
      try {
        deferred.resolve(...);
      } catch (Throwable x) {
        deferred.reject(x);
      }
    });
  }

The “promise” version of the deferred object is a key concept for integrating with external libraries.

advanced configuration

Suppose you want to build a truly async application and after a deep analysis of your business demands you realize your application needs to:

  • Access a database
  • Call a remote service
  • Make a CPU intensive computation

These are the 3 points where your application is supposed to block and wait for a result.

Let’s start by reducing the worker thread pool to the number of available processors:

server.threads.Min = ${runtime.processors}
server.threads.Max = ${runtime.processors}

With this change, you need to be careful to avoid any blocking code on routes, otherwise performance will suffer.

Let’s create a custom thread pool for each blocking access:

{
  executor("db", Executors.newCachedThreadPool());
  executor("remote", Executors.newFixedThreadPool(32));
  executor("intensive", Executors.newSingleThreadExecutor());
}

For database access, we use a cached executor which will grow without a limit but free and release threads that are idle after 60s.

For remote service, we use a fixed executor of 32 threads. The number 32 is just a random number for the purpose of the example.

For intensive computations, we use a single thread executor. Computation is too expensive and we want one and only one running at any time.

{
  executor("db", Executors.newCachedThreadPool());
  executor("remote", Executors.newFixedThreadPool(32));
  executor("intensive", Executors.newSingleThreadExecutor());

  get("/nonblocking", () -> "I'm nonblocking");

  get("/list", deferred("db", () -> {
    Database db = require(Database.class);
    return db.fetch();
  });
  
  get("/remote", deferred("remote", () -> {
    RemoteService rs = require(RemoteService.class);
    return rs.call();
  });

  get("/compute", deferred("intensive", () -> {
    return someCPUIntensiveTask();
  });
}

Here’s the same example with rx java:

{
  get("/nonblocking", () -> "I'm nonblocking");

  get("/list", deferred(() -> {
    Database db = require(Database.class);
    Observable.<List<String>> create(s -> {
      s.onNext(db.fetch());
      s.onCompleted();
    }).subscribeOn(Schedulers.io())
      .subscribe(deferred::resolve, deferred::reject);
    }));
  });

  get("/remote", deferred(() -> {
    RemoteService rs = require(RemoteService.class);
    Observable.<List<String>> create(s -> {
      s.onNext(rs.call());
      s.onCompleted();
    }).subscribeOn(Schedulers.io())
      .subscribe(deferred::resolve, deferred::reject);
    }));
  });
  
  get("/compute", deferred(() -> {
    Observable.<List<String>> create(s -> {
      s.onNext(someCPUIntensiveTask());
      s.onCompleted();
    }).subscribeOn(Schedulers.computation())
      .subscribe(deferred::resolve, deferred::reject);
    }));
  });
}

The main differences are:

  • the default executor is kept direct. No new thread is created and context switching is avoided.
  • the deferred object is used as a promise and integrate with rx java.
  • different thread pool semantics is achieved with the help of rx schedulers.

This is just another example to demonstrate the value of the deferred object, since a rxjava module is provided which takes care of binding the deferred object into Observables.

That sums up everything about the deferred object. It allows you to build async and reactive applications and at the same time: keep it simple (a Jooby design goal).

You’re also invited to check out the available async/reactive modules.

tests

This section will show you how to run unit and integration tests with Jooby.

unit tests

There are two available programming models:

  • the script programming model; and
  • the mvc programming model

Testing MVC routes is pretty straightforward since a route is bound to a method of some class. This makes it simple to mock and run unit tests against these kinds of routes.

To test script routes is more involved since a route is represented by a lambda and there is no easy or simple way to get access to the lambda object.

For this reason there’s a MockRouter provided to simplify unit testing of script routes:

usage

public class MyApp extends Jooby {

  {
    get("/test", () -> "Hello unit tests!");
  }
}

A unit test for this route, looks like:

@Test
public void simpleTest() {
  String result = new MockRouter(new MyApp())
     .get("/test");

  assertEquals("Hello unit tests!", result);
}

Just create a new instance of MockRouter with your application and call one of the HTTP method, like get, post, etc…

mocks

You’re free to choose the mock library of your choice. Here is an example using EasyMock:

{
  get("/mock", req -> {
    return req.path();
  });
}

A test with EasyMock looks like:

@Test
public void shouldGetRequestPath() {
  Request req = EasyMock.createMock(Request.class);

  expect(req.path()).andReturn("/mypath");

  EasyMock.replay(req);

  String result = new MockRouter(new MyApp(), req)
     .get("/mock");

  assertEquals("/mypath", result);

  EasyMock.verify(req);

}

You can mock a Response object similarly:

{
  get("/mock", (req, rsp) -> {
    rsp.send("OK");
  });
}

A test with EasyMock looks like:

@Test
public void shouldUseResponseSend() {
  Request req = EasyMock.createMock(Request.class);

  Response rsp = EasyMock.createMock(Response.class);

  rsp.send("OK");

  EasyMock.replay(req, rsp);

  String result = new MockRouter(new MyApp(), req, rsp)
     .get("/mock");

  assertEquals("OK", result);

  EasyMock.verify(req, rsp);

}

What about external dependencies? This is handled similarly:

{
  get("/", () -> {
    HelloService service = require(HelloService.class);
    return service.salute();
  });
}
@Test
public void shouldMockExternalDependencies() {
  HelloService service = EasyMock.createMock(HelloService.class);

  expect(service.salute()).andReturn("Hola!");

  EasyMock.replay(service);

  String result = new MockRouter(new MyApp())
     .set(service)
     .get("/");

  assertEquals("Hola!", result);

  EasyMock.verify(service);

}

The MockRouter#set(Object) calls push and will register an external dependency (usually a mock). This makes it possible to resolve services from require calls.

deferred

Mocking of promises is possible as well:

{
  get("/", promise(deferred -> {
    deferred.resolve("OK");
  }));

}
@Test
public void shouldMockPromises() {
  String result = new MockRouter(new MyApp())
     .get("/");

  assertEquals("OK", result);
}

The previous test works for deferred routes:

{
  get("/", deferred(() -> {
    return "OK";
  }));

}

integration tests

Integration tests are possible thanks to a JUnit Rule.

You can choose between @ClassRule or @Rule. The following example uses ClassRule:


import org.jooby.test.JoobyRule;

public class MyIntegrationTest {

  @ClassRule
  private static JoobyRule bootstrap = new JoobyRule(new MyApp());
  
}

Here one and only one instance will be created, which means the application will start before the first test and stop after the last test. Application state is shared between tests.

With Rule on the other hand, a new application is created per test. If you have N tests, the application will start and stop N times:


import org.jooby.test.JoobyRule;

public class MyIntegrationTest {

  @Rule
  private JoobyRule bootstrap = new JoobyRule(new MyApp());
}

Again you are free to select the HTTP client of your choice, like Fluent Apache HTTP client, REST Assured, etc.

Here is a full example with REST Assured:

import org.jooby.Jooby;

public class MyApp extends Jooby {

  {
    get("/", () -> "I'm real");
  }

}

import org.jooby.test.JoobyRyle;

public class MyIntegrationTest {

  @ClassRule
  static JoobyRule bootstrap = new JoobyRule(new MyApp());

  @Test
  public void integrationTestJustWorks() {
    get("/")
      .then()
      .assertThat()
      .body(equalTo("I'm real"));
  }
}

That’s all for now, enjoy testing!

https

HTTPS is supported across the available server implementations. To enable HTTPS, you need to set:

application.securePort = 8443

Or programmatically like:

{
  securePort(8443);
}

certificates

Jooby comes with a self-signed certificate, useful for development and test. But of course, you should NEVER use it in the real world.

In order to setup HTTPS with a real certificate, you need to set these properties:

  • ssl.keystore.cert: An X.509 certificate chain file in PEM format. It can be an absolute path or a classpath resource.
  • ssl.keystore.key: A PKCS#8 private key file in PEM format. It can be an absolute path or a classpath resource.

Optionally, you can set these properties too:

  • ssl.keystore.password: Password of the keystore.key (if any). Default is: null/empty.
  • ssl.trust.cert: Trusted certificates for verifying the remote endpoint’s certificate. The file should contain an X.509 certificate chain in PEM format. Default uses the system default.
  • ssl.session.cacheSize: Set the size of the cache used for storing SSL session objects. 0 to use the default value.
  • ssl.session.timeout: Timeout for the cached SSL session objects, in seconds. 0 to use the default value.

As you can see, setup is very simple. All you need is your .crt and .key files.

At startup you will see a line similar to this one:

    https://localhost:8443

http/2

HTTP/2.0 is supported across the available server implementations. To enable HTTP/2.0 you need to set these properties:

server.http2.enabled = true

application.securePort = 8443

Or programmatically like:

{
  // HTTP/2.0 clear text (a.k.a h2c)
  http2();

  // HTTP/2.0 over TLS
  securePort(8443);

  get("/", req -> req.protocol());
}

Browsers don’t support H2C (H2 clear text) which is why you need to configure HTTPS as well.

At bootstrap time you will see a +h2 flag that indicates that HTTP/2.0 is enabled:

   http://localhost:8080/ +h2
   https://localhost:8443/ +h2

Some servers require special configuration (besides HTTPS). So it’s always good practice to check the HTTP/2.0 section of the server implementation to learn more about current support, limitations and/or configuration steps.

server push

Server push is supported via Request.push:

{
  // HTTP/2.0 clear text (a.k.a h2c)
  http2();

  // HTTP/2.0 over TLS
  securePort(8443);

  assets("/js/**");

  get("/", req -> {

    req.push("/js/home.js");

    return Results.html("home");
  });
}

When the browser requests the home page, we will send the /js/home.js resource as well!

That’s all, but keep in mind that each server implementation might require some extra configuration steps to work, please check out the server documentation for more details.

web sockets

usage

{
  ws("/", ws -> {
    ws.onMessage(message -> System.out.println(message.value()));

    ws.send("connected");
  });
}

A WebSocket consists of a path pattern and a handler.

A path pattern can be as simple or complex as you need. All the path patterns supported by routes are supported.

The onOpen listener is executed on new connections, from there it’s possible to listen for messages and errors or to send data to the client.

Keep in mind that a WebSocket is different from a route. There is no stack/pipe or chain.

You can mount a socket to a path used by a route, but you can’t have two or more WebSockets under the same path.

send and broadcast

As seen earlier, a web socket can send data to the client via the ws.send(…) method.

The ws.broadcast(…) method does the same thing, but will send to all connected clients.

require

Access to existing services is provided through the ws.require(type) method:

ws("/", ws -> {
  A a = ws.require(A.class);
});

listener

As with routes you can choose between two flavors for WebSocket listeners:

script:

{
  ws("/ws", ws -> {
    MyDatabase db = ws.require(MyDatabase.class);
    ws.onMessage(msg -> {
      String value = msg.value();
      database.save(value);
      ws.send("Got: " + value);
    });
  });
}

class:

{
  ws(MyHandler.class);
}

@Path("/ws")
class MyHandler implements WebSocket.OnMessage<String> {

  WebSocket ws;
  
  MyDatabase db;

  @Inject
  public MyHandle(WebSocket ws, MyDatabase db) {
    this.ws = ws;
    this.db = db;
  }

  @Override
  public void onMessage(String message) {
    database.save(value);
    ws.send("Got: " + message);
  }
}

Optionally, your listener could implement onOpen, onClose or onError. If you need all of them, use the handler interface.

consumes

A WebSocket can define a type to consume:

{
  ws("/", ws -> {
    ws.onMessage(message -> {
      MyObject object = message.to(MyObject.class);
    });

    ws.send("connected");
  })
  .consumes("json");
}

Or via annotation for class listeners:

@Path("/ws")
@Consumes("json")
class MyHandler implements WebSocket.OnMessage<MyObject> {

  public void onMessage(MyObject object) {
   ...
  }
}

This is just a utility method for parsing a socket message into a Java Object. Consumes in WebSockets has nothing to do with content negotiation. Content negotiation is a route concept, it doesn’t apply for WebSockets.

produces

A WebSocket can define a type to produce:

{
  ws("/", ws -> {
    MyResponseObject object = ..;
    ws.send(object);
  })
  .produces("json");
}

Or via annotation for class listeners:

@Path("/ws")
@Consumes("json")
@Produces("json")
class MyHandler implements WebSocket.OnMessage<MyObject> {

  public void onMessage(MyObject object) {
   ws.send(new MyResponseObject());
  }
}

This is just a utility method for formatting Java Objects as text messages. Produces in WebSockets has nothing to do with content negotiation. Content negotiation is a route concept, it doesn’t apply for WebSockets.

server-sent events

Server-Sent Events (SSE) is a mechanism that allows the server to push data to the client once the client-server connection is established. After the connection has been established by the client, the server can send to the client whenever a new chunk of data is available. In contrast with websockets, SSE can only be used to send from the server to the client and not the other way round.

usage

{
  sse("/path", sse -> {

    // 1. connected
    sse.send("data"); // 2. send/push data
  });

}

Simple, effective and easy to use. The callback will be executed when a new client is connected. Inside the callback you can send data, listen for connection close events, etc.

The factory method sse.event(Object) will let you set event attributes:

{
  sse("/path", sse -> {

    sse.event("data")
        .id("id")
        .name("myevent")
        .retry(5000L)
        .send();
  });

}

structured data

Other than raw/string data you can also send structured data, like json, xml, etc..

The next example will send two messages, one in json format and one in text/plain format:

{
  use(new MyJsonRenderer());

  sse("/path", sse -> {

    MyObject object = ...
    sse.send(object, "json");
    sse.send(object, "plain");
  });
}

Or if your need only one format, just:

{
  use(new MyJsonRenderer());

  sse("/path", sse -> {

    MyObject object = ...
    sse.send(object);
  }).produces("json"); // json by default
}

request parameters

Request access is provided by a two argument callback:

{
  sse("/events/:id", (req, sse) -> {

    String id = req.param("id").value();
    MyObject object = findObject(id);
    sse.send(object);
  });

}

connection lost

The sse.onClose(Runnable) callback allows you to clean and release resources on connection close. A connection is closed when you call the sse.close() method or when the remote client closes the connection.

{
  sse("/events/:id", sse -> {

    sse.onClose(() -> {
      // clean up resources
    });
  });

}

A connection close event is detected and fired when you attempt to send some data.

keep alive time

The keep alive time feature can be used to prevent connections from timing out:

{
  sse("/events/:id", sse -> {

    sse.keepAlive(15, TimeUnit.SECONDS);
  });

}

The previous example will send a ':' message (empty comment) every 15 seconds to keep the connection alive. If the client drops the connection, then the sse.onClose(Runnable) event will be fired.

This feature is useful when you want to detect close events without waiting for the next time you send an event. If on the other hand your application already generates events every 15 seconds, the use of keep alive is unnecessary.

require

The sse.require(Type) methods gives you access to application services:

{
  sse("/events/:id", sse -> {

    MyService service = sse.require(MyService.class);
  });

}

example

The next example will generate a new event every 60 seconds. It recovers from a server shutdown by using the sse.lastEventId() and cleans resources on connection close.

{
  // creates an executor service
  ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

  sse("/events", sse -> {

    // if we go down, recover from last event ID we sent. Otherwise, start from zero.
    int lastId = sse.lastEventId(Integer.class).orElse(0);
    AtomicInteger next = new AtomicInteger(lastId);

    // send events every 60s
    ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
      Integer id = next.incrementAndGet();
      Object data = findDataById(id);
      // send data and id
      sse.event(data).id(id).send();
    }, 0, 60, TimeUnit.SECONDS);

    // on connection lost, cancel 60s task
    sse.onClose(() -> {
      future.cancel(true);
    });
  });

}

misc

cors

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources (e.g. fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated.

usage

{
  use("*", new CorsHandler());
}

Previous example, will handle CORS request (simple or preflight).

Default options are defined via .conf file:

cors {
  # Configures the Access-Control-Allow-Origin CORS header. Possibly values: *, domain, regex or a list of previous values. 
  # Example: 
  # "*" 
  # ["http://foo.com"] 
  # ["http://*.com"] 
  # ["http://foo.com", "http://bar.com"] 
  origin: "*"

  # If true, set the Access-Control-Allow-Credentials header 
  credentials: true

  # Allowed methods: Set the Access-Control-Allow-Methods header 
  allowedMethods: [GET, POST]

  # Allowed headers: set the Access-Control-Allow-Headers header. Possibly values: *, header name or a list of previous values. 
  # Examples 
  # "*" 
  # Custom-Header 
  # [Header-1, Header-2] 
  allowedHeaders: [X-Requested-With, Content-Type, Accept, Origin]

  # Preflight max age: number of seconds that preflight requests can be cached by the client 
  maxAge: 30m

  # Set the Access-Control-Expose-Headers header 
  # exposedHeaders: [] 
}

CORS options are represented at runtime by the Cors handler.

xss

Cross-site scripting (XSS) is a type of security vulnerability typically found in web applications. XSS enables attackers to inject client-side scripts into web pages viewed by other users.

Jooby provides a few XSS escapers and a simple and flexible way to provide custom and/or more featured XSS escapers.

Default XSS escapers are urlFragment, formParam, pathSegment and html, all provided by Guava.

More advanced and feature rich escapers like js, css, sql are provided via modules.

usage

There are a couple of ways to use XSS escape functions:

Applying an XSS escaper to param or header:
{
  post("/", req -> {
    String safeParam = req.param("input", "html").value();

    String safeHeader = req.header("input", "html").value();
  });
}

Here input is the param/header that you want to escape with the html escaper.

Applying multiple XSS escapers:
{
  post("/", req -> {
    String safeInput = req.param("input", "urlFragment", "html");
  });
}
Applying an XSS escaper to form/bean:
{
  post("/", req -> {
    MyForm form = req.params("input", "html");
  });
}
Applying an XSS escaper from template engines

Template engines usually provide built in methods to escape HTML. However, Jooby will also integrate its XSS escapers with the template engine of your choice:

handlebars:

{{xss input "js" "html"}} 

pebble:

{{xss (input, "js", "html")}} 

freemarker:

${xss (input, "js", "html")}

jade:

p= xss.apply(input, "js", "html")

modules

jooby.conf

################################################################################################### 
#! application 
################################################################################################### 
application {

  # environment default is: dev 
  env = dev

  # contains the simple name of package of your application bootstrap class. 
  # For example: com.foo.App -> foo 
  # name = App.class.getPackage().getName().lastSegment() 
  # application namespace, default to app package. set it at runtime 
  # ns = App.class.getPackage().getName() 
  # class = App.class.getName() 
  # tmpdir 
  tmpdir = ${java.io.tmpdir}/${application.name}

  # path (a.k.a. as contextPath) 
  path = /

  # localhost 
  host = localhost

  # HTTP ports 
  port = 8080

  # uncomment to enabled HTTPS 
  # securePort = 8443 
  # we do UTF-8 
  charset = UTF-8

  # date format 
  dateFormat = dd-MMM-yyyy

  # number format, system default. set it at runtime 
  # numberFormat = DecimalFormat.getInstance(${application.lang})).toPattern() 
  # comma separated list of locale using the language tag format. Default to: Locale.getDefault() 
  # lang = Locale.getDefault() 
  # timezone, system default. set it at runtime 
  # tz = ZoneId.systemDefault().getId() 
  # redirect to/force https 
  # example: https://my.domain.com/{0} 
  redirect_https = ""

}

ssl {

  # An X.509 certificate chain file in PEM format, provided certificate should NOT be used in prod. 
  keystore.cert = org/jooby/unsecure.crt 

  # A PKCS#8 private key file in PEM format, provided key should NOT be used in prod. 
  keystore.key = org/jooby/unsecure.key

  # password of the keystore.key (if any) 
  # keystore.password = 
  # Trusted certificates for verifying the remote endpoint's certificate. The file should 
  # contain an X.509 certificate chain in PEM format. Default uses the system default. 
  # trust.cert = 
  # Set the size of the cache used for storing SSL session objects. 0 to use the 
  # default value. 
  session.cacheSize = 0

  # Timeout for the cached SSL session objects, in seconds. 0 to use the default value. 
  session.timeout = 0

}

################################################################################################### 
#! session defaults 
################################################################################################### 
session {

  # we suggest a timeout, but usage and an implementation is specific to a Session.Store implementation 
  timeout = 30m

  # save interval, how frequently we must save a none-dirty session (in millis). 
  saveInterval = 60s

  cookie {

    # name of the cookie 
    name = jooby.sid

    # cookie path 
    path = /

    # expires when the user closes the web browser 
    maxAge = -1

    httpOnly = true

    secure = false

  }

}

################################################################################################### 
#! flash scope defaults 
################################################################################################### 
flash {

  cookie {

    name = jooby.flash

    path = ${application.path}

    httpOnly = true

    secure = false

  }

}

################################################################################################### 
#! server defaults 
################################################################################################### 
server {

  http {

    HeaderSize = 8k

    # Max response buffer size 
    ResponseBufferSize = 16k

    # Max request body size to keep in memory 
    RequestBufferSize = 1m

    # Max request size total (body + header) 
    MaxRequestSize = 200k

    IdleTimeout = 0

    Method = ""

  }

  threads {

    Min = ${runtime.processors}

    Max = ${runtime.processors-x8}

    IdleTimeout = 60s

  }

  routes {

    # Guava Cache Spec 
    Cache = "concurrencyLevel="${runtime.concurrencyLevel}",maximumSize="${server.threads.Max}

  }

  ws {

    # The maximum size of a text message. 
    MaxTextMessageSize = 16k

    # The maximum size of a binary message. 
    MaxBinaryMessageSize = 16k

    # The time in ms (milliseconds) that a websocket may be idle before closing. 
    IdleTimeout = 5minutes

  }

  http2 {

    cleartext = true

    enabled = false

  }

}

################################################################################################### 
#! assets 
################################################################################################### 
assets {

  #! If asset CDN is present, the asset router will do a redirect to CDN and wont serve the file locally 
  #! /assets/js/index.js -> redirectTo(cdn + assets/js/index.js) 
  cdn =  ""

  etag = true

  lastModified = true

  env = ${application.env}

  charset = ${application.charset}

  # -1 to disable or HOCON duration value 
  cache.maxAge = -1

}

################################################################################################### 
#! Cross origin resource sharing 
################################################################################################### 
cors {

  # Configures the Access-Control-Allow-Origin CORS header. Possibly values: *, domain, regex or a list of previous values. 
  # Example: 
  # "*" 
  # ["http://foo.com"] 
  # ["http://*.com"] 
  # ["http://foo.com", "http://bar.com"] 
  origin: "*"

  # If true, set the Access-Control-Allow-Credentials header 
  credentials: true

  # Allowed methods: Set the Access-Control-Allow-Methods header 
  allowedMethods: [GET, POST]

  # Allowed headers: set the Access-Control-Allow-Headers header. Possibly values: *, header name or a list of previous values. 
  # Examples 
  # "*" 
  # Custom-Header 
  # [Header-1, Header-2] 
  allowedHeaders: [X-Requested-With, Content-Type, Accept, Origin]

  # Preflight max age: number of seconds that preflight requests can be cached by the client 
  maxAge: 30m

  # Set the Access-Control-Expose-Headers header 
  # exposedHeaders: [] 
}

################################################################################################### 
#! runtime 
################################################################################################### 
#! number of available processors, set it at runtime 
#! runtime.processors = Runtime.getRuntime().availableProcessors() 
#! runtime.processors-plus1 = ${runtime.processors} + 1 
#! runtime.processors-plus2 = ${runtime.processors} + 2 
#! runtime.processors-x2 = ${runtime.processors} * 2 
################################################################################################### 
#! status codes 
################################################################################################### 
err.java.lang.IllegalArgumentException = 400

err.java.util.NoSuchElementException = 400

err.java.io.FileNotFoundException = 404

################################################################################################### 
#! alias 
################################################################################################### 
contextPath = ${application.path}

mime.properties

mime.ai=application/postscript

mime.aif=audio/x-aiff

mime.aifc=audio/x-aiff

mime.aiff=audio/x-aiff

mime.apk=application/vnd.android.package-archive

mime.asc=text/plain

mime.asf=video/x.ms.asf

mime.asx=video/x.ms.asx

mime.au=audio/basic

mime.avi=video/x-msvideo

mime.bcpio=application/x-bcpio

mime.bin=application/octet-stream

mime.bmp=image/bmp

mime.cab=application/x-cabinet

mime.cdf=application/x-netcdf

mime.class=application/java-vm

mime.cpio=application/x-cpio

mime.cpt=application/mac-compactpro

mime.crt=application/x-x509-ca-cert

mime.csh=application/x-csh

mime.css=text/css

mime.scss=text/css

mime.less=text/css

mime.csv=text/comma-separated-values

mime.dcr=application/x-director

mime.dir=application/x-director

mime.dll=application/x-msdownload

mime.dms=application/octet-stream

mime.doc=application/msword

mime.dtd=application/xml-dtd

mime.dvi=application/x-dvi

mime.dxr=application/x-director

mime.eps=application/postscript

mime.etx=text/x-setext

mime.exe=application/octet-stream

mime.ez=application/andrew-inset

mime.gif=image/gif

mime.gtar=application/x-gtar

mime.gz=application/gzip

mime.gzip=application/gzip

mime.hdf=application/x-hdf

mime.hqx=application/mac-binhex40

mime.htc=text/x-component

mime.htm=text/html

mime.html=text/html

mime.ice=x-conference/x-cooltalk

mime.ico=image/x-icon

mime.ief=image/ief

mime.iges=model/iges

mime.igs=model/iges

mime.jad=text/vnd.sun.j2me.app-descriptor

mime.jar=application/java-archive

mime.java=text/plain

mime.jnlp=application/x-java-jnlp-file

mime.jpe=image/jpeg

mime.jpeg=image/jpeg

mime.jpg=image/jpeg

mime.js=application/javascript

mime.ts=application/javascript

mime.coffee=application/javascript

mime.json=application/json

mime.yaml=application/yaml

mime.jsp=text/html

mime.kar=audio/midi

mime.latex=application/x-latex

mime.lha=application/octet-stream

mime.lzh=application/octet-stream

mime.man=application/x-troff-man

mime.mathml=application/mathml+xml

mime.me=application/x-troff-me

mime.mesh=model/mesh

mime.mid=audio/midi

mime.midi=audio/midi

mime.mif=application/vnd.mif

mime.mol=chemical/x-mdl-molfile

mime.mov=video/quicktime

mime.movie=video/x-sgi-movie

mime.mp2=audio/mpeg

mime.mp3=audio/mpeg

mime.mpe=video/mpeg

mime.mpeg=video/mpeg

mime.mpg=video/mpeg

mime.mpga=audio/mpeg

mime.ms=application/x-troff-ms

mime.msh=model/mesh

mime.msi=application/octet-stream

mime.nc=application/x-netcdf

mime.oda=application/oda

mime.odb=application/vnd.oasis.opendocument.database

mime.odc=application/vnd.oasis.opendocument.chart

mime.odf=application/vnd.oasis.opendocument.formula

mime.odg=application/vnd.oasis.opendocument.graphics

mime.odi=application/vnd.oasis.opendocument.image

mime.odm=application/vnd.oasis.opendocument.text-master

mime.odp=application/vnd.oasis.opendocument.presentation

mime.ods=application/vnd.oasis.opendocument.spreadsheet

mime.odt=application/vnd.oasis.opendocument.text

mime.ogg=application/ogg

mime.otc=application/vnd.oasis.opendocument.chart-template

mime.otf=application/vnd.oasis.opendocument.formula-template

mime.otg=application/vnd.oasis.opendocument.graphics-template

mime.oth=application/vnd.oasis.opendocument.text-web

mime.oti=application/vnd.oasis.opendocument.image-template

mime.otp=application/vnd.oasis.opendocument.presentation-template

mime.ots=application/vnd.oasis.opendocument.spreadsheet-template

mime.ott=application/vnd.oasis.opendocument.text-template

mime.pbm=image/x-portable-bitmap

mime.pdb=chemical/x-pdb

mime.pdf=application/pdf

mime.pgm=image/x-portable-graymap

mime.pgn=application/x-chess-pgn

mime.png=image/png

mime.pnm=image/x-portable-anymap

mime.ppm=image/x-portable-pixmap

mime.pps=application/vnd.ms-powerpoint

mime.ppt=application/vnd.ms-powerpoint

mime.ps=application/postscript

mime.qml=text/x-qml

mime.qt=video/quicktime

mime.ra=audio/x-pn-realaudio

mime.ram=audio/x-pn-realaudio

mime.ras=image/x-cmu-raster

mime.rdf=application/rdf+xml

mime.rgb=image/x-rgb

mime.rm=audio/x-pn-realaudio

mime.roff=application/x-troff

mime.rpm=application/x-rpm

mime.rtf=application/rtf

mime.rtx=text/richtext

mime.rv=video/vnd.rn-realvideo

mime.ser=application/java-serialized-object

mime.sgm=text/sgml

mime.sgml=text/sgml

mime.sh=application/x-sh

mime.shar=application/x-shar

mime.silo=model/mesh

mime.sit=application/x-stuffit

mime.skd=application/x-koan

mime.skm=application/x-koan

mime.skp=application/x-koan

mime.skt=application/x-koan

mime.smi=application/smil

mime.smil=application/smil

mime.snd=audio/basic

mime.spl=application/x-futuresplash

mime.src=application/x-wais-source

mime.sv4cpio=application/x-sv4cpio

mime.sv4crc=application/x-sv4crc

mime.svg=image/svg+xml

mime.swf=application/x-shockwave-flash

mime.t=application/x-troff

mime.tar=application/x-tar

mime.tar.gz=application/x-gtar

mime.tcl=application/x-tcl

mime.tex=application/x-tex

mime.texi=application/x-texinfo

mime.texinfo=application/x-texinfo

mime.tgz=application/x-gtar

mime.tif=image/tiff

mime.tiff=image/tiff

mime.tr=application/x-troff

mime.tsv=text/tab-separated-values

mime.txt=text/plain

mime.ustar=application/x-ustar

mime.vcd=application/x-cdlink

mime.vrml=model/vrml

mime.vxml=application/voicexml+xml

mime.wav=audio/x-wav

mime.wbmp=image/vnd.wap.wbmp

mime.wml=text/vnd.wap.wml

mime.wmlc=application/vnd.wap.wmlc

mime.wmls=text/vnd.wap.wmlscript

mime.wmlsc=application/vnd.wap.wmlscriptc

mime.wrl=model/vrml

mime.wtls-ca-certificate=application/vnd.wap.wtls-ca-certificate

mime.xbm=image/x-xbitmap

mime.xht=application/xhtml+xml

mime.xhtml=application/xhtml+xml

mime.xls=application/vnd.ms-excel

mime.xml=application/xml

mime.xpm=image/x-xpixmap

mime.xsd=application/xml

mime.xsl=application/xml

mime.xslt=application/xslt+xml

mime.xul=application/vnd.mozilla.xul+xml

mime.xwd=image/x-xwindowdump

mime.xyz=chemical/x-xyz

mime.z=application/compress

mime.zip=application/zip

mime.conf=application/hocon

#! fonts 
mime.ttf=font/truetype

mime.otf=font/opentype

mime.eot=application/vnd.ms-fontobject

mime.woff=application/x-font-woff

mime.woff2=application/font-woff2

#! source map 
mime.map=text/plain

mime.mp4=video/mp4