Documentation

do more, more easily

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

Jooby keeps it simple but 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 server is ready. The onStarted callbacks are executed when 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, like 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 the annotation way then:

@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 delegates configuration management to Config library.

By defaults Jooby expects to find an application.conf file at the root of classpath. You can find the .conf file under 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 entirely 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 defaults, 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 too:

Using a fat jar:

java -jar myfat.jar prod

Using stork:

bin/myapp --start prod

turn on/off features

As described before the application.env property defines the environment where the application is being executed. It is possible to turn on/off specific features base 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 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 (first-listed are higher priority)

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

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 define his own set of (default) properties via Module.config method.

{
  use(new Jdbc());
}

custom .conf

As we said before, the default conf file is application.conf, but you can use any other name you want:

{
  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 conf tree similar to:
.
└── system properties
    ├── command line
    ├── application.conf
    ├── foo.conf
    ├── bar.conf
    ├── ...
    └── ...
  • Starting the application in prod produces 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

Of course if the logback[.env].xml isn’t, present we fallback to default one logback.xml.

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 need it).

do less and be flexible

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

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

  • Bootstrap X
  • Configure X
  • exports API of X

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

This principle, keep module usually small, maintainable and flexible.

creating modules

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

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 Config.parseResources(getClass(), "m1.properties");
  }
}

usage

A module must be imported/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/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 a 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 model for writing routes:

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

creating routes

A script route definition looks like:

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

while a MVC route definition looks like:


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 details. For simplicity and easy to understand all the example uses the script routes, but keep in mind you can do the same in MVC routes.

We created a route to handle a GET request at the root of our application. Any other HTTP method can be created in the same way.

If you need a POST:

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

or need to listen to any HTTP method:

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

It is possible to name a route explicitly:

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

Default route name is anonymous. Naming a route is useful for debugging purpose (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. Returned value will be send to the client.

functional handler: req

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

This handler depends on some external attribute which is available via Request object. Returned value will be send 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 via Request object and we explicitly send a response via 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, Response and Route.Chain objects.

From here you can end a response by calling response.send method, abort the request by throwing an Err or allow 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 path element with 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 are covered later, for now all you need to know is that you can access to a path parameter using the 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 is 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}");
}

and responds 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 is possible to specify an external file system location too:

{ 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 add 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 an awesome and powerful assets module. The assets is library to validate, concatenate, minify or compress JavaScript and CSS assets.

precedence and order

Routes are executed in the order they are defined. So the ordering of routes is crucial to the behavior of an application. Let’s review this fact via 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.

Now, why is it allowed to have two routes on the same path?

Because we want filters for routes.

A route handler accept 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. We will learn more about this behavior in the examples of the next section.

request handling

When a request is made to the server, which matches a route definition, the associated callback functions kick in to process the request and send back a response. We call this route pipe or stack.

Routes are like a plumbing pipe, requests start at the first route you define and work their way “down” the route stack processing for each path they match.

Each route handler has the capability to send a response or pass on the request 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 along with the req and the rsp objects to it:

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 it 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() terminates the flow of the request then and there; the request is not passed on to any other route handler.

So, how do we 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() because it terminates the request flow. 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");
});

Alternative, 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 3rd arg is required if you need to decide if the next route need to be executed or not. If you always call chain.next(req, rsp) the 3rd arg isn’t required and does exactly what the 2arg handler does: always call chain.next(req, rsp).

A good example for a filter is to handle for example 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 produces 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)
);

Performs content-negotiation on the Accept HTTP header of the request object. It select a handler for the request, based on the acceptable types ordered by their quality values. 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 a 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 response is complete and sendt

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 syntax 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 to be registered before (of course) 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 you don’t generate a response.

after

Allows for customize response before sending it. It will be invoked at the time a response need to be send.

{
  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 syntax 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);
      }
    });
  });

}

Due after is implemented by wrapping the response object. A after handler must to 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 then 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 log and cleanup a request. It will be invoked after we send a response.

{
  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. The goal of the complete handler is to probably cleanup request object and log responses. Please note that the complete handler is just syntax 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 then 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 like controllers in Spring and/or resources in Jersey with some minor enhancements and/or 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 his own version of the annotations.

A 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 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 a 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 has a huge importance 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 bootstrap time, declaring the route explicitly helps to reduce bootstrap time.

So, how do I register a 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 bind that name to a request parameter. If you want explicit mapping and/or the request parameter isn’t a valid Java identifier:

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

form submit

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

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

request body

Annotated the method parameter with @Body annotation:

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

request headers

Annotated the method parameter with @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 method returns 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 need/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 method 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 custom or completely different response.

For example, suppose you need to serve different content based on 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 annotated a route at application bootstrap time. It is 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 previous types.

Attributes can be accessed at runtime in a request/response cycle. For example, a security module might check for role attribute, a sitemap generator might check for 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 it as route attribute. Following these rules:

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

request attributes vs route attributes:

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

While, request attributes are created in a request/response cycle.

with operator

The with operator set 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 skip/ignore a route path match:

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

consumes

The consumes operator indicates the expected input the route can handle. It is used will accepting requests.

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

  }).consumes("json");
}

