Skip to main content

skip to main content

developerWorks  >  Open source | Java technology | WebSphere  >

Dependency injection in Apache Geronimo, Part 2: The next generation

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss


Rate this page

Help us improve this content


Level: Intermediate

Neal Ford (nford@thoughtworks.com), Application Architect, ThoughtWorks Inc.

14 Feb 2006

Continue the exploration of dependency injection (DI) in Apache Geronimo that began in Part 1 of this two-part article series. Get an overview of the Geronimo architecture, discover how DI impacts Geronimo, and learn how to use the DI features in Geronimo to change the way you write code. This article also covers how GBeans work and how Geronimo handles both constructor and setter injection.

The first article of this series discussed how DI works at the core level, separate from the particular implementation found in Geronimo. That article demonstrated why DI is an effective way to handle coupling points between classes that contain references to each other. This article expands on those ideas and puts them into the context of Geronimo. Along the way, you'll learn about uses for DI and understand how Geronimo implements it (both constructor and setter).

DI has real-world benefits for decoupling code. Here are a few examples. Say you have a human resources department that bases pay raises on the customer for which an employee works. To handle this, you implement customers as an abstract base class with many different subclasses (for example, financial, manufacturing, and so on). When you write the Human Resources class, you don't know which type of customer you'll process for a given employee. Thus, the Human Resources class depends on a specific implementation of a customer class. With DI, you can allow the container to inject the appropriate customer class either by configuring the container or implementing the injection in the code.

The second, and more typical, example is the use of a persistence framework. Your container knows that it wants to use a persistence framework (such as Hibernate, Java Data Objects [JDO], or iBATIS) and that each framework offers a common set of functions. Using DI, the container can inject the code for a particular persistence framework using container configuration. Having persistence this loosely coupled allows you to more easily switch one framework for another.

Geronimo's architecture

Geronimo isn't built like most traditional Java™ 2 Platform, Enterprise Edition (J2EE) application servers where the classes that make up the application server are tightly bound into the application server's code base. Geronimo defines a basic kernel where you can plug in desired behaviors. In fact, there is nothing that specifically makes Geronimo a J2EE application server if you haven't plugged in the components that define the behaviors from J2EE. Geronimo is a loose federation of components called GBeans, which are registered with the base kernel as they are deployed, and define the capabilities of the application server. The GBeans, in turn, may have dependencies on one another. For example, a persistence framework bean may rely on some facilities defined by a file management bean. Figure 1 shows this conceptual definition of Geronimo.


Figure 1. The architecture of Geronimo
architecture diagram

The arrows in Figure 1 imply references to other beans. Everything in Geronimo is a GBean: containers, connectors, adapters, applications, and so on. Even the applications you create and deploy are converted into GBeans when they are deployed in Geronimo. Geronimo's use of GBean components results in the following:

  • All parts of the container (whether core J2EE behavior or your applications) behave consistently.
  • It's easy to extend Geronimo by deploying new GBeans.
  • You can control the dependencies between components, because they all act the same.
  • GBeans contain state within the container (either persistent, via serialized GBeans, or nonpersistent).
  • GBeans contain logic that defines how they respond to events.

Ultimately, defining Geronimo in terms of simple interactions between simple components makes the container both more robust and easy to modify. To understand Geronimo, you must understand GBeans.



Back to top


GBeans

GBean components adhere to a particular life cycle as the kernel creates them, performs DI on them, calls their methods, and sends events to them. The items of interest in this article are the creation and destruction life cycle events and DI.

GBean life cycle

To register themselves as interested in participating in the normal life cycle operations, GBeans implement the GBeanLifecycle interface. This interface defines a contract with three methods, as shown in Listing 1.


Listing 1. The GBeanLifecycle methods
public class Customer implements GBeanLifecycle {
    public void doStart() throws Exception {

    }

    public void doStop() throws Exception {

    }

    public void doFail() {

    }
}

As you can discern from their names, the methods in Listing 1 are callback methods that the container calls at the life cycle stages for your GBean. These methods allow you to plug your GBean in to the kernel and receive notifications of interesting events.

GBean information

To use your GBean, the kernel must be able to ask it what it can do. This information resides in a BeanInfo class, which is typically provided by your bean when the bean's class is loaded via a static initializer. Listing 2 shows a typical static initializer.


