Skip navigation links

Package org.apache.river.phoenix

A configurable Java(TM) Remote Method Invocation (Java RMI) activation system daemon implementation.

See: Description

Package org.apache.river.phoenix Description

A configurable Java(TM) Remote Method Invocation (Java RMI) activation system daemon implementation. Phoenix is a configurable alternative to rmid; it implements and supports all of the same activation interfaces and semantics as rmid, but it also implements and supports network security. Like rmid, Phoenix also provides a simple command line interface for stopping an activation system daemon on the same host.

Starting Phoenix

Phoenix can be started by command line, or it can be launched by the ServiceStarter. In either case, its configuration options must be specified as described in Phoenix Configuration.
  1. Starting Phoenix by command line

    The standard command line for running Phoenix is:

    java [options] -jar install_dir/lib/phoenix.jar configOptions
    
    where options are standard options for the java command, install_dir is the directory where the River-Internet release is installed, and configOptions are the options (typically starting with a filename or URL) of a Phoenix configuration.

    Phoenix has code that must be downloadable to clients, so one of the options should set the java.rmi.server.codebase system property to a codebase path (space-separated list of URLS) that serves up install_dir/lib-dl/phoenix-dl.jar and install_dir/lib-dl/jsk-dl.jar (in that order). Phoenix also runs with a security manager, so one of the options should set the java.security.policy system property to a security policy file. The Phoenix configuration must supply at least a persistenceDirectory entry for the component named org.apache.river.phoenix.

  2. Starting Phoenix via Service Starter

    Phoenix supports being started via a NonActivatableServiceDescriptor. An example service descriptor for starting Phoenix is:

    new NonActivatableServiceDescriptor(
        codebase,
        policy,
        "install_dir/lib/phoenix.jar",    // classpath
        "org.apache.river.phoenix.PhoenixStarter",  // implClassName
        serverConfigArgs)
    
    where codebase is the codebase path, policy is the name of the security policy file, install_dir is the directory where the River-Internet release is installed, install_dir/lib/phoenix.jar is the class path for Phoenix, org.apache.river.phoenix.PhoenixStarter is the class name of the instance to construct to start Phoenix, and serverConfigArgs are the options (typically starting with a filename or URL) of a Phoenix configuration.

    Phoenix has code that must be downloadable to clients, so the codebase path specified by codebase should be two URLs that serve up install_dir/lib-dl/phoenix-dl.jar and install_dir/lib-dl/jsk-dl.jar (in that order). The Phoenix configuration must supply at least a persistenceDirectory entry for the component named org.apache.river.phoenix.

    The PhoenixStarter instance implements the ServiceProxyAccessor interface, and implements the getServiceProxy method to return Phoenix's ActivationSystem proxy. When Phoenix's ActivationSystem is shutdown, the activation system implementation will invoke the unregister method of the LifeCycle instance passed to the PhoenixStarter constructor.

    See the org.apache.river.start package documentation for details on configuring service descriptors.

Phoenix configuration

Phoenix obtains its configuration by calling ConfigurationProvider.getInstance with the specified configOptions.

The following entries are obtained from the configuration, all for the component named org.apache.river.phoenix:

loginContext
  Type: LoginContext
  Default: null
  Description: JAAS login context
persistenceDirectory
  Type: String
  Default: no default
  Description: log directory
activatorExporter
  Type: Exporter
  Default:
new BasicJeriExporter(
    new TcpServerEndpoint.getInstance(1198),
    new BasicILFactory(),
    false, true,
    PhoenixConstants.ACTIVATOR_UUID)
  Description: activator exporter
systemExporter
  Type: Exporter
  Default:
new BasicJeriExporter(
    new TcpServerEndpoint.getInstance(1198),
    new SystemAccessILFactory(),
    false, true,
    PhoenixConstants.ACTIVATION_SYSTEM_UUID)
  Description: ActivationSystem exporter
monitorExporter
  Type: Exporter
  Default:
new BasicJeriExporter(
    new TcpServerEndpoint.getInstance(1198),
    new AccessILFactory())
  Description: ActivationMonitor exporter
registryExporter
  Type: Exporter
  Default: new RegistrySunExporter()
  Description: Registry exporter
instantiatorPreparer
  Type: ProxyPreparer
  Default: new BasicProxyPreparer()
  Description: ActivationInstantiator proxy preparer
