HomeIndex > GBeansArticle1

Content

Geronimo GBean Architecture

Abstract

Geronimo is built on a Kernel that knows nothing about J2EE, rather it's a general-purpose Inversion of Control (IOC) framework for components called GBeans. This article explains the theoretical aspects of inversion of control, then describes the GBean Life Cycle, GBean States and Dependency Injection with a few examples, and then explains how to write a simple GBean.

Introduction

Consider a platform like Linux or Windows which has an operating system core that provides system calls and set of applications that run on top of the those system calls. Geronimo is built on a similar core which knows nothing about J2EE and that core, the Geronimo Kernel, is essentially a framework for components called GBeans. Any complex system can be modeled as set of components that hold states, a set of relationships among them, and how each component reacts to certain events. The Geronimo Kernel provides a framework for the GBeans with following services:

  1. GBeans can hold states (either persistent or non-persistent)
  2. Relationships among the GBeans can be defined with them
  3. GBeans can contain logic that defines how they react to events

Almost everything in Geronimo is a GBean: containers, connectors, adapters, applications and so on. The relationship between GBeans can be viewed as a mesh of components as explained by the following figure:

Any J2EE Container is comprised of two classes of citizens; the first class citizens like Web Containers and EJB Containers that provide the core services, and the second class citizens which are the applications such as EJB's and Servlets that are deployed in the first class citizens. Geronimo's architecture can be explained in terms of GBean behaviors. Geronimo is set of running GBeans, and each first class citizen is a set of GBeans that are related to one another. They are loaded via "plans" which are essentially an XML serialization of GBeans. When the plans are loaded and started the GBeans that are included in the plans will be started, i.e. the EJB Containers and/or the Web Containers will be started.

The applications that run in a J2EE Container are described by Deployment Descriptors and Java Classes that are packed as a J2EE application archive. The Geronimo Deployer parses those archives and constructs GBeans out of them. When the GBeans are started the application is deployed and available. The GBeans can be serialized and stored and as a result once an application is converted to GBeans and added to Geronimo it will be available even after the server is restarted.

GBeans From a Conceptual Point of View

The concepts that provide the base for the GBeans have much longer history in the sense that the Geronimo Kernel is an inversion of Control, IOC framework where the IOC is also known as the dependency injection. The essence of IOC or the dependency injection is to create a loosely coupled architecture where the dependencies among components are managed by the framework. When a component has a dependency on other component the IOC framework will find the correct component and make it available to the first component. The dependency injection name comes from the fact that the framework automatically injects the dependencies to the component. Most simple form of IOC or the Dependency injection is to by system wide known values. For an instance let us assume this is Web Service that is to deployed inside Axis, do not worry about Axis but just assume that the Axis will load the following class and call the doit() method.

public class WS { public WS(MesageContext msgctx) { } public void doit() { } }

But Axis has something called

!MessageContext that has all the configuration information. In order to give a reference of !MessageContext to the WS class Axis can say "if you have a Constructor like XX(!

MessageContext) or you have a method like setMessageContext(!MessageContext msgctx) I will inject the !MessageContext to you".

This is the simplest form of dependency injection the framework look at the class and find out that the class expected a !MessageContext to be inject it in. Or in other words the IOC framework inspect our components for certain patterns in methods, Parameters and the Constructors that when the pattern is found provides a Service to the Component, in the example we consider the service is giving a reference to the message Context.

To get a better understanding about the use of the IOC one should look at the methods that were used to develop the system with decoupled components. The decoupling of the system is all about the interaction among the components.

  1. Interfaces and Header files together with Factories provide decoupling of the system with minimal amount of coding.
  2. The property files together with the, interfaces and factories (e.g. loading of the different implementations of JNDI using the property files.) make it possible to decouple with out recompilation of the code.
  3. Registry Service together with lookups separate the service information from the caller by hiding them behind a name.
  4. IOC or the dependency injection allows the automatic injection of references as they become available.

