|
Page Edited :
FELIX :
iPOJO Advanced Tutorial
iPOJO Advanced Tutorial has been edited by Clement Escoffier (Aug 27, 2007). Content:This tutorial presented some iPOJO features. It shows:
The sources of this tutorial is available here ContextThis tutorial uses a very simple application. Customers use a Vendor service to buy hot dog or pop corn according to the availability of these two providers. Both the pop corn vendor and the hot dog vendor implement (and provide) the vendor service. The hot dog vendor depends on two other services for getting ingredient. It depends on a bun service and a wiener service. Writing a component providing two servicesThe hot dog vendor requires at the same time the bun service and the wiener service. In our application these two services are provided by one component. This component can be implemented as following: public class BunWienerProvider implements BunService, WienerService { public void getBun() { System.out.println("Get a bun"); }
public void getWiener() {
System.out.println("Get a wiener");
}
}
This class just implements the two service interfaces. Then, the descriptor of this component is: <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.provider.BunWienerProvider" name="buns_and_wieners" factory="false"> <provides/> </component> <instance component="buns_and_wieners"/> </ipojo> First, we declare a component type for our component. The component type contains the implementation class. The attribute "classname" contains simply 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 "<provides/>" element means that the component provide services. IPOJO will manage automatically service publication and providing at runtime. If not describes, iPOJO publish all implemented interfaces by the implementation class. In our case, it will publish the BunService and WienerService interfaces. At runtime, the bundle containing this component will create an instance. This instance provides the BunService and the WienerService. Publishing a service propertyThe hot dog vendor just provides the Vendor service. To provide this service, it used a bun service and a wiener service. The following code snippet shows a very simple implementation of this component: public class HotDogVendor implements VendorService { private BunService bunProvider; private WienerService wienerProvider; public String getName() { return "The Best Hot Dogs"; } public String sell() { bunProvider.getBun(); wienerProvider.getWiener(); return "sell an hotdog"; } } The two fields are used to inject the two required services. At runtime, iPOJO injects automatically a BunService provider in the "bunProvider" field and a WienerService provider in the "wienerProvider" field. The implementation uses these field as other field (as illustrated in the sell method). <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.hotdog.HotDogVendor" name="HD" factory="false"> <provides/> <requires field="bunProvider"/> <requires field="wienerProvider"/> </component> <instance component="HD"/> </ipojo> The component type declares a provided service (the Vendor Service). Then, the component declares the two service dependencies. However, we would like to add a service property on the Vendor service describing the sale product (here hotdog). To achieve this, we just need to add a property element in the "provides" tags as following: <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.hotdog.HotDogVendor" name="HD" factory="false"> <provides> <property name="product" type="string" value="hotdog"/> </provides> <requires field="bunProvider"/> <requires field="wienerProvider"/> </component> <instance component="HD"/> </ipojo> IPOJO publishes the "product" property in the "vendor" service registration. This property has the "hotdog" value. Publishing a 'dynamic' propertyThe bun service and the wiener service can expose service properties too. These service properties describe the stock of the ingredient. At each time the service is used, the property is decreased. To achieve this, we modify the current implementation to add a field representing the property: public class BunProvider implements BunService, WienerService { private int bunStock; private int wienerStock; public void getBun() { bunStock = bunStock - 1; } public void getWiener() { wienerStock = wienerStock - 1; } } Then the component type metadata must describe this property : <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.provider.BunProvider" name="buns_and_wieners" factory="false"> <provides> <property name="buns" field="bunStock" value="10"/> <property name="wieners" field="wienerStock" va lue="10"/> </provides> </component> <instance component="buns_and_wieners"/> </ipojo> In the "provides" element, two properties are added. This property contains a "field" attribute aiming to attach the service property with a field of the implementation class. Then a default value is given. In the code, the property fields will obtain the initial value (10). Then at each time the fields are modified, the service property is updated (and the OSGi™ service registration is updated). Configuring instancesIn the previous example, the properties were configured in the component type description. It is possible to customized value in the instance declaration. Then, each instance can obtain different values. <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.provider.BunProvider" name="buns_and_wieners" factory="false"> <provides> <property name="buns" field="bunStock" value="10"/> <property name="wieners" field="wienerStock" va lue="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 this unvalued property. Using filter in service requirementNow that bun and wiener provider publishes its 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 simply must describe an LDAP filter in the service dependency description. The following xml snipped shows this metadata: <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.hotdog.HotDogVendor" name="HD" factory="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 used provider does no more matches with the LDAP filter, the provider is no more used, and another (matching with the filter) is looked. If no provider fulfilling the constraint is found, the instance becomes invalid and waits a matching provider. Note: when an instance becomes invalid, provided services are withdraw from the service registry. Immediate component instanceNow that we get the hot dog provider, we are going to implement customers. 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()); } The previous code shows a possible implementation of a customer. However, sell method is called in a constructor. The constructor is called only if an object of the class is created. With iPOJO there is two different way to "activate" an instance as soon it becomes valid. The first one use lifecycle callback (describe 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 it becomes valid. <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.customer.Customer" factory="customer" immediate="true"> <requires field="vendor"/> <properties> <property field="name"/> </properties> </component> <instance component="customer"> <property name="name" value="my_customer"/> </instance> </ipojo> To declare a component immediate, just write "immediate=true". Then as soon as the vendor service is available, it creates the object. Note: There is a difference beetween immedaite componenet and component with a 'validate' lifecycle callback. Indeed, the callback is call at each time the instance becomes valid. IT call the constructor only if no object already exist. On the other side, the immediate component called the constructor only once time. Creating instances from an external component typeIn the previous section we have declared a customer component type. This component type does not have the "factory=false" attribute. This allows another descriptor to use this type. This feature allows deploying separately implementation from instance creation. <ipojo> <instance component="customer"> <property name="name" value="customer-1"/> </instance> <instance component="customer"> <property name="name" value="customer-2"/> </instance> <instance component="customer"> <property name="name" value="customer-3"/> </instance> <instance component="customer"> <property name="name" value="customer-4"/> </instance> <instance component="customer"> <property name="name" value="customer-5"/> </instance> <instance component="customer"> <property name="name" value="customer-6"/> </instance> <instance component="customer"> <property name="name" value="customer-7"/> </instance> <instance component="customer"> <property name="name" value="customer-8"/> </instance> <instance component="customer"> <property name="name" value="customer-9"/> </instance> <instance component="customer"> <property name="name" value="customer-10"/> </instance> </ipojo> When deployed this bundle looks for the required factory. If not available it waits for it apparition. When this bundle is stopped, all instances are destroyed. Using the lifecycle controllerSometimes you want to invalidate your instance from your code (to unregister a service...). That's possible with the lifecycle controller handler. public class PopCornVendor implements VendorService { private int m_corn_stock = 5; private boolean m_can_sell = true; public String getName() { return "Eat my pop corn !"; } public String sell() { m_corn_stock = m_corn_stock - 1; if (m_corn_stock == 0 && m_can_sell) { m_can_sell = false; } return "popcorn"; } } When the field is set to false, the instance is invalidated (the vendor service is no more available). To configure the controller, just use following metadata : <ipojo> <component classname="org.apache.felix.ipojo.example.vendor.popcorn.PopCornVendor" name="popcorn" factory="false" architecture="true"> <provides/> <controller field="m_can_sell"/> </component> <instance component="popcorn"/> </ipojo> The instance can be re-validated if the field is set to true. ConclusionThis small tutorial has presented some iPOJO features. If you have comments or questions, do not hesitate to send me an email to [[EMAIL PROTECTED]]. |
Unsubscribe or edit your notifications preferences