Listing 2. The static initializer for GBean initialization
private static final GBeanInfo GBEAN_INFO;
    static {
        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(
                Customer.class.getName(),
                        Customer.class);

        // attributes
        infoFactory.addAttribute("name", String.class, true);
        infoFactory.addAttribute("salary", double.class, true);

        infoFactory.addOperation("setName",
                new Class[]{String.class});
        infoFactory.addOperation("getName");
        infoFactory.addOperation("setSalary", 
                new Class[] {double.class});
        infoFactory.addOperation("getSalary");

        GBEAN_INFO = infoFactory.getBeanInfo();
    }

    public static GBeanInfo getGBeanInfo() {
        return GBEAN_INFO;
    }

The static initializer in Listing 2 shows the registration process for a Customer class that contains two properties: name and salary. The infoFactory class has methods that allow your class to register interesting attributes and operations. Notice that you're never required to register all the public properties or methods of the class -- you can choose which ones you want to expose to the container. The class includes a static method, getGBeanInfo(), to return the GBean information created in the static initializer.

The initialization code in Listing 2 is part of a class that uses setter injection for DI. In the first article of this series, I defined setter injection as the container (in this case, Geronimo) calling a setXXX() method on your code to inject some dependent property value or class. To facilitate this setter injection, I've registered set methods for both my properties. Listing 3 shows the complete listing for the Customer class.


Listing 3. The setter-injectable Customer class
package com.nealford.articles.di_geronimo;

import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Customer implements GBeanLifecycle {
    private static Log log = LogFactory.getLog(Customer.class);
    private String name;
    private double salary;

    private static final GBeanInfo GBEAN_INFO;
    static {
        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(
                Customer.class.getName(),
                        Customer.class);

        // attributes
        infoFactory.addAttribute("name", String.class, true);
        infoFactory.addAttribute("salary", double.class, true);

        infoFactory.addOperation("setName",
                new Class[]{String.class});
        infoFactory.addOperation("getName");
        infoFactory.addOperation("setSalary", 
                new Class[] {double.class});
        infoFactory.addOperation("getSalary");

        GBEAN_INFO = infoFactory.getBeanInfo();
    }

    public void doStart() throws Exception {
        log.info("Starting Customer GBean");
    }

    public void doStop() throws Exception {
        log.info("Stopping Customer GBean");
    }

    public void doFail() {
        log.info("customer GBean failure!");
    }

    public static GBeanInfo getGBeanInfo() {
        return GBEAN_INFO;
    }

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(final double salary) {
        this.salary = salary;
    }

}


Starting and stopping GBeans

One more life cycle topic deserves attention before moving on to more DI: starting and stopping beans. If you want to load a GBean programmatically, you can. Listing 4 shows how to programmatically load a GBean whose name is defined via a Java Management Extension (JMX) interface.


Listing 4. Manually handling GBean's life cycle
GBeanMBean gmb = new GBeanMBean(Customer.getGBeanInfo());
gmb.setAttribute("name","Homer");
gmb.setAttribute("salary", 2500.00);
ObjectName myGbeanName = ObjectName.newInstance(
        "Geronimo.my:Customer=customer1");
kernel.loadGBean(myGbeanName, gmb);
kernel.startGBean(myGbeanName);

//do some work with Customers

kernel.stopGBean(myGbeanName);
kernel.unloadGBean(myGbeanName);

Of course, you don't have to do this for most GBeans you create -- the container loads them for you based on the deployment plan you provide. As you can see, nothing prevents you from loading, starting, and stopping your own GBean references in your code.

GBean states

GBeans, like other object references, hold state information in the form of either attributes or references to other GBeans. An attribute for a GBean is a value held by one of the properties of the GBean object. In other words, an attribute corresponds to a combination of an accessor/mutator pair of methods in the GBean class (for example, getter and setter). A reference is like an object reference in a normal JavaBean, only in this case it explicitly refers to another GBean. References are covered in more detail later.

Attributes are either persistent or nonpersistent. Persistent attributes retain their values between instances of the GBean. They are stored permanently by serializing the GBean or by another mechanism, such as storing them in a database. Nonpersistent attributes retain their value during the life of the GBean instance and disappear when the instance goes away.

Magic attributes

The Geronimo architecture defines special types of attributes called magic attributes. The values loaded for magic attributes are dependent on the environment in which the GBean is loaded. For example, the magic attribute kernel refers to the kernel, which is automatically injected into the class by the framework. Similarly, the ClassLoader attributes inject the current class loader, and the ObjectName attributes inject the current name into the GBean. Listing 4 shows the use of the kernel attribute that is automatically injected into the class that defines the life cycle code. A complete list of all magic attributes resides in the GBeanMBean class in the kernel. Magic attributes are not persistent, because they are bound with the startup environment.



Back to top


DI in GBeans

