Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Include Page
apache-felix-ipojo-header
apache-felix-ipojo-header

HTML

<div class="content">

The iPOJO Snack Bar

This tutorial presents illustrates some iPOJO features and shows:

...

advanced features of iPOJO

Div
classtoc
Table of Contents
maxLevel4
minLevel2

...

Context

This tutorial is based on a very simple application; customers are using a vendor service to buy hot dog or pop corn according to the availability of appropriate providers. Both of the vendors implement (and provide) the vendor service. The hot dog vendor depends on two others services to get the ingredients (buns and wiener). To sell pop corn, the pop corn vendor requires having enough corn in stock.

...

The tutorial archive is available here. This archive contains both the source code and a pre-configured version of Felix. First, unzip the archive. Then, launch 'ant' to compile the bundles composing this tutorial. Once compiled, you can launch Felix and start the tutorial. To launch, Felix launch the following command from the felix-1.0.3 directory:

Code Block
java -jar bin/felix.jar

...

The sources of this project are inside the _ vendor.buns-and-wieners_ directory.
The hot dog vendor requires at the same time the bun service and the wiener service. In our application these services are provided by the same component. This component can be implemented as follows (src\org\apache\felix\ipojo\example\vendor\provider\BunWienerProvider.java):

...

This class just implements the two service interfaces. Its descriptor (contained in the metadata.xml file) is:

Code Block
xml
xml
<ipojo>
<component 
    classname="org.apache.felix.ipojo.example.vendor.provider.BunWienerProvider" 
    name="buns_and_wieners" factorypublic="false">
	<provides/>
</component>

<instance component="buns_and_wieners"/>
</ipojo>

In the descriptor, we declare a component type for this vendor which contains the implementation class. The "classname" attribute contains the qualified name of the component implementation. The "name" attribute is the component type name. It is only used to refer to this type.

The "factorypublic=false" attribute disables factory exposition. A component type publishing a factory provides a way to create instance of this type from outside this descriptor. In our case, we want to guarantee that only one instance (singleton) can be created, so we disable the factory mechanism.

...

Code Block
java
java
public class HotDogVendor implements Vendor {
    
    /**
     * Bun provider (required service).
     */
    private Bun bunProvider;
    
    /**
     * Wiener provider (required service). 
     */
    private Wiener wienerProvider;
    
    /**
     * Sell method.
     * To provide an hotdog, the vendor consume a bun and a wiener.
     * This method is synchronized to avoid serving to client 
     * at the same time.
     * @return a hotdog.
     * @see org.apache.felix.ipojo.example.vendor.service.Vendor#sell()
     */
    public synchronized Product sell() {
        bunProvider.getBun();
        wienerProvider.getWiener();
        return new HotDog();
    }

...

Code Block
xml
xml
<ipojo>
<component 
   classname="org.apache.felix.ipojo.example.vendor.hotdog.HotDogVendor"
   name="HD" factorypublic="false">
	<provides/>
	<requires field="bunProvider"/>
	<requires field="wienerProvider"/>
</component>

<instance component="HD"/>
</ipojo>

...

Code Block
xml
xml
<ipojo>
<component 
   classname="org.apache.felix.ipojo.example.vendor.hotdog.HotDogVendor" 
   name="HD" factorypublic="false">
	<provides>
		<property name="product" type="string" value="hotdog"/>
	</provides>
	<requires field="bunProvider"/>
	<requires field="wienerProvider"/>
</component>

<instance component="HD"/>
</ipojo>

...

Code Block
xml
xml
<ipojo>
<component 
   classname="org.apache.felix.ipojo.example.vendor.provider.BunProvider" 
   name="buns_and_wieners" factorypublic="false">
	<provides>
		<property name="buns" field="bunStock" value="10"/>
		<property name="wieners" field="wienerStock" value="10"/>
	</provides>
</component>

<instance component="buns_and_wieners"/>
</ipojo>

...

Code Block
xml
xml
<ipojo>
<component 
   classname="org.apache.felix.ipojo.example.vendor.provider.BunProvider"
   name="buns_and_wieners" factorypublic="false">
	<provides>
		<property name="buns" field="bunStock" value="10"/>
		<property name="wieners" field="wienerStock" value="10"/>
	</provides>
</component>

<instance component="buns_and_wieners">
	<property name="buns" value="9"/>
	<property name="wieners" value="8"/>
</instance>
</ipojo>

The previous metadata shows how to push a configuration in instance declarations. The instance declaration contains two property elements containing the name of the value of the property. Instance configuration override component type initial value. If a property does not have an initial value, the instance must provide a value for every unvalued property.

...

Properties are optional by default ; that's means that they do not need to receive a value. In this case, default values are the same as the Java default fields values (boolean : false, int : 0, double : 0.0d, ...). You can specify that a property must receive a default value from either the component type description or the instance configuration by setting the mandatory attribute to 'true'.

Using filter in service requirements

Now that bun and wiener providers publish their remaining stock, the hot dog provider can look for a bun service and a wiener service with a non empty stock. To achieve this, we must describe an LDAP filter in the service requirement description. The following XML snipped shows this metadata:

Code Block
xml
xml
<ipojo>
<component 
   classname="org.apache.felix.ipojo.example.vendor.hotdog.HotDogVendor"
   name="HD" factorypublic="false">
	<provides>
		<property name="product" type="string" value="hotdog"/>
	</provides>
	<requires field="bunProvider" filter="(buns>=1)"/>
	<requires field="wienerProvider" filter="(wieners>=1)"/>
</component>

<instance component="HD"/>
</ipojo>

When a provider does no more matches with the LDAP filter, the provider is no more used, and another (matching with the filter) is tracked. If no provider fulfilling the constraint is found, the instance becomes invalid and waits a matching provider.

Note: when an instance becomes invalid, all its provided services are withdrawn from the service registry.

Immediate component instance

Now that we get the hot dog provider, we are going to implement customers. Customers are implemented in the _ vendor.customer _ project). A customer simply looks for a vendor service and buys a product:

...


public class Customer {
    
    private VendorService vendor;
    
    private String name;
    
    public Customer() {
        System.out.println("Customer " + name + " bought " +  vendor.sell() + " from " + vendor.getName());
    }

HTML

<div class="box">
	<div class="box-blue-header">
	<div class="box-blue-title">
		<img src="http://people.apache.org/~clement/ipojo/site/information.gif"> <b>Instance invalidation and services</b>
	</div>
	</div>
	<div class="box-blue-content">
When an instance becomes invalid, all its provided services are withdrawn from the service registry. So, this instance is no more _accessible_ from the service registry.
	</div>
	<div class="box-blue-footer"></div>
</div>

Immediate component instance

Now that we get the hot dog provider, we are going to implement customers. Customers are implemented in the vendor.customer project). A customer simply looks for a vendor service and buys a product:

Code Block
java
java

public class Customer {
    
    private VendorService vendor;
    
    private String name;
    
    public Customer() {
        System.out.println("Customer " + name + " bought " 
           +  vendor.sell() + " from " + vendor.getName());
    }

The previous code shows a possible implementation of a customer. However, the "sell" method is called in a constructor, The previous code shows a possible implementation of a customer. However, the "sell" method is called in a constructor, and the constructor can only be called only if an object of the class is created. With iPOJO there are two different way to "activate" an instance as soon as it becomes valid.
The first one uses the lifecycle callback (described in the previous tutorial). The second one is by declaring the component as an immediate component. An immediate component instance creates an object of its implementation as soon as it becomes valid.

Code Block
xml
xml
<ipojo>
<component 
    classname="org.apache.felix.ipojo.example.vendor.customer.Customer" factory
    name="customer" immediate="true">
	<requires field="vendor"/>
	<properties>
		<property field="name"/>
	</properties>
</component>
</ipojo>

To declare a component immediate, just add "immediate=true" in the component descriptor. Then as soon as the vendor service is available, the object is created. Moreover, this type declares a property (to give a name to the customers). This property is not a service property, but just an internal property. As for service properties, the name field will be injected by a value necessary given during the instance creation (i.e. contained inside the instance configuration).

By default, all all components that do not provide any service are immediate. Other components create call their constructors when they are used for the first time.

HTML

<div class="box">
	<div class="box-blue-header">
	<div class="box-blue-title">
		<img src="http://people.apache.org/~clement/ipojo/site/information.gif"> <b>Difference between 'validate' and 'immediate'</b>
	</div>
	</div>
	<div class="box-blue-content">
There is a difference between immediate components and components with a 'validate' lifecycle callback. Indeed, the callback is call at each time the instance becomes valid and calls the constructor only if no object already exists. On the other side, the immediate component's constructor is call only once.
        </div>
	<div class="box-blue-footer"></div>
</div>
Note: there is a difference between immediate components and components with a 'validate' lifecycle callback. Indeed, the callback is call at each time the instance becomes valid and calls the constructor only if no object already exists. On the other side, the immediate component's constructor is call only once.

Creating instances from an external component type

In the previous section we have declared a customer component type, which does not have the "factorypublic=false" attribute. This feature allows separate deployment from instance creation. Moreover, we didn't declare instances in the descriptor.
Another metadata file can be used to declare instances from the customer type, this descriptor being contained in another bundle. The following descriptor creates 10 customer instances (look at the vendor.customer.creator\metadata.xml file):

...

Compile the bundles, by launching ant at the root of the tutorial. Then launch Felix is indicated above.
Once started (write any profile name), launch the following commands

Code Block
none
none
start file:../vendor.services/output/vendor.services.jar
start file:../vendor.buns-and-wieners/output/vendor.buns-and-wieners.jar
start file:../vendor.hotdog/output/vendor.hotdog.jar
start file:../vendor.customer/output/vendor.customer.jar
start file:../vendor.customer.creator/output/vendor.customer.creator.jar

Something like this should appear:

Code Block
none
none
Customer customer-1 bought Hotdog from Fenway Park
Customer customer-2 bought Hotdog from Fenway Park
Customer customer-3 bought Hotdog from Fenway Park
Customer customer-4 bought Hotdog from Fenway Park
Customer customer-5 bought Hotdog from Fenway Park
Customer customer-6 bought Hotdog from Fenway Park
Customer customer-7 bought Hotdog from Fenway Park
Customer customer-8 bought Hotdog from Fenway Park

Only 8 customers can buy a hotdoghot-dog, as the stock of wieners and buns can't supply more hotdoghot-dog. The remainder of this tutorial will try to solve the problem of these two hungry customers.

...

Sometimes you want to invalidate your instance in the code (for example: to unregister a service). That's possible with the lifecycle controller handler.
Let's take the popcorn vendor with a corn stock from the _ vendor.popcorn_ project. Each time it sells some popcorn, its stock is decreased. When the stock reaches 0, it cannot sell popcorns any more (so the vendor service needs to be withdrawn).

The following implementation (rcsrc\org\apache\felix\ipojo\example\vendor\popcorn\PopCornVendor.java) uses a field to control the lifecycle.

Code Block
java
java
/**
     * The corn stock.
     */
    private int m_corn_stock;
    