groupConfig
  Type: String[]
  Default: same configuration options as Phoenix
  Description: default activation group configuration options
groupLocation
  Type: String
  Default: obtain the location (a URL) from the code source of the protection domain of Phoenix's implementation class; if the URL ends in ".jar", then the default value for this entry is that URL except that ".jar" is replaced with "-group.jar", otherwise the default value is null
  Description: codebase for ActivationGroupImpl
groupOptions
  Type: String[]
  Default: new String[0]
  Description: extra options for all activation group commands
groupTimeout
  Type: int
  Default: 60000
  Description: maximum time in milliseconds to wait for group activation or termination
groupThrottle
  Type: int
  Default: 3
  Description: maximum concurrent activation group execs
groupOutputHandler
  Type: GroupOutputHandler
  Default: a handler that sends the group standard output to the standard output stream of Phoenix and sends the group error output to the error output stream of Phoenix
  Description: handler for the output of activation group processes
unexportTimeout
  Type: int
  Default: 60000
  Description: maximum time in milliseconds to wait for in-progress calls to finish during shutdown before forcibly unexporting remote objects
unexportWait
  Type: int
  Default: 10
  Description: milliseconds to wait between unexport attempts during shutdown
persistenceSnapshotThreshold
  Type: int
  Default: 200
  Description: number of log updates between snapshots

If no default is listed for an entry and the configuration cannot provide that entry, Phoenix will not start.

If a non-null loginContext entry is provided, a login is performed on that context, and Phoenix executes under the resulting Subject (via Subject.doAsPrivileged).

The persistenceDirectory entry specifies the name of the directory Phoenix uses to write its persistent database. If the named directory does not exist, Phoenix will create it. Only the directory itself will be created; the parent directory must already exist.

Phoenix exports four remote objects: activator, activation system, activation monitor, and registry. Each object is exported using an Exporter obtained from the configuration. Phoenix provides various classes to facilitate the configuration of these exporters, as described further below. None of the remote objects implement any access control directly; any desired access control must be implemented through the exporters. The exported registry is always read-only, with a single binding of the name java.rmi.activation.ActivationSystem to the activation system proxy; this proxy also implements ActivationAdmin. For compatibility with ActivationGroup.getSystem, the registry must be exported to the JRMP runtime, using the registry well-known object identifier. To support persistent references, the exporters for the activator and the activation system must produce proxies that are reusable across restarts of Phoenix. For JRMP and Jini extensible remote invocation (Jini ERI), this means using fixed object identifiers and fixed ports.

If the activator supports constraints (meaning the activator proxy implements RemoteMethodControl), then the activation identifiers handed out by Phoenix will also implement RemoteMethodControl, otherwise they will not. The activator implements ServerProxyTrust and its getProxyVerifier method returns a verifier for the activation identifiers used by Phoenix. The activation identifier class implements the getProxyTrustIterator method required by ProxyTrustVerifier, providing an iterator that produces the activator proxy. To support trust verification of activation identifiers, the activator proxy must implement TrustEquivalence and must either be a bootstrap proxy that implements ProxyTrust or be able to produce one through its own getProxyTrustIterator method.

Whenever an ActivationInstantiator proxy is received from an activation group, Phoenix passes it to the ProxyPreparer given by the instantiatorPreparer entry, if any. The resulting proxy is used whenever Phoenix makes a remote call to that activation group.

When Phoenix starts an activation group, it relies on phoenix-init.jar being in the class path of the group VM, therefore this JAR file must be included directly or indirectly in the class path specified in the CommandEnvironment of the ActivationGroupDesc. Phoenix starts an activation group as follows:

The class ActivationGroupImpl is used by Phoenix when an activation group descriptor does not specify a group class name. This group implementation is also configurable. The groupConfig entry specifies the configuration options that should be used when the group descriptor does not specify a group class and does not provide any explicit initialization data for the group. The configuration entries used by ActivationGroupImpl are described further below.

The groupLocation entry specifies the codebase, if any, that should be used to load the class ActivationGroupImpl. This codebase is used when an activation group descriptor does not specify a group class name and does not specify a group location.

The groupOptions entry specifies extra command line options to pass for every activation group process created by Phoenix. These options are always placed after any properties and command line options specified in the activation group descriptor, in the final command line used to create the process. The most common use of this feature is to temporarily set system properties for debugging.

