blob: 60552322dae8cba535b4f4fb1af83c44bc8e5ad4 [file] [log] [blame]
type=page
status=published
title=Writing HK2 Components
next=extending-the-admin-console.html
prev=introduction.html
~~~~~~
= Writing HK2 Components
[[GSACG00002]][[ghmna]]
[[writing-hk2-components]]
== Writing HK2 Components
The Hundred-Kilobyte Kernel (HK2) is the lightweight and extensible
kernel of {productName}. To interact with {productName}, 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:
* link:#ghokq[HK2 Component Model]
* link:#ghojt[Services in the HK2 Component Model]
* link:#ghokt[HK2 Runtime]
* link:#ghojb[Inversion of Control]
* link:#ghmoe[Identifying a Class as an Add-On Component]
* link:#ghpvp[Using the Apache Maven Build System to Develop HK2 Components]
[[ghokq]][[GSACG00091]][[hk2-component-model]]
=== HK2 Component Model
The Hundred-Kilobyte Kernel (HK2) provides a module system and component
model for building complex software systems. HK2 forms the core of
{productName}'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
[[ghojt]][[GSACG00092]][[services-in-the-hk2-component-model]]
=== Services in the HK2 Component Model
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.annotations.Contract` annotation.
[source,java]
----
@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.
[source,java]
----
@Retention(RUNTIME)
@Target(TYPE)
public @interface Service {
...
}
----
For more information, see
http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Service.html[`Service`]
(`http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Service.html`).
[[ghokt]][[GSACG00093]][[hk2-runtime]]
=== HK2 Runtime
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.
[[ghoib]][[GSACG00181]][[scopes-of-services]]
==== Scopes of Services
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).
[source,java]
----
@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:
[source,java]
----
@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.
[[ghoky]][[GSACG00182]][[instantiation-of-components-in-hk2]]
==== Instantiation of Components in HK2
Do not call the `new` method to instantiate components. Instead,
retrieve components by using the `Habitat` instance. The simplest way to
use the `Habitat` instance is through a `getComponent(Class`T
`contract)` call:
[source,java]
----
public <T> T getComponent(Class<T> clazz) throws ComponentException;
----
More APIs are available at
http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/component/Habitat.html[`Habitat`]
(`http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/component/Habitat.html`).
[[ghois]][[GSACG00183]][[hk2-lifecycle-interfaces]]
==== HK2 Lifecycle Interfaces
Components can attach behaviors to their construction and destruction
events by implementing the
http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/component/PostConstruct.html[`org.jvnet.hk2.component.PostConstruct`]
(`http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/component/PostConstruct.html`)
interface, the
http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/component/PreDestroy.html[`org.jvnet.hk2.component.PreDestroy`]
(`http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/component/PreDestroy.html`)
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.
[[GSACG00014]][[ghoqv]]
Example 2-1 Example Implementation of `PostContruct` and `PreDestroy`
[source,java]
----
@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.");
}
}
----
[[ghojb]][[GSACG00094]][[inversion-of-control]]
=== Inversion of Control
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.
[[ghoiz]][[GSACG00184]][[injecting-hk2-components]]
==== Injecting HK2 Components
Services usually rely on other services to perform their tasks. The HK2
runtime identifies the `@Contract` implementations required by a service
by using the
http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Inject.html[`org.jvnet.hk2.annotations.Inject`]
(`http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Inject.html`)
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:
[source,java]
----
@Inject
ConfigService config;
----
The following example shows how to use `@Inject` at the setter level:
[source,java]
----
@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:
[source,java]
----
@Inject(Scope=Singleton.class, name="deploy")
AdminCommand deployCommand;
----
[[ghoic]][[GSACG00186]][[instantiation-cascading-in-hk2]]
==== Instantiation Cascading in HK2
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.
[[GSACG00015]][[ghquz]]
Example 2-2 Example of Instantiation Cascading
The following example shows how the instantiation of `DeploymentService`
as a `Startup` contract implementation will trigger the instantiation of
the `ConfigService`.
[source,java]
----
@Contract
public interface Startup {...}
Iterable<Startup> startups;
startups = habitat.getComponents(Startup.class);
@Service
public class DeploymentService implements Startup {
@Inject
ConfigService config;
}
@Service
public Class ConfigService implements ... {...}
----
[[ghmoe]][[GSACG00095]][[identifying-a-class-as-an-add-on-component]]
=== Identifying a Class as an Add-On Component
{productName} 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 {productName} service,
add the `org.jvnet.hk2.annotations.Service` annotation at the
class-definition level of your Java programming language class.
[source,java]
----
@Service
public class SamplePlugin implements ConsoleProvider {
...
}
----
The `@Service` annotation has the following elements. All elements are
optional.
`name`::
The name of the service. The default value is an empty string.
`scope`::
The scope to which this service implementation is tied. The default
value is `org.glassfish.hk2.scopes.PerLookup.class`.
`factory`::
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`.
[[GSACG00016]][[ghoip]]
Example 2-3 Example of the Optional Elements of the `@Service` Annotation
The following example shows how to use the optional elements of the
`@Service` annotation:
[source,java]
----
@Service (name="MyService",
scope=com.example.PerRequest.class,
factory=com.example.MyCustomFactory)
public class SamplePlugin implements ConsoleProvider {
...
}
----
[[ghpvp]][[GSACG00096]][[using-the-apache-maven-build-system-to-develop-hk2-components]]
=== Using the Apache Maven Build System to Develop HK2 Components
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.
[[GSACG00017]][[ghqsa]]
Example 2-4 Example of the Maven Plug-In Configuration
[source,xml]
----
<plugin>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-maven-plugin</artifactId>
<configuration>
<includes>
<include>com/example/**</include>
</includes>
</configuration>
</plugin>
----
[[GSACG00018]][[ghoik]]
Example 2-5 Example of `META-INF/services` File Generation
This example shows how to use
http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Contract.html[`@Contract`]
(`http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Contract.html`)
and
http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Service.html[`@Service`]
(`http://hk2.java.net/auto-depends/apidocs/org/jvnet/hk2/annotations/Service.html`)
and the resulting `META-INF/services` files.
The interfaces and classes in this example are as follows:
[source,java]
----
package com.example.wallaby.annotations;
@Contract
public interface Startup {...}
package com.example.wombat;
@Contract
public interface RandomContract {...}
package com.example.wallaby;
@Service
public class MyService implements Startup, RandomContract, PropertyChangeListener {
...
}
----
These interfaces and classes generate this `META-INF/services` file with
the `MyService` content:
[source]
----
com.example.wallaby.annotations.Startup
com.example.wombat.RandomContract
----