The customer class shown in Listing 3 is a good example of a class that uses setter injection. Next, you see an example of constructor injection and how Geronimo's deployment plans define how the container injects code. As explained in the first article of this series, constructor injection implies that the framework supplies constructor parameters required by the class upon instantiation.

Single references

Many proponents of DI prefer constructor injection, because it means that the injected class never exists in a state where its dependencies aren't present. Listing 5 shows a GBean that's defined to use constructor injection to provide a Customer reference.


Listing 5. The constructor-injectable Human Resources class
package com.nealford.articles.di_geronimo;

import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class HumanResources implements GBeanLifecycle {
    private static Log log = LogFactory.getLog(HumanResources.class);
    private Customer customer1;
    private static final GBeanInfo GBEAN_INFO;

    static {
        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(
                "HumanResources",
                 HumanResources.class);

        // attributes
        infoFactory.addReference("Customer", Customer.class);

        // operations
        infoFactory.setConstructor(new String[]{"Customer1"});
        GBEAN_INFO = infoFactory.getBeanInfo();
    }

    public HumanResources(Customer customer1) {
        this.customer1 = customer1;
    }


    public void doStart() throws Exception {
        log.info("Starting HR GBean");
    }

    public void doStop() throws Exception {
        log.info("Stopping HR GBean");
    }

    public void doFail() {
        log.info("HR GBean failure!");
    }
}


The GBean in Listing 5 looks similar in format to the Customer GBean defined in Listing 3. However, notice that this class does not have any get or set methods. Instead, the lone constructor is designed to accept a Customer object. Also, in the attributes section, I define a reference to the Customer class. The reference established in Listing 5 is an example of the references between GBeans represented by the arrows in Figure 1.

The creator of this GBean (that is, whoever fires the constructor on this class) must provide a reference to a Customer object. In most cases, the constructing entity is the Geronimo container itself. The next section shows you how the constructing entity knows to provide a customer and which customer to provide.

Deployment plans

Information about references and dependencies in Geronimo resides in deployment plans. Deployment plans are XML documents that define which GBeans are constructed and resolve their dependencies. The deployment plan for both the Customer and Human Resources classes appears in Listing 6.


Listing 6. Deployment plan
<?xml version="1.0" encoding="UTF-8"?>
<configuration
    xmlns="http://geronimo.apache.org/xml/ns/deployment"
    configId="test/HRPlan">
    
    
    <gbean name="test:Customer=customer1" class="Customer">
        <attribute name="name">Homer</attribute>
        <attribute name="salary">2500.00</attribute>
        
    </gbean>
    
    <gbean name="test:HR=HumanResources" class="HumanResources">
        <reference name="customer1">
            test:Customer=customer1
        </reference> 
        </gbean>
</configuration>

The deployment plan defines both types of GBeans. For the Customer GBean, it defines values to be injected into the attributes of the Customer instance. For the HumanResources class, it creates the reference Geronimo needs to inject the Customer instance into the HumanResources class as it is instantiated.

This example is clearly a simple one -- two GBeans, each using a different type of DI. Frequently, setting up one-to-one mappings between dependencies is more complex than this. For example, what if there are a variety of different Customer subclasses, and the environment needs to decide which one to inject in a given situation?

Reference patterns

Geronimo uses reference patterns to handle multiple dependency situations. Using a reference pattern, you can specify a family of legal dependency types that can be injected. You create reference patterns using wildcards in the name of the class to inject, and they may appear either in code or in deployment plans. The following code shows how to create a reference that allows any customer subclass to be injected:

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


Back to top


Summary

This series of articles demonstrates what a powerful code reuse mechanism DI is. Geronimo is the first J2EE server written entirely as a DI container. This allows a tremendous level of flexibility in building components designed for Geronimo. DI is a code reuse technique that has existed for years, but only now are programmers starting to reap the real benefits of the extremely loose coupling that it provides.



Resources

Learn

Get products and technologies

Discuss


About the author

Neal Ford

Neal Ford is an application architect at ThoughtWorks, a global IT consultancy with an exclusive focus on end-to-end software development and delivery. He is also the designer and developer of applications, instructional materials, magazine articles, courseware, video/DVD presentations, and author of the books Developing with Delphi: Object-Oriented Techniques, JBuilder 3 Unleashed, and Art of Java Web Development. His primary consulting focus is the building of large-scale enterprise applications. He is also an internationally acclaimed speaker, having spoken at numerous developers conferences worldwide. Check out his Web site at http://www.nealford.com. He welcomes feedback and can be reached at nford@thoughtworks.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top