0 - Kilim2, what is it for ? |
Kilim is a generic open-source configuration framework for Java, which can be used easily with existing applications, frameworks, and systems. It gives your applications the opportunity to be configured :
It is not intrusive at all, and makes no assumption on how your existing code has been developped:
If you are tired of writing boring configuration code right in the middle of your business logic, Kilim might just be the solution.
0-1 - Overview |
Kilim 2 enables :
1) the defininition of composable abstractions called Templates, capturing encasulated sub-systems defining :
2) the definition of the mapping of these abstractions to existing code in terms of :
3) the recursive assembly of these bricks, allowing to build complex systems :
1 - Static Definition of Templates |
1-1 - Definition of an Abstract Template |
All the examples given in this tutorial are implemented in the Kilim2Tutorial distribution available through CVS.
build.xml file contains the following ant tasks :
|
Let us define in the Kilim description language (which is an XML dialect) the Channel
Template of the
publish & subscribe paradigm :
Publisher
publishes financial data in a Channel
,Subscriber
s receive the published data,Console
is a subscriber able to display the received data,MultiConsole
is a container of Console
s.
The name
attribute of the Template
tag defines the name of the template.
The templates can belong to a package (like in Java), in this case, the name in the attribute
tag must be its fully qualified name, using /
as a separator.
In this example, the Template belongs to package quotes
, it must be stored in a file contained in a
subdirectory quotes
of the CLASSPATH
.
Then we add in the template
body a property
tag defining the type, the value and the accessibility
of the property name
.
Available types are :
- byte, short, int, long
- char
- float, double
- boolean
- string
public
, the property can be accessed from any template,private
, it can be accessed only from the containig template,protected
, it can be accessed by all the templates extend
ing the containing template.These levels of accessibility will be the same for all the members of the template, with the same semantics.
We can then define two extension slot
s. They will be used to plug publishers and subscribers into the channel.
In this first definition step, we stay at a very abstract level, an do not make any assumption about :
Channel
abstraction,
1-2 - Specifying an abstract object mapping for the
|
Now we will define the object interfaces offered and required by the Template
. These interfaces will, in the next section,
be mapped on one, or many, language objects. The aim of the current step is not to describe the precise mapping of
the template yet, but to define, for each interface (we call them ports
) :
private
, protected
or public
,arity
(will it be mapped on one, or many language objects),offered
or required
by the template.
Ports
are used to exchange references on object instances between the template and the rest of the system.
The ports
defined directly in the template
body are used to provide/retrieve references to/from the outside of the template.
The ports
defined in the slot
blocks are used to define how the template exchanges references with another
template when it is plugged into it.
The template expects to :
offered ports
required ports
it offers.In out example, the template Channel :
The subscriber port has an "any" arity, it means that many subscriber templates can be inserted in the slot, while only one publisher can be inserted in the subscriber slot because port "publisher" has an arity of 1.
This template is still abstract, as, if it is instantiated, no language object will be instantiated.
The template Channel is defined in file kilim/quotes/Channel.kilim .
|
1-3 - Pinning a template to code |
Now time has come to define how this template will be mapped on language objects.
First, we want to preserve the genericity of our abstract template Channel
, in order to define, if
necessary, different mappings, all of them compliant with the abstract specification.
We will do that by a classical extension mechanism. LocalChannel
template extends
Channel,
this means that it inherits all the properties, slots and ports of Channel
.
channel
is mapped on code. This mapping will be achieved by a bind
block. In this bind
we declare that the binding of the port is done by instanciating a new
object of class quotes.local.LocalChannel
. This instanciation is done by calling the constructor
taking a String as parameter. This parameter will be provided by the property name
defined in the
super template. This is achieved through a reference
tag designing the property through is name.
bind
ing event is generated by the runtime. This event can
trigger a call to any specified method on any given interface, with an arbitrary number of parameter among :
channel
, with the object mapping que port subscriber
of the plugged component as a parameter.
publisher
port is quite different. Here, the event that triggers the call to the method setChannel()
is the binding of the port channel
itself. This means that as soon as the LocalChannel
instance will be created,
the method will be called. This will work only if the plug of the publisher into the channel has been done prior to the instanciation of
the template.
The template LocalChannel is defined in file kilim/quotes/local/LocalChannel.kilim .
|
1-4 - Assembling Components |
Let us now build a template by simply assembling existing templates.
For example we want to build a template representing a sub-system
with one channel, one publisher plugged into it, and one subscriber.
The tag instance
allows us tu create an instance of a given template
. The attributes will define :
In the instance
block we can override existing properties in order to change their default value. Here the property
name
, the default value of which was "Default Channel", is replaced by "USD Channel".
Now we will specify which template instance (we will call these "components" from now on) is plugged in which slot of the channel.
To do that, we use the tag plug
. Its attributes reference :
In this example we want to make this template ready to be assembled with others, with a view to building a global console
displaying the quotations of different currencies.
To achieve this goal, we create an external port, named console
which makes the port console
of an inner
component available from the outside. It is thus ready to be plugged, in turn, in an other higher level template, like in the following example :
Many assembly examples are available in the distribution, all of them in the kilim/quotes/application directory :
|
1-5 - Creating a component from Java code |
Now we can try to instantiate one of the components we have previously specified via .kilim
files.
The API to do so is extremely straightforward :
newComponent()
from helper class org.objectweb.kilim.helpers.KilimHelper
- the fully qualified name of the template that should be instantiated,
- a class, the ClassLoader of which should be used to load the classes specified in the
.kilim
file.
getInterface()
to retrieve a reference on the console interface, which the runtime
representation of the console port
,
getValue()
to retrieve a reference on the language object (here a javax.swing.JFrame
Of course, due to the lack of genericity in Java, we then have to cast this reference into a javax.swing.JFrame
reference in order to make it visible.
In order to correctly instantiate and configure the console interface, all required objects will be instantiated and configured
lazily, according to the .kilim
specification.
The Launcher 's code can be found in src/test/Launcher.java
|
2 - Dynamic Reconfiguration |
2-1 - Reconfiguration API |
Now we have an instantiated component running (e.g. a Channel
), mapped on instantiated language objects.
Kilim 2.0 gives us the possibility of, to some extent, to reconfigure the Component
once instantiated.
In order to do that the only thing we need is have kept, in some way, a reference on the Component
we want to modify.
Here are the different modifications we can make on a component :
property
This can be achieved in two easy steps :
component.getValue("propertysName")
, what we get then is a ComponentInterface
,ComponentProperty
by calling the isProperty()
method,true
, then we cast is into a ComponentProperty
reference,setValue(value)
method.
This changes the value of the ComponentProperty
, but not the state of the underlying language object.
This is not a drawback, this is a feature ! In fact, we probably want to change the value of many properties before we
actually change the state of the real objects (for example we change the value of property A and B, and then trigger
a unique call to the setAandB(A,B)
of the language object)). The moment when we want the changes to take effect
on the objects must then be explicit, and this is precisely what we explain in the next paragraph.
Another question that rises when it comes to change a property's value is :
What happens when I change the value of a property which is used to configure the state of many language object, through different methods
The answer is quite simple : you will have to call explicitly the method update()
on
all the ComponentInterface
s that are likely to be concerned by the property's modification.
This is as simple as it gets :
ComponentSlot
where you want to plug your new Component
, through a call to cmpnnt.getSlot("slotName")
,slt.plug(newComponent)
.In this case, nothing more needs to be done, as the semantics of insertion is intrinsicly atomic.
The unplug is done exactly in the same way. This code snippet sums up the whole thing.
2-2 - Reconfiguration through JMX |
The Java Management Extensions (JMX) technology represents a universal, open technology for management, and monitoring that can be deployed wherever management and monitoring are needed. In Kilim, JMX is used to :
Components
through different protocols (RMI, HTTP, etc.),- properties reconfiguration,
- runtime plug and unplug of components,
Component
Making an existing component monitorable through JMX can easily be achieved in a few simple steps :
extending
the template you want to monitor,- add an instance of template
JMX Deployment Data
namedJMX
near the member tou want to monitor,- and bind its ports
{visible,editable}
to the members you want to make {visible,editable}.
monitored sub-components
port
of the JMX instance to the port THIS
of the sub-component you want to monitor.
See template kilim/quotes/application/Yet Another Composite Inherited with JMX.kilim for more details.
|
Component
Now take the usual steps to launch you JMX server and your adaptors (a complete example is given in the
test.LauncherJMX
class).
The only thing you have to do to make your Kilim Component
visible through any JMX
console is to register it on the server. This can be achieved simply by :
org.objectweb.kilim.tools.jmx.ComponentMBean
wrapping the Kilim Component,registerMBean()
.
And this is it. Your component is now visible, with all its exported properties and slots. You can now, through any JMX compliant console, do all the operations listed in the previous section (Reconfiguration API) without a line of Java, simply by clicking.
The LauncherJMX 's code can be found in src/test/LauncherJMX.java
|