    /**
     * Lifecycle controller.
     * If set to false, the instance becomes invalid. 
     */
    private boolean m_can_sell = true;

    /**
     * The sell method.
     * To provide popcorn, the vendor needs to decrease its corn stock level.
     * This method is synchronized to avoid to client being serve at 
     * the same time. 
     * @return
     * @see org.apache.felix.ipojo.example.vendor.service.Vendor#sell()
     */




    public synchronized Product sell() {
        m_corn_stock--;
        if (m_corn_stock == 0 && m_can_sell) { // Last pop corn
            m_can_sell = false;
            System.out.println("Stop selling popcorn 
                  ... Run out of stock");
            return new PopCorn();
        } else if (m_corn_stock > 0) { // Normal case
            return new PopCorn();
        } else { // Cannot serve.
            return PopCorn.NO_MORE_POPCORN;
        }
    }

...

Code Block
xml
xml
<ipojo>
<component 
    classname="org.apache.felix.ipojo.example.vendor.popcorn.PopCornVendor"
    name="popcorn" factorypublic="false" architecture="true">
	<provides/>
	<controller field="m_can_sell"/>
</component>

<instance component="popcorn"/>
</ipojo>

The instance can be re-validated by setting the field to true.
So, no deploy the pop corn vendor..

code
Code Block
none
none
-> start file:../vendor.popcorn/output/vendor.popcorn.jar
Customer customer-10 bought popcorn from D & P
Customer customer-9 bought popcorn from D & P

Our two last customers are no more hungry. However, new customers arrives, we have the following situation:

Code Block
none
none
-> update 10
Customer customer-1 bought popcorn from D & P
Customer customer-2 bought popcorn from D & P
Stop selling popcorn ... Run out of stock
Customer customer-3 bought popcorn from D & P

...

Code Block
java
java
/**
     * A transporter refills the stock of corn.
     * This method is synchronized to avoid to client being served 
     * during the update.
     * @param newStock : the stock of corn to add to the current stock.
     */
    public synchronized void refillStock(int newStock) {
        m_corn_stock += newStock;
        System.out.println("Refill the stock : " + m_corn_stock);
        if (m_corn_stock > 0) {
            m_can_sell = true;
        }
    }

...

Code Block
xml
xml
<ipojo>
<component 
    classname="org.apache.felix.ipojo.example.vendor.popcorn.PopCornVendor" 
    name="popcorn" architecture="true">
	<provides/>
	<controller field="m_can_sell"/>
	<properties>
		<property name="stock" method="refillStock" value="5"/>
	</properties>
</component>

<instance component="popcorn" name="SuperPopCorn">
	<property name="managed.service.pid" value="Super.PopCorn.Stock"/>
</instance>
</ipojo>

We add two different things. First we add a "stock" property attached to the refillStock method. When this instance is configured or reconfigured, this method is called to push the new stock value. Then we add the _managed.service.pid" property inside the instance creation. This property will be used by the Configuration Admin to attach configuration to instances. The property value must be unique.
So now, our popcorn vendor can be reconfigured dynamically to get increments its corn stock.
However, we need to create something refilling the stock ... a corn transporter !
Inside the vendor.corn.transporter project, we have a component dealing with the ConfigurationAdmin to push the new pop corn vendor configuration.
Thie The implementation is contained in the _ src\org\apache\felix\ipojo\example\vendor\corn\transporter\CornTransporter.java_ file.

Code Block
java
java
public class CornTransporter {
    
    private ConfigurationAdmin m_configAdmin;
    
    
    /**
     * Reconfigure the popcorn vendor with the configuration admin. 
     */
    public void refillStock() {
        try {
            // Retrieve or Create the instance configuration
            // from the configuration admin
            Configuration configuration = 
                 m_configAdmin.getConfiguration("Super.PopCorn.Stock",", 
                 "file:../vendor.popcorn/output/vendor.popcorn.jar");
            configuration.setBundleLocation(
                 "file:../vendor.popcorn/output/vendor.popcorn.jar");
            Properties props = new Properties();
            props.put("stock", new Integer(15)); // Delivered corn
 Delivered corn
            configuration.update(props);
            System.out.println("Update the configuration of " 
                  + configuration.updategetPid(props);
 + "(" 
         System.out.println("Update the configuration of " + configuration.getPid() + "(" + configuration.getBundleLocation() + ")");
            configuration.delete();
        } catch (IOException e) {
            e.printStackTrace();
        } //We indicates the set ManagedService PID
    }
}

Create a new configuration from the configuration admin and configure this configuration to add corn. Then, we update this configuration. This will reconfigured our popcorn vendor. More information on the Configuration Admin is available in the OSGi R4 Compendium.
So, now if we deploy this bundle, we will provide enough corn to feed all the customers:

Code Block
none
none
-> start file:../vendor.corn.transporter/output/vendor.corn.transporter.jar
Update configuration of Super.PopCorn.Stock(
       file:../vendor.popcorn/output/vendor.popcorn.jar)
Refill the stock : 5
Customer customer-10 bought popcorn from D & P
Customer customer-9 bought popcorn from D & P
Customer customer-8 bought popcorn from D & P
Customer customer-7 bought popcorn from D & P
Customer customer-6 bought popcorn from D & P
Customer customer-5 bought popcorn from D & P
Customer customer-4 bought popcorn from D & P

That's it!

Conclusion

This small tutorial has presented some of of the iPOJO features. Subscribe to the Felix users mailing list by sending a message to users-subscribe@felix.apache.org; after subscribing, email questions or feedback to users@felix.apache.org.

Include Page
apache-felix-ipojo-footer
apache-felix-ipojo-footer