produces

The produces operator indicates the expected output the route can produces.

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

request

The request object contains methods for reading parameters, headers and body (among others). In the next section we will mention the most important method of a request object, if you need more information please refer to the javadoc.

parameters

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

The req.param(“name”) always returns a mutant instance. A mutant had severals 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 class must have a default constructor or a constructor annotated with javax.inject.Injext.

parameter type and precedence

A request parameter can be present at:

1) path level: /user/:id

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

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

(first listed are higher precedence)

Now, let’s suppose a very poor API where we have a route handler that accept an id parameter in the 3 location:

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));
});

It is clear that an API like this should be avoided, but is a good example of parameter precedence.

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
  • It is an Optional , List , Set or SortedSet where T satisfies one of 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”). All the explained before for request params apply for headers too.

body

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

A parser is responsible for parse or convert the HTTP request body to something else.

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

Parsers are explained later. For now, all you need to know is that they can read/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 POST encoded as application/x-www-form-urlencoded or multipart/form-data, see next section for such requests.

form submit

Form submit parsing is done via req.params(Class) or 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 too:

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 method or field.

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

  • 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: 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 comes in and destroyed at the end of the request.

{
  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 @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. The flash scope is similar to Session but lifecycle is shorter: data are kept for only one request.

The flash scope is implemented as client side cookie, so it helps to keep 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("/");
  });

}

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

@Path("/")
public class Controller {
  
  // Access to 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) {
    ... 
  }
} 

Worth to mention that flash attributes are accessible from template engine of your choice by prefixing flash attribute’s name with flash.. Here is 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 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 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 the matched incoming requested 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>

Due that authentication is provided via module or custom filter, there is no concept of logged/authenticated user. Still you can log the current user by setting an 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");
  }));

}

Here an application filter set an userId request attribute and then we provide that userId to {@link RequestLogger}.

custom log function

By default it uses the underlying logging system: logback. That’s why we previously show how to configure the org.jooby.RequestLogger in logback.xml.

If you want to log somewhere else and/or use a different technology then:

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

This is just an example but of course you can log the NCSA line to database, jms queue, etc…

latency

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

It add a new entry at the end of the NCSA output that represents 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 use the default server time zone, provided by ZoneId#systemDefault(). It’s possible to just override the time zone too:

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

response

The response object contains methods for reading and setting headers, status code and body (between others). In the next section we will mention the most important method of a 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 see 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 is not set is text/html.

The resulting Status Code when is 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”). The method always returns a Mutant and from there you can convert to any of the supported types.

Setting a header is pretty straightforward too:

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

send file

Send file API is available via rsp.download* methods:

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

The download method sets all these headers:

  • Content-Disposition

  • Content-Length

  • Content-Type

In the next example we explicitly set some of these headers:

{
  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 most commons are: auth, store information about current user, etc.

A session attribute must be String or a primitive. Session doesn’t allow to store arbitrary objects. It is 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();
  });
}

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. We sign the cookie with the application.secret property.

If memory or cookie store are not an option, then you can choose one of the high performance session store provided by Jooby. We provide session stores for redis, memcached, mongodb, cassandra, couchbase, hazelcast and 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 server perspective. By default, a session will expire when the user close the browser (a.k.a session cookie) or the cookie session has expired via maxAge attribute.

Session store implementation might or might not implemented 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 Web browser exits.

Default maxAge is: -1.

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

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

error handling

Error handler is represented by the Err.Handler class and allows you to log and/or render exceptions.

default err handler

The default error handler does content negotiation and optionally display friendly err pages using 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 simply public/err.html error page:

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

HTTP status code will be set too.

no html

If a request to / has an Accept: application/json header. Then, the default err handler will ask to 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.

HTTP status code will be set too.

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(...);
  });
}

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

catch 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

Default status code is 500, except for:

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

custom status code

Just throw an Err:

throw new Err(403);

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

err.com.security.Forbidden = 403

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

parser and renderer

parser

A Parser is responsible for parsing the HTTP params and/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
  • It is an Optional , List , Set or SortedSet where T satisfies one of previous rules

custom parser

Suppose we want to write a custom parser to convert a value into an integer. In practice we don’t need such parser bc it is provided, this is 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 is what we can parse to

2) We add a param callback

3) We can’t deal with current type, so we ask next parser to resolve it

Now, if we ask for HTTP body

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

Our custom parser won’t be able to parse the HTTP body, because it works on HTTP parameter. In order to extend our custom parser and use it for HTTP Body we must do:


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();
});

And now we can ask for a HTTP param and/or body.

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

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

Parser API is very powerful. It let you apply a parser to a HTTP param, set of param (like a form post), file uploads and/or body. But not just that, you are 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 param parser isn’t able to resolve a param a BAD REQUEST(400) error will be generated.

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

renderer

A Renderer converts a Java Object to a series of bytes or text and write them into 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 we want to apply a custom rendering for MyObject. 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 looks like:


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

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

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

view engine

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

A view carry the template name + model data:

{
  use(new MyTemplateEngine());

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

}

In order to support multiples view engine, a view engine is allowed to throw a java.io.FileNotFoundException when a template can’t be resolved it. This gives the chance to the next view resolver to load the template.

There is no much to say about views and template engines, any other detail or documentation should be 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. Being Netty the default web server.

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 the web server: Netty, Jetty and 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()) {
        ...
      } 
    }
  });
}

Here the web server can accept as many connection it can (as its on non blocking) while the worker thread might blocks.

Default worker thread pool is 20/100. The correct/right size depends on your business and work load your application is suppose to handle. We suggest you to start with default setup and see how it goes, later you can reduce or increase the thread pool.

In Jooby we favor simplicity over complexity that is why your code can block, still there are more advanced setup that 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 ...;
    });
  }

Previous examples are just syntax sugar for:

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

There is more syntax 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 convert java.util.concurrent.Callable and java.util.concurrent.CompletableFuture objects to deferred objects.

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

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

You might first see this as a bad thing, but is actually a good decision, because:

  • It is super easy to setup a default executor (we will see how soon)

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

executor

As we said before, the default executor run 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 result run in a ForkJoinPool. It 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 ...;
    });
  }

Worth mention 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 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 you realize your application need to:

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

These are the 3 points where your application is suppose 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 and don’t run blocking code on routes anymore. Otherwise performance will be affected.

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 that will grow without a limit but free and release thread that are idle after 60s.

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

For intensive computation, 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 is 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);
    }));
  });
}

Main difference are:

  • we keep the default executor: direct. So we don’t create a new thread and avoid context switching.
  • we use deferred object as promise and integrate with rx java.
  • different thread pool semantic is done via rx schedulers.

This is just one more example to demonstrate the value of the deferred object, because we provide an rxjava module which takes care of binding deferred object into Observables.

That’s all about deferred object, it allows you to build async and reactive applications and at the same time: keep it simple (Jooby design goal).

Also, we invite you to checkout the available async/reactive modules.

tests

In this section we are going to see how to run unit and integration tests in Jooby.

unit tests

We do offer two programming models:

  • script programming model; and
  • mvc programming model

You don’t need much for MVC routes, because a route is binded to a method of some class. So it is usually very easy and simple to mock and run unit tests against a MVC route.

We can’t say the same for script routes, because a route is represented by a lambda and there is no easy or simple way to get access to the lambda object.

We do provide a MockRouter which simplify unit tests for 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 in the same way:

{
  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? It works in a similar way:

{
  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) call push and register an external dependency (usually a mock). This make it possible to resolve services from require calls.

deferred

Mock of promises are possible too:

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

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

  assertEquals("OK", result);
}

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 next 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 start before the first test and stop after the last test. Application state is shared between tests.

While with Rule a new application is created per test. If you have N test, then the application will start/stop N times:


import org.jooby.test.JoobyRule;

public class MyIntegrationTest {

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

Again you are free to choice 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 enabled 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) so that’s why you need to configure HTTPS too.

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 (beside HTTPS). So it’s always a good practice to check the HTTP/2.0 section of the server implementation to learn more about current support, limitation 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 request the home page, we will send the /js/home.js resource too!!

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

web sockets

The use of web sockets is pretty easy too:

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

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

A web socket consist 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 here.

A handler is executed on new connections, from there we can listen for message, errors and/or send data to the client.

Keep in mind that web socket are not like routes. 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 web sockets under the same path.

require

You can ask Guice to wired an object from the ws.require(type)

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

consumes

Web socket can define a type to consume:

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

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

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

produces

Web socket can define a type to produce:

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

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

server-sent events

Server-Sent Events (SSE) is a mechanism that allows server to push the data from the server to the client once the client-server connection is established by the client. Once the connection is established by the client, it is the server who provides the data and decides to send it to the client whenever new chunk of data is available.

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 we can send data, listen for connection close events, etc.

There is a factory method sse.event(Object) that let you set event attributes:

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

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

}

structured data

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

The next example will send two message 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

We provide request access via two arguments 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 allow you to clean and release resources on connection close. A connection is closed when you call sse.close() method or the client/browser close 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 sent a ':' message (empty comment) every 15 seconds to keep the connection alive. If the client drop the connection, then the sse.onClose(Runnable) event will be fired it.

This feature is useful when you want to detect close events without waiting for the next time you send an event. But for example, if your application already generate events every 15s, then the use of keep alive is useless and you can avoid it.

require

The sse.require(Type) methods let 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 60s. It recovers from a server shutdown by using the sse.lastEventId() and clean 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 Cors.

xss

Cross-site scripting (XSS) is a type of computer 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 them from Guava.

More advanced/featured escapers like js, css, sql are provided via modules.

usage

There are a few way of using XSS escape functions:

Applying a 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 a XSS escaper to form/bean:
{
  post("/", req -> {
    MyForm form = req.params("input", "html");
  });
}
Applying a XSS escaper from template engines

Template engines usually provide a way to escape HTML (mainly) … still Jooby integrates 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 = 20

    Max = 100

    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