The environment component together with the wire context is a kind of Inversion of Control (IoC) container. It reads configuration information that describes how objects should be instantiated, configured and wired together.
The environment is used to retrieve resources and services needed by Activity implementations and the Process Virtual Machine itself. The main purpose is to make various aspects of the Process Virtual Machine configurable so that the PVM and the languages that run on top can work in a standard Java environment as well as an enterprise Java environment.
The environment is partitioned into a set of contexts. Each context can have its own lifecycle. For instance, the application context will strech over the full lifetime of the application. The block context only for the duration of a try-finally block. Typically a block context represents a database transaction. Each context exposes a list of key-value pairs.
To start working with an environment, you need an EnvironmentFactory. One single environment factory object can be used throughout the complete lifetime of the application. So typically this is kept in a static member field. The EnvironmentFactory itself is the application context.
An EnvironmentFactory is typically obtained by parsing a configuration file like this:
static EnvironmentFactory environmentFactory = EnvironmentFactory.parse(new ResourceStreamSource("pvm.cfg.xml");
See javadocs package org.jbpm.stream for more types of stream sources.
There is a default parser in the environment factory that will create DefaultEnvironmentFactorys. The idea is that we'll also support spring as an IoC container. But that is still TODO. Feel free to help us out :-). The parser can be configured with the static setter method EnvironmentFactory.setParser(Parser)
An environment exists for the duration of a try-finally block. This is how an environment block looks like:
Environment environment = environmentFactory.openEnvironment();
try {
...
} finally {
environment.close();
}
The environment block defines another lifespan: the block context. A transaction would be a typical example of an object that is defined in the block context.
Inside such a block, objects can be looked up from the environment by name or by type. If objects can looked up from the environment with method environment.get(String name) or <T> T environment.get(Class<T>).
when an environment is created, it has a application context and a block context.
In the default implementation, the application context and the block context are WireContexts. A WireContext contains a description of how its objects are created and wired together to form object graphs.
To start with a simple example, we'll need a Book:
public class Book {
...
public Book() {}
...
}
Then let's create an environment factory that knows how to create book
static EnvironmentFactory environmentFactory = EnvironmentFactory.parse(new StringStreamSource( "<environment>" + " <application>" + " <object name='book' class='org.jbpm.examples.ch09.Book' />" + " </application>" + "</environment>" ));
Now we'll create an environment block with this environment factory and we'll look up the book in the environment. First the lookup is done by type and secondly by name.
Environment environment = environmentFactory.openEnvironment(); try { Book book = environment.get(Book.class); assertNotNull(book); assertSame(book, environment.get("book")); } finally { environment.close(); }
To prevent that you have to pass the environment as a parameter in all methods, the current environment is maintained in a threadlocal stack:
Environment environment = Environment.getCurrent();
Contexts can be added and removed dynamically. Anything can be exposed as a Context.
public interface Context { Object get(String key); <T> T get(Class<T> type); Set<String> keys(); ... }
When doing a lookup on the environment, there is a default search order in which the contexts will be scanned for the requested object. The default order is the inverse of the sequence in which the contexts were added. E.g. if an object is defined in both the application context and in the block context, the block context is considered more applicable and that will be scanned first. Alternatively, an explicit search order can be passed in with the get lookups as an optional parameter.