Hosted by Three Crickets

Prudence
Scalable REST Platform
For the JVM

Prudence logo: bullfinch in flight

Configuration

Your Prudence container's "/component/" directory has various subdirectories in which you can configure it.
Prudence uses "configuration-by-script" almost everywhere: configuration files are true JavaScript source code, meaning that you can do pretty much anything you need during the bootstrap process, allowing for dynamic configurations that adjust to their deployed environments.
Prudence, as of version 2.0, does not support live re-configuration. You must restart Prudence in order for changed settings to take hold. The one exception is the system crontab: changes there are picked up on-the-fly once per minute.

/configuration/logging/

Used by the Sincerity logging plugin. Configure system-wide logging here, using Apache log4j.

/configuration/sincerity/

Used by Sincerity to manage installation of packages in your Prudence container. You usually won't be editing this files directly, instead using "sincerity" commands to manipulate it. However, take special note of artifacts.conf if you are committing your container to a VCS.

/configuration/hazelcast/

Configure Hazelcast here, if you are running in a cluster. Note that the configuration is actually loaded by the distributed service.
You have two options for configuration:
For your convenience, the entire com.hazelcast.config package is already imported for configuration-by-script. See the Hazelcast configuration guide for a general overview.
The default configuration creates a single Hazelcast instance belonging to an "application" cluster, and the default configuration of applications points them to use this instance, though each application gets its own Hazelcast map to handles it application.distributedGlobals API. However, you can configure applications to use any Hazelcast instance, as well as change which shared objects to use, in their settings.js. This allows each application to belong to a different Hazelcast cluster.
Likewise, if you are using Hazelcast as a cache backend, it will default to use the Hazelcast "application" instance. However, you can set the cache backend to use its own Hazelcast instance in its constructor.
There are good reasons why you might want a more complex configuration: for example, if you're doing task farming, you might want the "task" nodes to be separate from the "application" nodes. A commented-out suggested configuration for this is included—see the clusters chapter for a detailed explanation.

/component/

The Prudence component is bootstrapped here. If you wish to understand exactly how it works, take a look at "/component/default.js".

sharedGlobals

In all the "/component/" configuration files, you have access to a global "sharedGlobals" JavaScript dict. Values you set here will become application.sharedGlobals once the component is started. This dict works similarly to app.globals in settings.js; it is likewise "flattened."
For an example, here we set a database connection pool as a shared global, using a custom service defined in "/component/services/database/default.js":
sharedGlobals.database = sharedGlobals.database || {}
sharedGlobals.database.pool = createPool()

Initializers

In all the "/component/" configuration files, you have access to a global "initializers" JavaScript array. Any function you add to this array will be executed after the component is started, in the order in which they were added. Here's a trivial usage example:
initializers.push(function() {
	println('This is my trivial initializer!')
})

/component/hosts/

Restlet has excellent virtual host support. There is a many-to-many relationship routing between servers, hosts and applications, allowing you considerable flexibility in binding your URI-spaces. For example, you can easily have a single Prudence container (running in a single JVM instance) managing several sites at once, with several applications, on several domains, on several servers.
Define your virtual hosts as ".js" files under "/component/hosts/". A minimal host definition would like this:
var host = new org.restlet.routing.VirtualHost(component.context)
host.name = 'privatehost'
component.hosts.add(host)
The "host.name" param exactly matches the string used in app.hosts per each application.
A virtual host can route according to domain name, and incoming server IP address and port assignment:
host.resourceScheme = [string]
host.resourceDomain = [string]
host.resourcePort = [string]
host.serverAddress = [string]
host.serverPort = [string]
host.hostScheme = [string]
host.hostDomain = [string]
host.hostPort = [string]
An example of a virtual host for a specific domain name:
var host = new org.restlet.routing.VirtualHost(component.context)
host.name = 'other'
host.resourceDomain = 'otherdomain.org'
component.hosts.add(host)
If you do this, you will likely want to set "resourceDomain" for the default host to a specific domain, too.
Some notes:

The Hungry Host

The "default" host that comes with the Prudence skeleton doesn't configure any routing limitations, meaning that all incoming requests are routed (equivalent to setting all properties at "*"). We call such a host "hungry," because it will "eat" any request coming its way, denying any other virtual hosts the opportunity to route them. Thus, if you want to use more than one host, you have two options:
  1. Don't use a hungry host: delete the "default.js" file or comment out the "component.hosts.add" call in it.
  2. Make sure that the hungry host is the last one, so that it acts as a fallback when other hosts don't match incoming requests. Because host files are executed in alphabetical order, a good way to ensure that the hungry host is last is to number your host filenames. For example:

Deploying Multiple Sites

