Managing life cycles

This page describes the life cycle of Petals components and service-units.
Understanding it is essential when developing a Petals component.

Contributors
No contributors found for: authors on selected page(s)

Components life cycle

Every artifact in Petals has a life cycle.
Components do not break the rule.

The diagram below illustrates the different states of a component.

(Illustration taken from the version 1.0 of the JBI specifications)

Component specific parameters

As explained previously in this document, a component has a JBI descriptor which specifies parameters.
Among these parameters, there are standard parameters (from in the JBI specification), CDK parameters (explained farther) and component specific parameters.

Component specific parameters are parameters which configure your component behavior.
They can be used when your component is started, when it is running, or more generally, at any stage of its life cycle.
To define a new component parameter, simply add a new element in your jbi.xml file.


The example below defines an new element, called executor-size.
Notice that this element is associated to the component name space.

All the component parameters must be associated to the component name space.
To see how to retrieve this parameter in your component, check the sample code which is given below.

A life cycle to customize

When you created your component with Maven, you got a class named ServiceEngine or BindingComponent.
This class is generated by the Maven plug-in for commodity reasons. It allows you to find it easily.

The effective component class is the one specified in the component JBI descriptor (src/main/jbi/jbi.xml), in the component-class-name element.
If you have nothing to do in this class, you can delete it and set a default component class in your jbi.xml:

  • org.ow2.petals.component.framework.se.DefaultServiceEngine
  • org.ow2.petals.component.framework.bc.DefaultBindingComponent

The component class manages the component life cycle and the service-units.
Notice that unlike message listeners, there is only one component class instance per component name. The pool of message listeners is managed by the component instance.

If you have specific actions to perform on key events in the component life cycle, you need to have a custom component class.


protected void doInit() throws JBIException {}

Is called when the component is deployed (from the shutdown to the stopped state).


protected void doStart() throws JBIException {}

Is called when the component is started.


protected void doStop() throws JBIException {}

Is called when the component is stopped.


protected void doShutdown() throws JBIException {}

Is called when the component is shutdown.


Here is an example which creates an executor service when our component is deployed.
When the component stops, the executor stops accepting new threads.
When the component is shutdown, threads which were still processed are stopped.

public class ServiceEngine extends AbstractServiceEngine {

    private static int DEFAULT_POOL_SIZE = 5;
    private ExecutorService executor;


    /**
     * Initializes the executor service from the component configuration.
     * @see org.ow2.petals.component.framework.AbstractComponent#doInit()
     */
    @Override
    protected void doInit() throws JBIException {

        // Get the executor size from the component configuration
        int poolSize = DEFAULT_POOL_SIZE;
        for( Element elt : getComponentConfiguration().getAny()) {
            if( "executor-size".equals( elt.getLocalName().toLowerCase())) {

                try {
                    poolSize = Integer.valueOf( elt.getTextContent().trim());
                    if( poolSize < 1 )
                        poolSize = DEFAULT_POOL_SIZE;

                } catch( Exception e ) {
                    getLogger().info( ''Could not retrieve the pool size from the JBI descriptor.'' );
                }

                break;
            }
        }

        // Create the executor
        executor = Executors.newFixedThreadPool( poolSize );
    }

    /**
     * Does not accept any other thread.
     * @see org.ow2.petals.component.framework.AbstractComponent#doStop()
     */
    @Override
    protected void doStop() throws JBIException {
        executor.shutdown();
    }

    /**
     * Stops processing the threads.
     * @see org.ow2.petals.component.framework.AbstractComponent#doShutdown()
     */
    @Override
    protected void doShutdown() throws JBIException {
        executor.shutdownNow();
    }


    /**
     * Delegate method.
     * @param <T>
     * @param task
     * @return a future
     * @see java.util.concurrent.ExecutorService#submit(java.util.concurrent.Callable)
     */
    public <T> Future<T> submit( Callable<T> task ) {
        return executor.submit( task );
    }
}

Service-Units life cycle

The life-cycle of a service-unit is very similar to the life cycle of a component.
The diagram below illustrates the different states of a service-unit.

(Illustration taken from the version 1.0 of the JBI specifications)

The service-unit resources

Service-units are used by the component to create (emulate) services in the bus.
Remember that these services should have a WSDL interface, either provided by in the service-unit or by the component.

When a message is sent to one of these services, it is received by the component which then uses the associated resources to perform the treatment.


Service-unit configurations and resources can be used at several moments:

  • At deployment: the configuration influences the way resources are created.
  • At runtime: the configuration is used to determine how to process a message.
  • At any other life cycle moment, e.g. to determine what to do when the service stops.


If configurations are used beyond the deployment phase, they should be cached at deployment.
One way to do that is to build a structure maintained by the component.
Then, message listeners will access the component data and determine how to process a message depending on the end-point relative data.
This is achieved with a SU (service-unit) manager.

A SU manager is responsible for the life cycle of a service-unit.

The Service-Unit manager