The Idea of IOC is to define all the relationships among the components in terms of the framework level configurations and each component is injected to the other components that refer the service when the first component is available. For an example let us consider the how a GBean G1 obtains a reference to a Configuration Store. The G1 is configured specifying the patterns of the reference it is expected.

e.g. bean.setReferencePatterns("configStore,*:type=configStore,*");

When a another GBean whose name match the pattern ":type=configStore," is started it is automatically injected in to the G1. This is more like Aspect Oriented programming, AOP where the developer says to the framework what is need to be done rather than how to do it.

  • But this does not means that the IOC is the only way to create decoupled systems and all the four ways of doing is useful and used in practice. It is matter of choosing the right tool for the occasion and there are good examples as well as bad examples for each.

GBean Life Cycle

A GBean can be in any of the three states: stored, loaded or running. In the stored state it is saved in a configuration store (also known as a "plan"). When it is loaded the kernel has mapped it to a name and knows about it. Each GBean is bound with a name at start up; that name is not persistent. This can be explained by an analogy, we can think about the GBean as a Java Class and the name as a variable name that refers to a specific instance of the class. In the same way that the class can have more than one instance, the same GBean can be loaded under more than one name and stored in number of places.

State of GBeans

GBeans have two types of states, attributes and references. The references will be covered under the third part. The attributes are two types, persistent and non-persistence. The GBeans can be stored and restored using the built-in support. There are special types of magic attributes defined by the architecture; the values for the magic attributes loaded depend on the environment the GBean loaded on. For an example the magic attribute "kernel" refers to the Kernel and when it is specified at the constructor it is automatically injected to the class by the framework. Similarly the !ClassLoader attributes inject the current class loader and the !ObjectName attributes inject the current name the GBean started under. Complete list of all magic attributes can be found in the GBeanMBean class in the kernel. The magic attributes can not be persistent as they are bound with the environment it is started. Note that in our first example of dependency injection where a Web Service is deployed to Axis, the !MessageContext is a magic attribute.

A GBean with one attribute "val" will look like this:

public class MyGBean implements GBeanLifecycle { private final String val; static { GBeanInfoBuilder infoFactory = ... // attributes infoFactory.addAttribute("val", String.class, true); // operations infoFactory.addOperation("getBean1"); infoFactory.setConstructor(new String[]{"val"}); GBEAN_INFO = infoFactory.getBeanInfo(); } public ConstructorInjectionGbean(String val ) { this.val = val; } ... }

We can start our GBean with the following code. The GBean implementation is done on top of JMX and the GBeanMBean accepts a GBean info and converts it into an Mbean.

GBeanMBean gmb = new GBeanMBean(MyGBean.GBEAN_INFO); gmb.setAttribute("value","To be or not to be that is the question"); ObjectName myGbeanName = ObjectName.newInstance("Geronimo.my:name=mine"); kernel.loadGBean(myGbeanName,gmb); kernel.startGBean(myGbeanName); //dowork kernel.stopGBean(myGbeanName); kernel.unloadGBean(myGbeanName);

Dependency Injection

There are two types of relationships