The groupTimeout entry specifies the maximum time in milliseconds to wait after an activation group process is created before an activeGroup remote call must be received from that group. If the remote call is not received within that period, the attempt to activate an object within that group will terminate with an ActivationException. If an activation group process subsequently must be destroyed (so that a new process for the same group can be created), this entry also specifies the maximum time to wait for the process to actually terminate (after being destroyed) before giving up and proceeding to create a new process.

The groupThrottle entry specifies the maximum number of group processes Phoenix will create concurrently, to avoid system thrashing (particularly when Phoenix is restarting). If too many group processes need to be created at one time, creation of some will temporarily be deferred until existing creations complete.

The groupOutputHandler entry specifies the GroupOutputHandler for the output of activation group processes. The handler is called each time a group process is created.

The unexportTimeout entry specifies the maximum time in milliseconds to wait for in-progress inbound remote calls to finish during shutdown before forcibly unexporting all remote objects. The exportWait entry specifies the time in milliseconds to wait between unsuccessful export attempts.

Phoenix writes information about registered groups and objects to a persistent database, so that Phoenix can be restarted after a crash without losing state. As registrations are changed, incremental update records are written out, until a threshold is reached, and then a consolidated snapshot of the entire state is written out (to speed up recovery after a crash). The persistenceSnapshotThreshold entry specifies the the number of incremental updates to write between snapshots.

Phoenix uses the Logger named org.apache.river.phoenix to log information at the following levels:

Level Description
SEVERE persistent database snapshot failures
WARNING failure to restart an activatable object that has been registered to auto-restart
WARNING failure to recover from the persistent database
INFO startup of Phoenix
INFO shutdown of Phoenix
FINE process command line when an activation group is created

Exporter classes

The following class is useful for exporting the registry: A custom exporter class will be needed for Java 2 Standard Edition implementations from vendors other than Sun Microsystems(TM), Inc.

The following classes are useful in conjunction with Jini ERI:

The following classes are useful in conjunction with JRMP:

Activation group configuration

ActivationGroupImpl obtains the following entries from its configuration, all for the component named org.apache.river.phoenix:

loginContext
  Type: LoginContext
  Default: null
  Description: JAAS login context
inheritGroupSubject
  Type: boolean
  Default: false
  Description: if true, group subject is inherited when an activatable object is created
instantiatorExporter
  Type: Exporter
  Default: retains existing JRMP export of instantiator
  Description: ActivationInstantiator exporter
monitorPreparer
  Type: ProxyPreparer
  Default: new BasicProxyPreparer()
  Description: ActivationMonitor proxy preparer
systemPreparer
  Type: ProxyPreparer
  Default: new BasicProxyPreparer()
  Description: ActivationSystem proxy preparer
unexportTimeout
  Type: int
  Default: 60000
  Description: maximum time in milliseconds to wait for in-progress calls to finish before forcibly unexporting the group when going inactive
unexportWait
  Type: int
  Default: 10
  Description: milliseconds to wait between unexport attempts when going inactive

If a non-null loginContext entry is provided, a login is performed on that context, the group is created under the resulting Subject (set to be read-only), and all subsequent remote calls by the group to Phoenix are executed under that subject.

By default, the activation group automatically exports itself as a JRMP unicast remote object. (This is a limitation of the existing activation system design.) If an instantiatorExporter entry is provided, the group is unexported from the JRMP runtime and then re-exported with the provided exporter.

When the group is initially created, it passes the ActivationSystem proxy (obtained from the ActivationGroupID) to the ProxyPreparer given by the systemPreparer entry, if any. A new ActivationGroupID is constructed with the resulting proxy, and is used to create the group. A remote call through the prepared ActivationSystem proxy is made to report the group as active, and the ActivationMonitor proxy returned by that call is passed to the ProxyPreparer given by the monitorPreparer entry, if any. The prepared ActivationMonitor proxy will be used for all subsequent remote calls to Phoenix.

The unexportTimeout entry specifies the maximum time in milliseconds to wait for in-progress inbound remote calls to finish before forcibly unexporting the group when it goes inactive. The exportWait entry specifies the time in milliseconds to wait between unsuccessful export attempts.

