Print
Lifecycle

Why Lifecycle?

Life cycle support is an important aspect of an IOC container because objects contained in the container may sometimes demand life-cycle transitions such as "start", "stop", "init", "dispose" etc. These transition phases should respect the dependency between objects so that an object is not "initialized" until all objects it depends on are already "initialized" and it is "disposed" before any object it depends on gets disposed.

Life evolves, not develops.

Yan's life cycle support is provided as an add-on package. There's no special facility in the core designed with lifecycle in mind. Yet, this life cycle support naturally evolved from using the general purpose combinators provided by the core. This means, you can always come up with your own life-cycle library if you don't opt to use or extend this life cycle package.

Lifecycle support in Yan does not require the components to be singleton. Any component can be managed by lifecycle manager.

Make them alive

Now let's discuss some details about the out-of-box life cycle package.

In order to manage lifecycle for components, a LifecycleManager needs to be created:

DefaultLifecycleManager man = new DefaultLifecycleManager();

Then, to define a life-cycle for a component, we need to create a Lifecycle object:

DefaultLifecycleManager.DefaultLifecycle life = man.newLifecycle()
  .initializer("init)
  .disposer("destroy")
  .starter("start")
  .stopper("stop");

The above code creates a Lifecycle object with 4 phases: init, dispose, start, stop. Each phase is associated with a method name so that init(), destroy(), start(), stop() are executed for each phase.

The Lifecycle object can then be associated with any Component that wishes to have such lifecycle defined. As a metaphore, any component can be given life:

Component automobile = Components.ctor(Automobile.class);
Component live_automobile =life.manage(automobile);

This live automobile along with many other live or non-live components can be registered to containers and instantiated:

Container yan = ...;
yan.registerComponent("auto", live_automobile);
yan.registerComponent(another_component);
...
Automobile auto = (Automobile)yan.getInstance("auto");

And when it comes to the time to initialize and start live components that support these phases, the LifecycleManager object can be used this way:

man.init();
man.start();

More than one lives

Because LifecycleManager is independent of Container, it is possible that components from different containers are managed by the same LifecycleManager object. It is also possible that the same component is managed by two different LifecycleManager objects, so that using different LifecycleManager object, we get different treatment of lifecycle for the same component.

For example:

DefaultLifecycleManager man1 = new DefaultLifecycleManager();
DefaultLifecycleManager man2 = new DefaultLifecycleManager();
Component c = ...;

c = man1.newLifecycle()
  .starter("start1")
  .stopper("stop1")
  .manage(c);

c = man2.newLifecycle()
  .starter("start2")
  .stopper("stop2")
  .manage(c);
...
container.registerComponent("key", c);
...
container.getInstance("key");
...

Then, we can use the object man1 to invoke the "start1" and "stop1" methods. We can also use object man2 to invoke the "start2" and "stop2" methods.

man1.start();  //start1() is called.
man2.start();  //start2() is called.
man2.stop();   //stop2() is called.
man1.stop();   //stop1() is called.

Advanced Lifecycle

The DefaultLifecycleManager is designed to make it handy for the several frequently used life cycle phases. If you want more sophisticated life-cycle, you can always use the LifecycleManager class directly for finer control.

For example, class PooledThread has such requirement:

  1. The stop phase requires an additional boolean parameter indicating if a "force stop" is to be performed so that the object will stop immediately without waiting for any pending process.
  2. We want to be able to print a message before the stop() method is called.

The DefaultLifecycleManager is not useful in this case, we use LifecycleManager directly:

LifecycleManager man = new LifecycleManager();
Lifecycle life = new Lifecycle();
life.put("stopper", new Procedure(){
    void invoke(Object self, Object[] args){
      PooledThread target = (PooledThread)self;
      Boolean force = (Boolean)args[0];
      System.out.println("closiing thead...");
      target.stop(force.booleanValue());
    }
  }, true);
Component pooled_thread = ...;
Component live_thread = man.withLifecycle(pooled_thread, life);
  • "stopper" is the key of the phase.
  • The 3rd parameter of Lifecycle.put() method is a boolean variable indicating whether the phase procedure allows re-entry. If not, the lifecycle manager will ignore the second time and subsequent calls to this method.
  • The Procedure interface needs to be implemented to tell lifecycle manager what to do for this phase.
  • pooled_thread is a component that creates PooledThread.
  • withLifecycle() associates a Component with a lifecycle managed by the LifecycleManager object.

The registration and instantiation of live_thread is exactly the same as we showed before.

Finally when we need to invoke the "stop" phase, we do:

man.filo(
  "stopper", new Object[]{Boolean.valueOf(true)},
  ExceptionHandlers.suppresser()
);
  • man.filo(...) calls a phase for every component instances managed by this LifecycleManager in the reverse order of creation so that dependents are "stopped" before dependees are.
  • "stopper" is the key of the phase, again.
  • Boolean.valueOf(true) is the additional parameter to pass to the phase.
  • ExceptionHandlers.suppresser() tells the life cycle manager to suppress any exception thrown out of the stopping process.

Similarly, we can run a non-force stop with exceptions printed out:

man.filo(
  "closer", new Object[]{Boolean.valueOf(false)},
  ExceptionHandlers.printer()
);

Created by benyu

On Tue Oct 25 05:25:38 CST 2005

Powered by Atlassian Confluence