  1. Single references
  2. Reference Collections

Single References

The references are injected to a GBean in one of the two ways; the getter/setter injection and the constructor injection based on how the dependencies injected in to the java Class that use to implements the GBean. With the getter and setter injection framework injects the attributes/ references using the getter and setter methods whereas the constructor injection injects the parameters as constructor parameters. The second approach is more preferable as with that the GBeans support so called good citizen pattern making sure that the GBean is in usable state once it is started. Geronimo supports both methods but moving towards the complete constructor injection.

Following is a simple GBean that uses constructor injection.

public class MyGBean implements GBeanLifecycle { private final String GBean1 bean1; static { GBeanInfoBuilder infoFactory = ... // attributes infoFactory.addReferance("Bean1", GBean1.class, true); ---------(A) // operations infoFactory.setConstructor(new String[]{"Bean1"}); -------------(B) GBEAN_INFO = infoFactory.getBeanInfo(); } public ConstructorInjectionGbean(GBean1 bean1) {-----------(C) this.bean1 = bean1; } ... }

Here the Line (A) said that there is a reference to the GBean1 and line (B) registered the matching constructor (C). In the Following code the developer specified the reference instance of GBean1 and start GBeans.

ObjectName GBean1= ObjectName.newInstance("Geronimo.my:name=gb1"); ... //start the GBean 1 GBeanMBean gmb = new GBeanMBean(MyGBean.GBEAN_INFO); gmb.setReferencePatterns("GBean1", GBean1Name); ObjectName myGbeanName = ObjectName.newInstance("Geronimo.my:name=mine"); kernel.loadGBean(myGbeanName,gmb); kernel.startGBean(myGbeanName); //dowork

When the MyGBean is started an instance of GBean1 is injected via the constructor.

Reference Collections

Only difference when the Reference collection come in to play is replacing the GBean1 references in the above example with Collection and when specifying the name of the referenced GBean it is specified as a pattern with "*" and "?".

bean.setReferencePatterns("Geronimo.my:*");

The code asks the framework to make available all the GBeans with the Domain name "Geronimo.my:". When a GBean that whose matches the patterns is started it is automatically injected to the referee GBean.

Logic in GBeans

GBeans leave the handling of the logic more or less in the form of usual java code and the Methods in the GBean class can be register to the GBean by adding a

infoFactory.addOperation("doit", new Object[]{val1,val2},new String[]{ val1.getClass().getName(), val2.getClass().getName()}); --------- (A)

Kernel supports kernel.invoke("objectName","methodName",...) using which one can call any method in any other GBean. But use of this is discouraged and accepted way to do it is to register reference pattern and obtain the instance of the other GBean injected in and code simple Java style on the code.

E.g. Say GBean G1 need to call the doit() on GBean G2 the crude way to do it is to

kernel.invoke("G2Name","doit");

Correct way to do it is write G1 like

class G1 implements GBeanLifeCycle { ... private final G2 g2; static { GBeanInfoBuilder infoFactory = new GBeanInfoBuilder("G1", G1.class); infoFactory.addReference("G2",G2.class); --------- (A) infoFactory.addConstructor("G2"); ---------------- (B) GBEAN_INFO = infoFactory.getBeanInfo(); } public ConstructorInjectionGbean(G2 g2 ) { this.g2 = g2; } public void doG1Work(){ g2.doit(); } }

At line (A) defining there is a reference to G2 and at line (B) adding a constructor that inject the G2 and G1 obtain a reference to the G2. Then logic is implemented using simple Java code. GBeans have interfaces that make the GBean implements a given method. By making the GBean class implements the interface and putting a entry infoFactory.addInterface("interfaceName") will automatically add the all the methods in the interface to the GBean and the getters and setter will be mapped to the attributes.

Sample Code

You can find few Sample GBeans and few Test Cases that demonstrate their use from http://apache.org/~hemapani/docs/gbeans-sample.zip. To run the test case you need maven installed in your machine.

Summary

The GBean architecture knows nothing about J2EE and a General framework for the developed loosely coupled System with IOC. It can be used outside Geronimo and can have potential to be the generic Architecture for loosely coupled Systems. This article explains the concepts and how to use the GBeans.

References

1 The Article, A Brief Introduction to IoC by Sam Newman, 02/10/2004, http://today.java.net/pub/a/today/2004/02/10/ioc.html

2 Inversion of Control Containers and the Dependency Injection pattern, by Martin Fowler, http://www.martinfowler.com/articles/injection.html

3 IoC Overview, Web Work dashboard, http://wiki.opensymphony.com/display/WW/IoC+Overview

4 Article Dependency Inversion Principle, http://www.objectmentor.com/resources/articles/dip.pdf

5 Interview with Dian Sundstrom, at Server Side, http://www.theserverside.com/talks/videos/DainSundstrom/dsl/q01.html

6 Geronimo Kernel Module, http://geronimo.apache.org

--Please feel free to do the alternation and drop me a note at Srinath Perera<hemapani@apache.org> so I keep this in sync