Using the virtual hosts and application model, Prudence can let you manage several sites using a single Prudence installation (a "container"). But is this always a good idea?
Advantages of Using a Single Container
  1. Possibly simpler deployment: you are using a single base directory for the entire project, which might be easier for you. Because all configuration is done by JavaScript inside the container, it is very flexible.
  2. Less memory use than running multiple JVMs.
  3. Shared memory: you can use application.sharedGlobals to share state between applications.
Advantages of Using Multiple Containers
  1. Possibly simpler deployment: several base directories can mean separate code/distribution repositories, which might be easier for you. You'll configure routing between them at your load balancer.
  2. Robustness: crashes/deadlocks/memory leaks in one VM won't affect others. With this in mind, it may even be worth having each single application running in its own JVM/container.
  3. Run-time flexibility: you can restart the JVM for one container without affecting others that are running.
There is no performance advantage in either scenario. Everything in Prudence is designed around high-concurrency and threading, and generally threads are managed by the OS globally.
Well, there are caveats to that statement: Linux can group threads per running process for purposes of prioritization, but this is mostly used for desktop applications. The feature could possibly be useful when running several Prudence containers, if you want to guarantee high thread priority to one of the containers over the others. This kind of tweaking would only effect very high concurrency and highly CPU-bound deployments.

/component/servers/

Define your servers as ".js" files under "/component/servers/". At the minimum, you must specify a protocol and a port. Here's an example definition for an HTTP server, "http.js":
var server = new Server(Protocol.HTTP, 8080)
server.name = 'myserver'
component.servers.add(server)
The server name is optional, and used for debugging.
An important configuration is to bind a server to a specific IP address, in case your machine has more than one IP address:
server.address = [string]
There are many configuration parameters for Jetty, the HTTP engine, which you can set as parameters in the server's context. Here we'll increase the size of the thread pool and lower the idle timeout:
server.context.parameters.set('threadPool.minThreads', '50')
server.context.parameters.set('threadPool.maxThreads', '300')
server.context.parameters.set('connector.idleTimeout', '10000')
For a complete list of available configuration parameters, see JettyHttpServerHelper. Make sure to check the documentation for all the parent classes, because they are inherited. Note that parameters are always set as strings, even if they are interpreted as other types.
Load Balancing
Are your servers running behind a load balancer? See the explanation here as to why you would want to add this:
server.context.parameters.set('useForwardedForHeader', 'true')

Secure Servers (HTTPS)

If you are using a load balancer, it may make sense to handle secure connections there. But Prudence can also handle secure (HTTPS) connections itself. Here's an example configuration for "/component/servers/https.js":
var server = new Server(Protocol.HTTPS, 8082)
server.name = 'secure'
component.servers.add(server)
‚Äč
// Configure it to use our security keys
server.context.parameters.set('keystorePath', '/path/prudence.jks')
server.context.parameters.set('keystorePassword', 'mykeystorepassword')
//server.context.parameters.set('keyPassword', 'mykeypassword')
See DefaultSslContextFactory for all security configuration parameters.
Security Keys
The above configuration assumes the you have a Java KeyStore (JKS) file at "/path/prudence.jks" containing your security key. You can create a key using the "keytool" utility that is bundled with most JDKs. For example:
keytool -keystore /path/prudence.jks -alias mykey -genkey -keyalg RSA
When creating the keystore, you will be asked provide a password for it, and you may optionally provide a password for your key, too, in which case you need to comment out the relevant line in the example above. (The key alias and key password would be transferred together with the key if you move it to a different keystore.)
Note that if you want to make client requests to a server that uses such a self-created key, you will need your client to recognize that key. If this is done from the JVM, this means setting the "javax.net.ssl.trustStore" JVM property. For example, if you're using Prudence's request API, you will need to start Prudence like so:
JVM_SWITCHES=-Djavax.net.ssl.trustStore=/path/prudence.jks sincerity start prudence
Such self-created keys are useful for controlled intranet environments, in which you can provide clients with the public key, but for Internet applications you will likely want a key created by one of the "certificate authorities" trusted by most web browsers. Some of these certificate authorities may conveniently let you download a key in JKS format. Otherwise, if they support PKCS12 format, you can use keytool (only JVM version 6 and later) to convert PKCS12 to JKS. For example:
keytool -importkeystore -srcstoretype PKCS12 -srckeystore /path/prudence.pkcs12 -destkeystore /path/prudence.jks
If your certificate authority won't even let you download PKCS12 file, you can create one from your ".key" and ".crt" (or ".pem") files using OpenSSL:
openssl pkcs12 -inkey /path/mykey.key -in /path/mykey.crt -export -out /path/prudence.pkcs12 
(Note that in this case you must give your new PKCS12 a non-empty password, or else keytool will fail with an unhelpful error message.)
HTTP/2
Jetty adds full HTTP/2 support to your secure servers, bringing an improved user experience and a lighter load on the backend. See the Sincerity documentation for details on how to enable it.
API
It's sometimes necessary to support HTTPS specially in your implementation. One useful strategy is to create separate applications for HTTP and HTTPS, and then attach them to different virtual hosts, one for each protocol (the "resourceScheme" parameter). However, if the application behaves mostly the same for HTTP and HTTPS, but differs only in a few specific resources, it may be useful to check for HTTPS programmatically, via the conversation.reference.schemeProtocol API. For example:
if (conversation.reference.scheme == 'https') {
	...
}