The inheritGroupSubject entry if true indicates that the activation group's subject should be inherited by the access control context in which the group creates an activatable object. If the entry is false, then the group's subject is not inherited by that context. The default is false.

Access control

Access control mechanisms for incoming remote calls should be configured into the exporters used to export the various remote objects.

For JRMP and Jini ERI, a local-host-based access control policy (equivalent to that enforced by rmid) can be obtained by using the exporters provided in this package.

SystemPermission, ActivatorPermission, MonitorPermission, and InstantiatorPermission can be used to express access control policies for remote objects exported with BasicJeriExporter, by passing one of those classes to ProxyTrustILFactory or SystemAccessProxyTrustILFactory.

ExecPermission and ExecOptionPermission can be used to express the access control policy for what activation group descriptors can be registered. These permissions are checked by DefaultGroupPolicy, which is used by SystemAccessExporter, SystemAccessILFactory, and SystemAccessProxyTrustILFactory.

Compatibility with rmid

Activatable remote object implementations that work under rmid should work under Phoenix.

Activation group implementations that work under rmid should work under Phoenix, provided the Phoenix ActivationSystem is exported using a JRMP exporter. An unmodified JRMP-based group implementation will not work under Phoenix if the Phoenix ActivationSystem is exported using a Jini ERI exporter, because Jini ERI does not support stub replacement for JRMP implementation objects and the existing activation group design relies on such stub replacement. However, JRMP-based group implementations can be made compatible with activation systems based on Jini ERI, and still work under rmid, by adding a writeReplace method to the group implementation class that returns the stub.

Activation group implementations and activatable remote object implementations that depend on activation extensions supported by Phoenix will not work under rmid.

The persistent databases used by rmid and Phoenix are not compatible.

Stopping a daemon

A running activation system daemon (not necessarily an instance of Phoenix) can be shut down gracefully with the command:
java [options] -jar install_dir/lib/phoenix.jar -stop configOptions

In this case, the following entries are obtained from the configuration, all for the component named org.apache.river.phoenix:

loginContext
  Type: LoginContext
  Default: null
  Description: JAAS login context
systemPreparer
  Type: ProxyPreparer
  Default: new BasicProxyPreparer()
  Description: ActivationSystem proxy preparer
registryHost
  Type: String
  Default: null
  Description: registry host
registryPort
  Type: int
  Default: 1098
  Description: registry port

If a non-null loginContext entry is provided, a login is performed on that context, and the shutdown remote call is executed under the resulting Subject (via Subject.doAsPrivileged).

The activation system daemon to shut down is obtained by looking up the name java.rmi.activation.ActivationSystem in the registry at the host given by the registryHost configuration entry (defaulting to the local host) and the port given by the registryPort configuration entry (defaulting to port 1098).

The ActivationSystem proxy obtained from the registry is passed to the ProxyPreparer given by the systemPreparer entry, if any. The resulting proxy is used for the shutdown remote call.

Example configurations

The following are example Phoenix configurations, written using the syntax supported by ConfigurationFile. For simplicity, each configuration example combines both entries used by Phoenix and entries used by Phoenix's default group implementation. By default, the configuration options for Phoenix will be used as the configuration options for an activation group if either the ActivationGroupData in the group's ActivationGroupDesc does not specify configuration options or the Phoenix configuration file does not specify a groupConfig entry. If a configuration file is shared by both Phoenix and activation groups, some type-import-on-demand declarations (i.e., wildcard imports) may be required. If fully-qualified class names are imported, a ConfigurationException will be thrown if an import is encountered for a class that is not found. This may happen if a class is available to Phoenix, but is not available to the activation group, or vice versa.
// a Jini ERI configuration specifying constraints
import org.apache.river.config.KeyStores;
import org.apache.river.phoenix.*;
import net.jini.constraint.*;
import net.jini.core.constraint.*;
import net.jini.jeri.*;
import net.jini.jeri.ssl.SslServerEndpoint;
import net.jini.security.*;
import javax.security.auth.login.LoginContext;