In the example below, the SU manager overrides the deployment of a configuration to store parameters retrieved from the configuration jbi.xml file (the jbi.xml file in the service-unit).
To ease the comprehension, a sample service-unit jbi.xml file is also given.

<?xml version="1.0" encoding="UTF-8"?>
<jbi:jbi version="1.0"
	xmlns:jbi="http://java.sun.com/xml/ns/jbi"
	xmlns:petalsCDK="http://petals.ow2.org/components/extensions/version-5"
	xmlns:myComponent="http://petals.ow2.org/components/myComponent/version-1.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

	<jbi:services binding-component="false">
		<jbi:provides
			interface-name="genNs:myServiceItf"
			service-name="genNs:myServiceName"
			endpoint-name="myServiceEndpoint"
			xmlns:genNs="http://ebmwebsourcing.com/myService">

			<!-- CDK parameters -->
			<petalsCDK:wsdl>myWsdl.wsdl</petalsCDK:wsdl>

			<!-- Component specific parameters -->
			<myComponent:param1>myParam1</myComponent:param1>
			<myComponent:param2>myParam2</myComponent:param2>
		</jbi:provides>
	</jbi:services>
</jbi:jbi>


In this example, the component specific parameters are two elements called param1 and param2.
This is how your component can get and store them for a later use.

public class SampleSeSUManager
extends ServiceEngineServiceUnitManager
implements ServiceUnitManager {

    /**
     * Constructor.
     * @param engine
     */
    public SampleSeSUManager( ServiceEngine engine ) {
        super( engine );
    }

    /**
     * Handles the deployment of a service-unit.
     * @see org.ow2.petals.component.framework.su.AbstractServiceUnitManager
     * #doDeploy(java.lang.String, java.lang.String, org.ow2.petals.component.framework.jbidescriptor.generated.Jbi)
     */
    @Override
    protected void doDeploy( String serviceUnitName, String suRootPath, Jbi jbi )
    throws PEtALSCDKException {

        // Check the JBI descriptor
        if( jbi == null
                || jbi.getServices() == null
                || jbi.getServices().getProvides() == null
                || jbi.getServices().getProvides().size() == 0 )
            throw new PEtALSCDKException(
                                   "Invalid JBI descriptor: the submitted one does not contain a 'provides' section." );

        if( jbi.getServices().getProvides().size() != 1 )
            throw new PEtALSCDKException(
                                    "Invalid JBI descriptor: the submitted one contains more than one 'provides' section." );

        Provides provides = jbi.getServices().getProvides().get( 0 );
        if( provides == null )
            throw new PEtALSCDKException( "Invalid JBI descriptor: the 'provides' section is invalid." );


        // Get the job parameters
        ServiceUnitDataHandler suDataHandler = getSUDataHandlerForProvides( provides );
        if( suDataHandler == null )
            throw new PEtALSCDKException(
                                   "Error while processing the JBI descriptor in the component. The SU data handler was null." );

        ConfigurationExtensions extensions = suDataHandler.getConfigurationExtensions( provides );
        if( extensions == null )
            throw new PEtALSCDKException(
                                    "Invalid JBI descriptor: the submitted one does not contain any component extension." );

         String param1 = extensions.get( "param1" );
         String param2 = extensions.get( "param2" );

         // Store the parameters in the component (example)
         String edptName = provides.getEndpointName();
         // ((MyServiceEngine) getComponent()).params1.put( edptName, param1 );
         // ((MyServiceEngine) getComponent()).params2.put( edptName, param2 );
    }
}


Let's now suppose your service-unit jbi.xml has several elements with the same name, that is to say a list of elements.
The component specific part in the service-unit would look like:

<!-- Component specific parameters -->
<myComponent:paramName>myValue1</myComponent:paramName>
<myComponent:paramName>myValue2</myComponent:paramName>
<myComponent:paramName>myValue3</myComponent:paramName>
<myComponent:param2>myParam2</myComponent:param2>


Here, there are two kinds of elements: paramName and param2.
To retrieve the list of elements, Petals has an unsophisticated (or inconvenient, you decide) way to proceed.
Indeed, when they are parsed and stored in the configuration, the list elements are renamed.
With this example, it would result in the following element list: [petalsesbsnapshot: paramName1, paramName2, paramName3, param2 ].

Thus, you would have to go through among all the map keys and check if they match the pattern

paramName(\\d)*


Eventually, we would have to make sure this SU manager will be used instead of the component default one.
This is achieved by changing one method in the component class.

    /*
     * (non-Javadoc)
     * @see org.ow2.petals.component.framework.se.AbstractServiceEngine
     * #createServiceUnitManager()
     */
    @Override
    protected AbstractServiceUnitManager createServiceUnitManager() {
        return new SampleSeSUManager( this );
    }

As a general principle, the component should store service-unit data in a Map, where the key is the end-point name, and the value an object embedding the service-unit data.
This map is populated by the SU Manager instance.

When a JBI listener receives an exchange, it gets the target end-point, retrieves the associated data through the component, and then decides of the processing.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.