Other Server Engines

Prudence, by default, uses Jetty 9.3 as its server engine. Jetty is mature, performant and eminently scalable, and we highly recommend it for production environments.
However, it's possible to replace Jetty with a different engine should you require. To do this, you must remove the Jetty Restlet 9 connector from your container, and install a different connector instead, as well as its dependencies.
To make sure Jetty 9 is excluded from the next installation, you can use the following Sincerity command:
sincerity exclude org.restlet.jse org.restlet.ext.restlet.jetty9 :
	exclude org.eclipse.jetty jetty-security :
	exclude org.eclipse.jetty jetty-client
As of Restlet 2.3, three alternatives are available:
Jetty 9.2
This is the recommended alternative to Jetty 9.3 if you cannot use JVM 8 or above, and are limited to JVM 7. To install it:
sincerity add org.restlet.jse org.restlet.ext.jetty : install
The configuration parameters are documented here.
Simple
Simple Framework, which also works on JVM 6, is a lighter alternative to Jetty. Its documentation makes some controversial claims about its improved scalability in comparison to Jetty, but we encourage you to verify them for yourself. To install it:
sincerity add org.restlet.jse org.restlet.ext.simple : install
The configuration parameters are documented here.
Restlet's Internal Server Engine
Will be used if no other connector is installed. While the internal engine may be adequate for testing, we found that it suffers from stability issues, and do not recommend it for production environments.

/component/clients/

Client connectors have two main use cases:
To add a client, add a ".js" file to "/component/clients/". For example, here's a minimal configuration for an HTTP client, "http.js":
importClass(org.restlet.data.Protocol)
var client = component.clients.add(Protocol.HTTP)
Clients are configured by setting parameters in their context:
client.context.parameters.set('socketTimeout', '10000')
For a complete list of available configuration parameters, see HttpClientHelper. Make sure to check the documentation for all the parent classes, because they are inherited. Note that parameters are always set as strings, even if they are interpreted as other types.
(That link was for Jetty 9; use this link if you are using Apache HttpClient on JVM 6.)

Other Client Engines

Prudence, by default, uses Jetty 9 as its client engine. Jetty is mature, performant and eminently scalable, and we highly recommend it for production environments.
However, it's possible to replace Jetty with a different engine should you require. To do this, you must remove the Jetty Restlet 9 connector from your container, and install a different connector instead, as well as its dependencies.
To make sure Jetty 9 is excluded from the next installation, you can use the following Sincerity command:
sincerity exclude org.restlet.jse restlet-jetty9 :
	exclude org.eclipse.jetty jetty-security :
	exclude org.eclipse.jetty jetty-client
As of Restlet 2.2, three alternatives are available:
Apache HttpClient
Apache HttpClient is the recommended alternative to Jetty 9 if you cannot use JVM 7 or above, and are limited to JVM 6. To install it:
sincerity add org.restlet.jse restlet-httpclient : install
The configuration parameters are documented here.
URLConnection
This engine uses the java.net.URLConnection class included in the JVM. It does not scale well, however it does the job, and even supports FTP connections. To install it:
sincerity add org.restlet.jse restlet-net : install
The configuration parameters are documented here for HTTP, and here for FTP.
Restlet's Internal Client Engine
Will be used if no other connector is installed. While the internal engine may be adequate for testing, we found that it suffers from stability issues, and do not recommend it for production environments.

/component/services/

Services are run after the component is configured but before it is started. Several services are required to support various Prudence features, but you may freely add your own subdirectories here, to support your own features and subsystems.
Remember that your custom services have access to "sharedGlobals" and "initializers".

/component/services/log

Prudence supports NCSA-style logging of all incoming client requests, to all servers. By default, we just configure the logger name here (the default is "web"):
component.logService.loggerName = 'web'
You may also further configure the log format using the string interpolation variables:
component.logService.responseLogFormat = '{d}\t{cia}\t{ri}\t{S}'
See the Restlet documentation for more information.
To learn how configure this logger and connect it to an appender, refer to the documentation for Sincerity's logging plugin. By default, Prudence will use a rolling file appender to "/logs/web.log".

/component/services/prudence/status