org.apache.river.phoenix {
    registryExporter = new RegistrySunExporter();
    private sslPort = 2000; // pick one, cannot be 1098
    private daemonEndpoint = SslServerEndpoint.getInstance(sslPort);
    private integrity = new BasicMethodConstraints(
        new InvocationConstraints(Integrity.YES, null));
    systemExporter =
        new BasicJeriExporter(daemonEndpoint,
                              new SystemAccessProxyTrustILFactory(integrity),
                              false, true,
                              PhoenixConstants.ACTIVATION_SYSTEM_UUID);
    activatorExporter =
        new BasicJeriExporter(daemonEndpoint,
                              new ProxyTrustILFactory(integrity, null),
                              false, true,
                              PhoenixConstants.ACTIVATOR_UUID);
    monitorExporter =
        new BasicJeriExporter(daemonEndpoint,
                              new ProxyTrustILFactory(
                                     integrity, MonitorPermission.class));
    private groupEndpoint = SslServerEndpoint.getInstance(0);
    instantiatorExporter =
        new BasicJeriExporter(groupEndpoint,
                              new ProxyTrustILFactory(
                                     integrity, InstantiatorPermission.class));
    private static keystore = KeyStores.getKeyStore(
                                    "${user.home}${/}.keystore", null);
    private mutualAuth =
        new BasicMethodConstraints(new InvocationConstraints(
            new InvocationConstraint[]{
                Integrity.YES,
                ClientAuthentication.YES,
                ServerAuthentication.YES,
                new ServerMinPrincipal(
                    KeyStores.getX500Principal("phoenix", keystore))},
            null));
    instantiatorPreparer = new BasicProxyPreparer(true, mutualAuth, null);
    monitorPreparer = instantiatorPreparer;
    systemPreparer = instantiatorPreparer;
    loginContext = new LoginContext("phoenix");
    persistenceDirectory = "log";
}

// a Jini ERI configuration

org.apache.river.phoenix {
    persistenceDirectory = "log";
}

// a JRMP configuration
import org.apache.river.phoenix.*;

org.apache.river.phoenix {
    activatorExporter = new ActivatorSunJrmpExporter();
    systemExporter = new SystemAccessExporter();
    monitorExporter = new MonitorAccessExporter();
    instantiatorExporter = new InstantiatorAccessExporter();
    persistenceDirectory = "log";
}

// a JRMP configuration with no access control
import org.apache.river.phoenix.*;
import net.jini.jrmp.JrmpExporter;
import java.rmi.activation.ActivationSystem;

org.apache.river.phoenix {
    registryPort = ActivationSystem.SYSTEM_PORT;
    registryExporter = new RegistrySunExporter(registryPort);
    activatorExporter = new ActivatorSunJrmpExporter(registryPort);
    systemExporter = new SunJrmpExporter(4, registryPort);
    monitorExporter = new JrmpExporter(registryPort);
    instantiatorExporter = null;
    persistenceDirectory = "log";
}

Example security policies

The following are example Phoenix security policies, written using the syntax supported by the default security policy provider.
// a secure Jini ERI Phoenix policy
keystore "file://${user.home}/.keystore";

grant codebase "file:install_dir/lib/jsk-platform.jar" {
  permission java.security.AllPermission;
};

grant codebase "file:install_dir/lib/jsk-lib.jar" {
  permission java.security.AllPermission;
};

grant codebase "file:install_dir/lib/phoenix.jar" {
  permission java.io.FilePermission "${user.home}${/}phoenix.config", "read";
  // next two used in configuration file
  permission java.util.PropertyPermission "user.home", "read";
  permission javax.security.auth.AuthPermission "createLoginContext.phoenix";
  permission javax.security.auth.AuthPermission "doAsPrivileged";
  // next two needed by RegistrySunExporter
  permission java.lang.RuntimePermission
        "accessClassInPackage.sun.rmi.server";
  permission java.lang.RuntimePermission
        "accessClassInPackage.sun.rmi.transport";
};

