The Hundred-Kilobyte Kernel (HK2) is the lightweight and extensible kernel of Enterprise Server. To interact with Enterprise Server, add-on components plug in to this kernel. In the HK2 component model, the functions of an add-on component are declared through a contract-service implementation paradigm. An HK2 contract identifies and describes the building blocks or the extension points of an application. An HK2 service implements an HK2 contract.
The following topics are addressed here:
The Hundred-Kilobyte Kernel (HK2) provides a module system and component model for building complex software systems. HK2 forms the core of Enterprise Server's architecture.
The module system is responsible for instantiating classes that constitute the application functionality. The HK2 runtime complements the module system by creating objects. It configures such objects by:
Injecting other objects that are needed by a newly instantiated object
Injecting configuration information needed for that object
Making newly created objects available, so that they can then be injected to other objects that need it
An HK2 service identifies the building blocks or the extension points of an application. A service is a plain-old Java object (POJO) with the following characteristics:
The object implements an interface.
The object is declared in a JAR file with the META-INF/services file.
To clearly separate the contract interface and its implementation, the HK2 runtime requires the following information:
Which interfaces are contracts
Which implementations of such interfaces are services
Interfaces that define a contract are identified by the org.jvnet.hk2.annotation.Contract annotation.
@Retention(RUNTIME) @Target(TYPE) public @interface Contract { }
Implementations of such contracts should be identified with an org.jvnet.hk2.annotations.Service annotation so that the HK2 runtime can recognize them as @Contract implementations.
@Retention(RUNTIME) @Target(TYPE) public @interface Service { ... }
For more information, see Service.
Once Services are defined, the HK2 runtime can be used to instantiate or retrieve instances of services. Each service instance has a scope, specified as singleton, per thread, per application, or a custom scope.
You can specify the scope of a service by adding an org.jvnet.hk2.annotations.Scoped annotation to the class-level of your @Service implementation class. Scopes are also services, so they can be custom defined and added to the HK2 runtime before being used by other services. Each scope is responsible for storing the service instances to which it is tied; therefore, the HK2 runtime does not rely on predefined scopes (although it comes with a few predefined ones).
@Contract public abstract class Scope { public abstract ScopeInstance current(); }
The following code fragment shows how to set the scope for a service to the predefined Singleton scope:
@Service public Singleton implements Scope { ... } @Scope(Singleton.class) @Service public class SingletonService implements RandomContract { ... }
You can define a new Scope implementation and use that scope on your @Service implementations. You will see that the HK2 runtime uses the Scope instance to store and retrieve service instances tied to that scope.
Do not call the new method to instantiate components. Instead, retrieve components by using the ComponentManager instance. The simplest way to use the ComponentManager instance is through a getComponent(ClassT contract) call:
public <T> T getComponent(Class<T> clazz) throws ComponentException;
More APIs are available at ComponentManager.
Components can attach behaviors to their construction and destruction events by implementing the org.jvnet.hk2.component.PostConstruct interface, the org.jvnet.hk2.component.PreDestroy interface, or both. These are interfaces rather than annotations for performance reasons.
The PostConstruct interface defines a single method, postConstruct, which is called after a component has been initialized and all its dependencies have been injected.
The PreDestroy interface defines a single method, preDestroy, which is called just before a component is removed from the system.
@Service(name="com.example.container.MyContainer") public class MyContainer implements Container, PostConstruct, PreDestroy { @Inject Logger logger; ... public void postConstruct() { logger.info("Starting up."); } public void preDestroy() { logger.info("Shutting down."); } }
Inversion of control (IoC) refers to a style of software architecture where the behavior of a system is determined by the runtime capabilities of the individual, discrete components that make up the system. This architecture is different from traditional styles of software architecture, where all the components of a system are specified at design-time. With IoC, discrete components respond to high-level events to perform actions. While performing these actions, the components typically rely on other components to provide other actions. In an IoC system, components use injection to gain access to other components, and extraction to make component variables available to the system.
Services usually rely on other services to perform their tasks. The HK2 runtime identifies the @Contract implementations required by a service by using the org.jvnet.hk2.annotations.Inject annotation. Inject can be placed on fields or setter methods of any service instantiated by the HK2 runtime. The target service is retrieved and injected during the calling service's instantiation by the component manager.
The following example shows how to use @Inject at the field level:
@Inject ConfigService config;
The following example shows how to use @Inject at the setter level:
@Inject public void set(ConfigService svc) {...}
Injection can further qualify the intended injected service implementation by using a name and scope from which the service should be available:
@Inject(Scope=Singleton.class, name="deploy") AdminCommand deployCommand;
Although all services are automatically placed into a scope for later retrieval, a component may need to extract more than itself. One practical way of doing so is to use a factory service. For simplicity, however, the HK2 runtime extracts all fields or getter methods annotated with the org.jvnet.hk2.annotations.Extract annotation.
The following example shows how to use @Extract at the field level:
@Extract ConfigService config;
The following example shows how to use @Extract at the getter level:
@Extract public ConfigService getConfigService() {...}
Extraction, like injection, can also use the name and scope annotation fields to further qualify the extracted Contract implementation.
Extracted fields and properties are made available to other service instances by exporting them to the org.jvnet.hk2.component.Habitat instance. Habitat instances can be injected into other components, and the components can then extract and use the data contained in the Habitat instance.
@Inject protected Habitat habitat; ... public void doSomething(String name) { ... ConfigService config = habitat.getComponent(ConfigService.class); ... }
Injection of instances that have not been already instantiated triggers more instantiation. You can see this as a component instantiation cascade where some code requests for a high-level service will, by using the @Inject annotation, require more injection and instantiation of lower level services. This cascading feature keeps the implementation as private as possible while relying on interfaces and the separation of contracts and providers.
The following example shows how the instantiation of DeploymentService as a Startup contract implementation will trigger the instantiation of the ConfigService.
@Contract public interface Startup {...}
Iterable<Startup> startups; startups = componentMgr.getComponents(Startup.class);
@Service public class DeploymentService implements Startup { @Inject ConfigService config; }
@Service public Class ConfigService implements ... {...}
Enterprise Server discovers add-on components by identifying Java programming language classes that are annotated with the org.jvnet.hk2.annotation.Service annotation.
To identify a class as an implementation of an Enterprise Server service, add the org.jvnet.hk2.annotation.Service annotation at the class-definition level of your Java programming language class.
@Service public class SamplePlugin implements ConsoleProvider { ... }
The @Service annotation has the following elements. All elements are optional.
The name of the service. The default value is an empty string.
The scope to which this service implementation is tied. The default value is org.jvnet.hk2.component.PerLookup.class.
The factory class for the service implementation, if the service is created by a factory class rather than by calling the default constructor. If this element is specified, the factory component is activated, and Factory.getObject is used instead of the default constructor. The default value of the factory element is org.jvnet.hk2.component.Factory.class.
The following example shows how to use the optional elements of the @Service annotation:
@Service (name="MyService", scope=com.example.PerRequest.class, factory=com.example.MyCustomFactory) public class SamplePlugin implements ConsoleProvider { ... }
If you are using Maven 2 to build HK2 components, invoke the auto-depends plug-in for Maven so that the META-INF/services files are generated automatically during build time.
<plugin> <groupId>com.sun.enterprise</groupId> <artifactId>hk2-maven-plugin</artifactId> <configuration> <includes> <include>com/sun/enterprise/v3/**</include> </includes> </configuration> </plugin>
This example shows how to use @Contract and @Service and the resulting META-INF/services files.
The interfaces and classes in this example are as follows:
package com.sun.v3.annotations; @Contract public interface Startup {...} package com.wombat; @Contract public interface RandomContract {...} package com.sun.v3; @Service public class MyService implements Startup, RandomContract, PropertyChangeListener { ... }
These interfaces and classes generate this META-INF/services file with the MyService content:
com.sun.v3.annotations.Startup com.wombat.RandomContract