Here you may configure system-wide custom error pages. Though applications may define their own custom error pages using app.errors, you can set them up here, too. Note that app.errors takes precedence over definitions here: you may thus treat this feature as a fallback option for when applications do not handle errors themselves.
An example definition:
statusService.capture(404, 'myapp', '/404/', component.context)
The capture API uses an application's internal name, allowing you to implement the error page in any installed application. Note that this API does not let you capture-and-hide the target URI, e.g. "/404/!". If you wish to hide it, you must do so in the application's app.routes.

/component/services/prudence/caching

Configure the caching backends here.
In "/services/prudence/caching/default.js", a ChainCache instance is set up as the main cache implementation. This allows you to create a tiered cache. Each tier should be added in order: the first tier added to the chain is the first one from which Prudence will fetch, so it usually should be the fastest backend. When storing entries in the cache, all tiers will be invoked.
Configure your tiered backends under "/services/prudence/caching/backends/", making sure that their filenames are in alphabetical order. By default, Prudence calls these "backend.1.js", "backend.2.js", etc., though you may your use your own alphabetic scheme.
In-Process Memory Cache
By default, Prudence sets up an InProcessMemoryCache as the first tier. Its default max size is 1MB, however it's recommended to increase this size according to your machine's available RAM. Note that if you're limiting the JVM's RAM usage in some way (for example, if you're using the Sincerity service plugin), then you want to make sure that the JVM has enough room for your cache as well as its normal operational requirements.
Though the in-process memory cache offers the best possible performance, it's of course limited in size. For very large web sites, it might be too small to be effective: if cache entries keep being discarded to make room for others, it will not be helping you much as a 1st tier backend. Make sure to monitor your usage carefully to see how often cache entries are discarded. Otherwise, good alternatives for the 1st tier would be Hazelcast or memcached.
Other Cache Backends
The following cache backends are all supported in Prudence:
Easy Installation
We've made it easy to install the dependencies for all the supported cache backends via Sincerity packages and shortcuts. The packages will also install an example configuration, as a 2nd-level tier (after the default in-process memory cache), in the file "/services/prudence/caching/backends/backend.2.js". Here's a table of the shortcuts, as well as the full package identifiers:
Backend Shortcut Identifier
Hazelcast prudence.cache.hazelcast com.threecrickets.prudence prudence-cache-hazelcast
memached prudence.cache.memached com.threecrickets.prudence prudence-cache-memcached
MongoDB prudence.cache.mongodb com.threecrickets.prudence prudence-cache-mongodb
H2 prudence.cache.h2 com.threecrickets.prudence prudence-cache-h2
For example, to install the H2 cache backend in the second tier:
sincerity add prudence.cache.h2 : install
The default configuration will put the H2 files under "/cache/prudence/cache/".

/component/services/prudence/startup

This service provides you with access to a global "startupTasks" JavaScript array. Any object implementing Callable or Runnable that you add to this array will be executed after the component configured but before is started in a multi-threaded task pool. You can configure the thread pool here.
This feature is used by the defrost/preheat feature.

/component/services/prudence/executor

This service configures the thread pool used for background task execution. You may change the size of the thread pool here, or otherwise install specialized implementations. By default Prudence uses (number of CPU cores * 2 + 1) for the pool size, a formula which offers good performance under high loads for common network-bound scenarios, though you may want to decrease this size for heavy CPU-bound workloads.
See the Executors API for a few other built-in options. Note, however, that your implementation must support the ScheduledExecutorService interface if you wish to support task scheduling.
The executor can be accessed via the application.executor API.

/component/services/prudence/scheduler

The scheduler is used to handle the crontab feature. By default, Prudence here sets up a scheduler for the component, accessible via the application.scheduler API, and also installs the system-wide crontab. You may edit this file to add other specialized crontabs, or otherwise set up scheduled tasks.
See the cron4j Scheduler documentation for more options.

/component/services/prudence/distributed

This service's job is to load the Hazelcast configuration.

/component/services/prudence/singleton

Prudence assumes a single Restlet Component instance. If for some reason you have a more complex setup, you can configure Prudence's initialization here.

/component/services/prudence/version

Provides access to Prudence, Restlet and Jetty versions, and prints out the welcoming message. There's not much to configure here, but feel free to examine the code!

/component/templates/

The "prudence create" Sincerity command copies and adapts the application template under "/component/templates/default/". You can create your own templates in that directory, and use their name as a second argument to the "prudence create" command, e.g.: "sincerity prudence create cms mytemplate". Likely, you'd want to copy the default template and modify it.
The mechanism interpolates the "${APPLICATION}" string in any of these files to be the application name argument you supplied as the first argument to "prudence create".

The Prudence Manual is provided for you under the terms of the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. The complete manual is available for download as a PDF.

Download manual as PDF Creative Commons License