grant principal "phoenix" codebase "file:install_dir/lib/phoenix.jar" {
  permission javax.security.auth.AuthPermission "getSubject";
  // next two needed by RegistrySunExporter
  permission java.lang.RuntimePermission "accessDeclaredMembers";
  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
  permission java.io.FilePermission "log", "read,write";
  permission java.io.FilePermission "log/-", "read,write,delete";
  // needed to default group location to phoenix-group.jar
  permission java.lang.RuntimePermission "getProtectionDomain";
  // next two needed for default java program for groups
  permission java.util.PropertyPermission "java.home", "read";
  permission java.io.FilePermission "${java.home}${/}bin${/}java", "execute";
  permission java.lang.RuntimePermission "shutdownHooks";
  // used in configuration file
  permission java.io.FilePermission "${user.home}${/}.keystore", "read";
  // next two needed to export remote objects
  permission java.lang.RuntimePermission "getClassLoader";
  permission net.jini.export.ExportPermission "exportRemoteInterface.org.apache.river.phoenix.Activator";
  // next two for outgoing newInstance calls to groups
  // connect to just local host (but not "localhost") would be sufficient
  permission java.net.SocketPermission "*:1024-", "connect";
  permission net.jini.security.AuthenticationPermission
        "${{self}} peer ${{self}}", "connect";
  // next two for incoming remote calls
  permission java.net.SocketPermission "*:1024-", "accept";
  permission net.jini.security.AuthenticationPermission
        "${{self}}", "accept";
};

// grant administrator permission to do most things
grant principal "admin" {
  permission org.apache.river.phoenix.SystemPermission "register*";
  permission org.apache.river.phoenix.SystemPermission "get*";
  permission org.apache.river.phoenix.SystemPermission "set*";
  permission org.apache.river.phoenix.SystemPermission "shutdown";
  permission org.apache.river.phoenix.ExecOptionPermission
        "-Djava.security.auth.login.config=${user.home}${/}login";
  permission org.apache.river.phoenix.ExecOptionPermission
        "-Djavax.net.ssl.trustStore=${user.home}${/}.keystore";
  permission org.apache.river.phoenix.ExecOptionPermission
        "-Djava.security.policy=${user.home}${/}group.security.policy";
};

// typical service needs to verify trust, unregister, and change its descriptor
grant principal "service" {
  permission org.apache.river.phoenix.SystemPermission "getProxyVerifier";
  permission org.apache.river.phoenix.SystemPermission "unregister*";
  permission org.apache.river.phoenix.SystemPermission "*ActivationDesc";
};

// for callbacks from activation groups
grant principal "phoenix" {
  permission org.apache.river.phoenix.SystemPermission "getProxyVerifier";
  permission org.apache.river.phoenix.SystemPermission "activeGroup";
  permission org.apache.river.phoenix.MonitorPermission "*";
};

// part of a secure Jini ERI group policy
keystore "file://${user.home}/.keystore";

grant codebase "file:install_dir/lib/jsk-platform.jar" {
  permission java.security.AllPermission;
};

grant codebase "file:install_dir/lib/phoenix-init.jar" {
  permission java.security.AllPermission;
};

grant codebase "file:install_dir/lib/phoenix-group.jar" {
  permission java.lang.RuntimePermission "setContextClassLoader";
  permission java.lang.RuntimePermission "getClassLoader";
  // needed to set the ActivationGroup instance
  permission java.lang.RuntimePermission "setFactory";
  permission java.lang.RuntimePermission "modifyThreadGroup";
  permission javax.security.auth.AuthPermission "createLoginContext.phoenix";
  permission javax.security.auth.AuthPermission "getSubject";
  permission javax.security.auth.AuthPermission "doAsPrivileged";
  // next two need to call non-public constructors and activate methods
  permission java.lang.RuntimePermission "accessDeclaredMembers";
  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
  // accept from just local host (but not "localhost") would be sufficient
  permission java.net.SocketPermission "*:1024-", "accept";
  // need to connect to local host and codebases of all activatable objects
  permission java.net.SocketPermission "*:1024-", "connect";
  permission net.jini.security.AuthenticationPermission
    "${{alias:phoenix}} peer ${{alias:phoenix}}", "connect,accept";
  permission java.io.FilePermission "${user.home}${/}phoenix.config", "read";
  // used in configuration file
  permission java.io.FilePermission "${user.home}${/}.keystore", "read";

  /* Because ActivationGroupImpl creates the activatable objects, either
   * this codebase also needs whatever permissions are needed by those
   * objects, or the objects must use AccessController.doPrivileged or
   * equivalent to avoid the need for such grants.
   */
};

// for remote calls from Phoenix
grant principal "phoenix" {
  permission org.apache.river.phoenix.InstantiatorPermission "*";
};
Since:
2.0
Skip navigation links

Copyright 2007-2013, multiple authors.
Licensed under the Apache License, Version 2.0, see the NOTICE file for attributions.