Writing about technology, traveling, politics and more

Friday, January 30, 2009

Developing a context client - Tutorial 9

This is the next post in the series of tutorials that introduces OSGi and the MUSIC Context System. The previous tutorial, described how to develop a context sensor plug-in, that monitors the memory use and periodically generates events containing data about the last measurements. This tutorial describes how to develop a context client that listens for changes to the memory context type.

Developing the Memory Viewer context client

As usual, a memory viewer is first a normal OSGi component bundle. We described how to create components and bind them with services in the second and the third tutorials. As such, to develop a context client we need to specify three parts:
  • Define its Manifest: The Manifest describes the properties of the bundle. As such it defines the imported and possibly exported packages and it also provides a pointer to the service descriptor.
  • Define its service descriptor XML file: In the case of a context client, the service descriptor is simply used to define the needed IContextAccess service (which it was introduced in this tutorial).
  • Define the code that implements the context client: The code of the context client is implemented in one or more classes. In principle, a context client implements the IContextListener interface which allows it to asynchronously access context data.
For example, the development of the memory context client results to the following parts:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Memory viewer client for the MUSIC Context System
Bundle-SymbolicName: MemoryViewer
Bundle-Version: 1.0.0
Bundle-ClassPath: .
Import-Package: org.istmusic.mw.context,
org.istmusic.mw.context.exceptions,
org.istmusic.mw.context.events,
org.istmusic.mw.context.ontologies,
org.istmusic.mw.context.model.api,
cy.ac.ucy.cs.osgi.tutorial7.memory_model
Service-Component: OSGI-INF/MemoryViewer.xml

As a typical OSGi component, its Manifest defines imported and exported packages and a pointer to the service descriptor XML file. In this case, the service descriptor is named "MemoryViewer" and is as follows:
<?xml version="1.0"?>

<component name="MemoryViewer" immediate="true">

<implementation class="cy.ac.ucy.cs.osgi.tutorial9.MemoryViewer"/>

<!-- Reference to the context access service -->
<reference name="context.access"
interface="org.istmusic.mw.context.IContextAccess"
cardinality="1..1"
bind="setContextAccessService"
unbind="unsetContextAccessService"
policy="dynamic"/>

</component>
The most interesting part of this service descriptor is that it defines a reference to the context access service. By referring back to the details of dynamically binding (declarative) services, you can see that this definition will result to the automatic binding of this client to the IContextAccess service, which is provided by the context middleware. Furthermore, the "1..1" cardinality indicates that in order to activate the client, exactly one binding to a single provider of the context access service must be made. When bound (or unboun), a corresponding method is invoked.

Finally, we are ready to present the code of the Memory Viewer context client:
package cy.ac.ucy.cs.osgi.tutorial9;

import org.istmusic.mw.context.IContextAccess;
import org.istmusic.mw.context.exceptions.ContextException;
import org.istmusic.mw.context.events.IContextListener;
import org.istmusic.mw.context.events.ContextChangedEvent;
import org.istmusic.mw.context.ontologies.DefaultMusicOntologyV0_1;
import org.istmusic.mw.context.model.api.IEntity;
import org.istmusic.mw.context.model.api.IScope;
import org.istmusic.mw.context.model.api.IContextElement;

import java.util.logging.Logger;

import cy.ac.ucy.cs.osgi.tutorial7.memory_model.MemoryContextElement;

public class MemoryViewer implements IContextListener
{
public static final IEntity ENTITY = DefaultMusicOntologyV0_1.ENTITY_DEVICE_THIS;
public static final IScope SCOPE = DefaultMusicOntologyV0_1.SCOPE_RESOURCE_MEMORY;

private final Logger logger = Logger.getLogger(MemoryViewer.class.getCanonicalName());

public MemoryViewer()
{
logger.info("MemoryViewer: constructed");
}

public void contextChanged(ContextChangedEvent event)
{
IContextElement [] contextElements = event.getContextDataset().getContextElements();
for(int i = 0; i < contextElements.length; i++)
{
IContextElement contextElement = contextElements[i];

MemoryContextElement memoryContextElement = (MemoryContextElement) contextElement;
logger.info("ContextViewer: Received -> " + memoryContextElement);
}
}

public void setContextAccessService(final IContextAccess contextAccess)
{
try
{
contextAccess.addContextListener(ENTITY, SCOPE, this);
}
catch (ContextException ce)
{
logger.throwing(MemoryViewer.class.getName(), "setContextAccessService", ce);
}
}

public void unsetContextAccessService(final IContextAccess contextAccess)
{
try
{
contextAccess.removeContextListener(ENTITY, SCOPE, this);
}
catch (ContextException ce)
{
logger.throwing(MemoryViewer.class.getName(), "unsetContextAccessService", ce);
}
}
}
The most interesting parts in this class are the handling of the binding (and unbinding) of the context access service (in bold) and the handling of the received events (also in bold). The "setContextAccess" method is used to register this context listener for notifications of events related to changes in the memory of this device. Symmetrically to it, the "unsetContextAccess" method is used to unregister from events of the same type.

At this point, we should note that the context types are defined via the Ontology, which guarantees semantic consistency. For this purpose the entity and the scope are defined by referencing to the appropriate concepts in the ontology, which correspond to the memory resource of this device.

While registered with the context access service, the memory client receives every notification which corresponds to the registered context type. These notifications are received at the "contextChanged" method (which is defined in the IContextListener interface). In the Memory Viewer client, we simply read each context element received, cast it to the MemoryContextElement type (defined in Tutorial 7) and then simply print it out on the console. A real, context-aware application would of course the received context data in a more interesting way (e.g., to adapt its behavior).

Packaging and deploying the Memory Viewer context client

Following a similar approach to when developing OSGi bundles (and context plug-ins), we package the resulting code and the artifact documents (i.e., the Manifest and the service descriptor) in a single JAR file. An example of this JAR file, including a copy of the source code and the "build.xml" ANT configuration file that was used to create it, is available here.

When deployed and started, the memory viewer produces the following output:
osgi> install file:MemoryViewer.jar
Bundle id is 10

osgi> refresh 10

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.2.R34x_v20080826-1230
1 ACTIVE org.eclipse.equinox.ds_1.0.0.v20080427-0830
2 ACTIVE org.eclipse.equinox.util_1.0.0.v20080414
3 ACTIVE org.eclipse.osgi.services_3.1.200.v20071203
4 ACTIVE RunnableProvider_1.0.0
5 ACTIVE RunnableConsumer_1.0.0
6 ACTIVE CLI_Client_1.0.0
7 ACTIVE music-context_0.2.1.0-SNAPSHOT
8 ACTIVE MemoryModel_1.0.0
9 ACTIVE MemorySensor_1.0.0
10 RESOLVED MemoryViewer_1.0.0

osgi> start 10

osgi> Jan 29, 2009 12:34:15 PM cy.ac.ucy.cs.osgi.tutorial9.MemoryViewer <init>
INFO: MemoryViewer: constructed

Jan 29, 2009 12:34:15 PM org.istmusic.mw.context.manager.ContextManager addTrigger
INFO: Adding trigger for context type: http://www.ist-music.eu/Ontology_v0_1.xml#concepts.entities.device|this/http://www.ist-music.eu/Ontology_v0_1.xml#concepts.scopes.resources.memory

Jan 29, 2009 12:34:15 PM cy.ac.ucy.cs.osgi.tutorial8.MemorySensor activate
INFO: c_activating: Memory plugin

Jan 29, 2009 12:34:16 PM cy.ac.ucy.cs.osgi.tutorial8.MemorySensor run
INFO: MemorySensor: firing event: (#concepts.entities.device|this, #concepts.scopes.resources.memory)->[availableMemory=2437856, memoryLoad=0.5291299940664558]

Jan 29, 2009 12:34:16 PM cy.ac.ucy.cs.osgi.tutorial9.MemoryViewer contextChanged
INFO: ContextViewer: Received -> (#concepts.entities.device|this, #concepts.scopes.resources.memory)->[availableMemory=2437856, memoryLoad=0.5291299940664558]

osgi> stop 10

Jan 29, 2009 12:34:29 PM cy.ac.ucy.cs.osgi.tutorial8.MemorySensor deactivate
INFO: c_deactivating: Memory plugin

osgi>
There are many interesting observations to be made here. First, when the memory viewer (bundle 10) is started, it causes a sequence of events:
  1. First, the constructor of the memory viewer bundle is invoked.
  2. Second, the context access service is bound, and the memory viewer registers for memory context types, as it is indicated by the info log of the context middleware (which adds a trigger for asynchronous notification).
  3. Third, the context middleware realizes that the needed context type is offered by the installed memory context plug-in (bundle 9) and it activates it. This is indicated by memory sensor itself which logs the corresponding action.
  4. Next, the memory context plug-in (bundle 9) starts producing (i.e., firing) context change events encoding MemoryContextElements. These are communicated to the context middleware.
  5. Finally, the memory viewer (bundle 10) receives the corresponding events and prints out to the console the value of the received MemoryContextElement.
Actually, steps 4 and 5 will continue to be triggered automatically, repeatedly producing the corresponding output, until you stop the memory viewer (bundle 10). In particular, as long as the memory context plug-in (bundle 9) is active, it will keep generating events with the predefined interval (5 seconds in this case). As a result, the memory viewer context client will receive a copy of the event from the context middleware and will display it in the output.

Finally, when you stop the memory viewer (bundle 10), it first unregisters from the context middleware, which in result causes it to deactivate the memory context plugin, returning it to the C_RESOLVED state. More details about the extended plug-in lifecycle were discussed in the previous tutorial and are also available in [1].

Homework 9.1: Develop a context client for the location context type and name it LocationViewer. The resulting component must register for location context types when active and unregister when not. The received context events must be printed out to the console. You can use the model defined in homework 7.1 and the LocationPlugin defined in homework 8.1.

Homework 9.2: Develop a graphical context client for the weather context type and name it WeatherViewer. The resulting component must register for location context types when active and unregister when not. The received context events must be displayed in a simple Graphical User Interface (GUI). You can use the model defined in homework 7.1 and the LocationPlugin defined in homework 8.1.

Hint: Use Java's swing to develop the GUI. In that case, do not forget to include all used packages (e.g., "javax.swing") into the imported packages definition of the bundle's Manifest. Also make the component visible when it is started and invisible when it is stopped, by implementing the "activate(ComponentContext)" and "deactivate(ComponentContext)" methods, as described in Tutorial 5.

[1]. Nearchos Paspallis, Romain Rouvoy, Paolo Barone, George A. Papadopoulos, Frank Eliassen, Alessandro Mamelli, A Pluggable and Reconfigurable Architecture for a Context-aware Enabling Middleware System, 10th International Symposium on Distributed Objects, Middleware, and Applications (DOA'08), Monterrey, Mexico, Nov 10 - 12, 2008, Springer Verlag LNCS 5331, pp. 553-570

Wednesday, January 28, 2009

Developing your first context sensor plug-in - Tutorial 8

This tutorial builds on the foundations defined in the previous one, which described the use of the MUSIC Context Model, in order to describe the process needed to develop a Context Plug-in. These tutorials are part of a series which aims to first introduce OSGi and declarative services and then the MUSIC Context System.

What are the Context Plug-ins?

In Tutorial 6, we introduced the MUSIC Context System architecture and argued that one of its main advantages is the fact that it allows to develop context producers and context consumers independently. Furthermore, as the sources and the sinks are independent, it is possible to reuse the same plug-ins (i.e., sources) to satisfy the needs of multiple context clients (i.e., sinks).

Furthermore, as the plug-ins define their provided (and potentially required) context types in a well-defined, Ontology-backed manner, it is quite possible that the same plug-ins are reused by multiple developers for the construction of various applications and even, sometimes, across various platforms. On the other hand, again as the context clients express their needs in the same well-defined, ontology-backed manner, it is quite possible that the same clients accept context input from various plug-ins, depending on the availability (and resource consumption profile) of the latter.

Finally, this architecture has the additional benefits of allowing automatic resolution of plug-ins (only those which have no needs for context input or those of which their input is provided are resolved)
A more scientific discussion of the nature of the context plug-ins, along with a description of the intelligent resolution and activation mechanism are described in [1].

So, the answer to the question of the header, in a few words, is the following: "Context plug-ins are custom OSGi bundles which are designed to produce context data, perhaps by combining lower-level, more elementary context types or simply by wrapping hardware or OS-based sensors."

Anatomy of a Context Plug-in

As discussed already, Context Plug-ins are first and foremost OSGi bundles. Furthermore, these bundles have the special characteristic that they are discoverable via Declarative Services and they also implement the IContextPlugin interface. Most importantly, this interface specifies two custom life-cycle operations, activate and deactivate, which are automatically controlled by the middleware.

In principle a context plug-in, like all other OSGi components adhering to the Declarative Services standard, consists of the following parts:
  1. A bundle Manifest descriptor, defining its name, the imported and exported Java packages of the bundle and finally a reference to the service descriptor XML file.
  2. The service descriptor XML file that specifies the service provided by the plug-in (fixed to IContextPlugin) and the properties characterizing the context types provided and required by the plug-in.
  3. The actual code implementing the Context Plug-in. In its simplest form, this code implements the IContextPlugin interface (possibly by extending one of the abstract implementations available). In practice, the code needs to respond to life-cycle commands as instructed by the middleware with the purpose of starting (and stopping) the generation of context data (encapsulated in context events).
This structure is illustrated in the following class diagram:

Structure of the memory context plug-in and utilization of core classes

In this class diagram, we illustrate the three parts comprising a typical context plug-in measuring the memory use in the device. Notably, this plug-in extends an AbstractContextPlugin class provided by the core context middleware, and which automates a portion of the required functionality. The exact implementation of the memory context plugin are discussed in more detail further on.

An important detail is the extended life-cycle of the context plug-in components. In particular, while active the context plug-ins can transition among three possible states:
  • C_INSTALLED: In this state, the plug-in is simply installed meaning that it has not been resolved. To be resolved, a context plug-in must either have no context types required or it must require context types provided by already resolved plug-ins.
  • C_RESOLVED: In this state, the context plug-in has resolved its dependencies and can be started at any time. The only reason for a plug-in to be resolved but not started is when there is no current need for its provided context type, in which case the plug-ins stays inactive to avoid unnecessary resource consumption.
  • C_ACTIVE: This is the third and final possible state for the plug-ins, which indicates that it is resolved and active. In this state, and only in this, the plug-ins are active and generate context events.
Notably, these states are triply nested in each other: C_INSTALLED contains C_RESOLVED which itself contains C_ACTIVE. This is also illustrated in the following state diagram:

Context Plug-ins: Extended OSGi Component Lifecycle

While transitioning into (or out of) the C_ACTIVE state, the activate (or deactivate) method of the corresponding plug-in is invoked. To illustrate all these concepts, we here describe the development of a memory context plug-in step-by-step.

Developing the Memory Context Plugin

Similar to developing a normal OSGi bundle component, the first two steps are to define the Manifest file and the service descriptor. In the case of the memory context plug-in, the Manifest is defined as follows:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Memory sensor plug-in for the MUSIC Context System
Bundle-SymbolicName: MemorySensor
Bundle-Version: 1.0.0
Bundle-ClassPath: .
Import-Package: org.istmusic.mw.context.plugins,
org.istmusic.mw.context.events,
org.istmusic.mw.context.model.api,
org.istmusic.mw.context.model.impl,
org.istmusic.mw.context.util.scheduler,
cy.ac.ucy.cs.osgi.tutorial7.memory_model
Service-Component: OSGI-INF/MemorySensor.xml


As you can see, this Manifest is identical to any other OSGi bundle-describing Manifest file. It does not export any packages at all, but it imports a few. Notably, the set of imported packages cannot be typically defined a priori. First, the actual sensor plugin must be coded, and then any packages, beyond the standard ones, which are needed in the code must be added to the "Import-Package" declaration. As you have probably noticed already, this sensor imports the memory_model package that was defined in the previous tutorial. The reason for this is that we will leverage that model for encoding the sensed context information. Finally, the Manifest file points to the service descriptor of the plugin (in this case the "MemorySensor.xml"), which is described in the following.

<?xml version="1.0"?>

<component name="MemorySensor" immediate="true">

<implementation class="cy.ac.ucy.cs.osgi.tutorial8.MemorySensor"/>

<service>
<provide interface="org.istmusic.mw.context.plugins.IContextPlugin"/>
</service>

<property name="PROVIDED_CONTEXT_TYPES" value="memory"/>
<property name="PROVIDED_CONTEXT_TYPE_ENTITY[memory]" value="#concepts.entities.device|this"/>
<property name="PROVIDED_CONTEXT_TYPE_SCOPE[memory]" value="#concepts.scopes.resources.memory"/>

</component>

Again, the service descriptor is similar to those used in the development of typical OSGi components, as it was described in the second tutorial. The "implementation" element defines the absolute path of the implementing class (which must be a sub-type of IContextPlugin). By default, all context plug-ins must export the IContextPlugin service, which is achieved with the "service" XML element as shown above. In addition to these however, context plug-ins also define a number of properties. These properties describe the provided and the required context types of the plug-in. In this example, only provided types are defined but in other cases (i.e., when developing a context reasoner plug-in), required context types can also be defined.

The format of the properties is as follows:
  • PROVIDED_CONTEXT_TYPES: This property contains a space-separated list of names denoting what context types are provided by the plug-in. For example, a possible assignment could be "p1 p2", where the "p1" and the "p2" types are defined as provided. For each provided type defined in this property, an entity and a scope must also be defined, using the following two parameters.
  • PROVIDED_CONTEXT_TYPE_ENTITY[p]: This property must be defined for each context type "p" that has been specified in the provided context types property. The value of this property should be a string corresponding to the actual value of the entity. For example, in the case of the "memory" context type, the entity is the device itself (i.e., "#concepts.entities.device|this").
  • PROVIDED_CONTEXT_TYPE_SCOPE[p]: Similar to the entity property, thus one must be defined for each context type "p" that has been specified in the provided context types property. The value of this property should be a string corresponding to the actual value of the scope. For example, in the case of the "memory" context type, the scope is the memory resource (i.e., "#concepts.scopes.resources.memory").
It should be noted that besides these three properties, the following three are also possible:
  • REQUIRED_CONTEXT_TYPES: This property contains a space-separated list of names denoting what context types are required by the plug-in. For example, a possible assignment could be "r1 r2", where the "r1" and the "r2" types are defined as required ones. For each required context type defined in this property, an entity and a scope must also be defined, using the following two parameters.
  • REQUIRED_CONTEXT_TYPE_ENTITY[r]: This property must be defined for each context type "r" that has been specified in the required context types property. The value of this property should be a string corresponding to the actual value of the entity.
  • REQUIRED_CONTEXT_TYPE_SCOPE[r]: Similar to the entity property, thus one must be defined for each context type "r" that has been specified in the required context types property. The value of this property should be a string corresponding to the actual value of its scope.
Required context types are communicated to the context plug-in automatically (by the middleware), as it will be described later on.

Finally, to finish with the development of the context plug-in, we define its actual implementation in code. As mentioned already, the implementing class must be a sub-type of IContextPlugin. In this regard, the context plugin can either implement the IContextPlugin interface directly, or it can extend the AbstractContextPlugin class, as illustrated in the context plugins class diagram. The code of the plugin is listed in the following:

package cy.ac.ucy.cs.osgi.tutorial8;

import org.istmusic.mw.context.plugins.AbstractContextPlugin;
import org.istmusic.mw.context.util.scheduler.RecurringEvent;
import org.istmusic.mw.context.util.scheduler.Scheduler;
import cy.ac.ucy.cs.osgi.tutorial7.memory_model.MemoryContextElement;

import java.util.logging.Logger;

public class MemorySensor
extends AbstractContextPlugin
implements Runnable
{
private final Logger logger = Logger.getLogger(MemorySensor.class.getCanonicalName());

public static final String MEMORY_PLUGIN_ID = "Memory plugin";

public static final long MIN_WAIT_TIME = 500L; // 0.5 sec

public static final long INTERVAL = MemoryContextElement.MILLISECONDS_BEFORE_EXPIRY / 2;

private final RecurringEvent recurringEvent = new RecurringEvent(this, MIN_WAIT_TIME, INTERVAL);

private final double totalMemory;

public MemorySensor()
{
super(MEMORY_PLUGIN_ID);

logger.info("MemorySensor: constructed");
this.totalMemory = Runtime.getRuntime().totalMemory();
}

private final Scheduler scheduler = Scheduler.getInstance();

/** Automatically invoked by the context middleware when the plugin must be activated. */
public void activate()
{
logger.info("c_activating: " + this);
scheduler.scheduleEvent(recurringEvent);
}

/** Automatically invoked by the context middleware when the plugin must be deactivated */
public void deactivate()
{
logger.info("c_deactivating: " + this);
scheduler.removeRecurringEvent(recurringEvent);
}

public void run()
{
// acquire memory measurements
final long availableMemory = Runtime.getRuntime().freeMemory();
final double memoryLoad = 1d - (availableMemory / totalMemory);

// generate memory context element
MemoryContextElement memoryContextElement
= new MemoryContextElement(MEMORY_PLUGIN_ID, availableMemory, memoryLoad);

// fire context change event
logger.info("MemorySensor: firing event: " + memoryContextElement);
fireContextChangedEvent(this, memoryContextElement);
}

public String toString()
{
// produce a human-readable string
return MEMORY_PLUGIN_ID;
}
}
The first thing to note here is the fact that the class uses the "memory_mode package defined in the previous tutorial. The second important observation is that the memory sensor plugin extends the AbstractContextPlugin class. With this, we do not need to implement each and every method defined in the IContextPlugin interface, but rather simply implement the abstract methods defined in the abstract class and override any methods we need to change the functionality for. This is actually a popular idiom used extensively even in the J2SE libraries themselves (e.g., in the "javax.swing" package).

The constructor is quite straight-forward: we simply delegate the call to the super-constructor with the plug-in's ID as argument. Furthermore, in the constructor we initiate the "totalMemory" constant using a core JVM command.

The interesting part of the plug-in lies in the implementation of the activate and deactivate methods as well as to the implementation of the run method (of the "java.lang.Runnable" interface). The activate (and deactivate) method does a simple thing: it schedules (or unschedules) a recurring event. In this case, we use a custom scheduler which allows for the definition of one-time or recurring events. As shown in this example, the RecurringEvent is constructed by passing it three arguments:
  • A class implementing the Runnable interface: this is the code that will be invoked each time the recurring event is fired. In this case, the MemorySensor class itself is defined to implement the IContextListener interface, so the passed argument is itself (i.e., "this").
  • Minimum wait time: This long arithmetic value indicates the number of milliseconds the scheduler must wait before it fires an event for the first time. In this example this number is set to 500 (i.e., half a second).
  • Interval: The interval is also a long arithmetic value indicating the number of milliseconds which the scheduler waits before it repeats an invocation. In this example, the interval is set to 10000 (or 10 seconds) which means that the scheduler will invoke the runnable listener once every 10 seconds).
When scheduling the recurring event with the scheduler ("activate" method), in principle you register for asynchronous invocation of the run method. When you remove the recurring event, then you unregister form the asynchronous invocations. This mechanism is quite similar to using Threads, but it uses a Thread pool instead which can be quite beneficial in case you need to deal with resource constrained, mobile devices.

Finally, we examine the run method. This is where the actual context sensing takes place. In the first place, we collect our needed data. In this case we measure the available memory using the freeMemory core call of Java. Then we compute the memory load as a function of the available and total memory.

In the next step, we construct an instance of the MemoryContextElement (defined in the previous tutorial) by using the sensed data as input.

Finally, using the generated context element, we fire an event by using the overriden fireContextChangedEvent method. The only two arguments are the source (i.e., the plugin itself) and the context element.

Packaging and deploying the Memory Context Plugin

As usual, we package the code of the plug-in in a JAR file which contains the Manifest, the service descriptor XML file and the Memory Sensor class. A ready JAR file with the source code and an ANT build script is readily available here.

To deploy it, follow the steps indicated in the following screen:

osgi> install file:MemorySensor.jar
Bundle id is 9

osgi> bundle 9
file:MemorySensor.jar [9]
Id=9, Status=ACTIVE Data Root=C:\eclipse\configuration\org.eclipse.osgi\bundles\9\data
Registered Services
{org.istmusic.mw.context.plugins.IContextPlugin}={PROVIDED_CONTEXT_TYPE_SCOPE[memory]=#concepts.scopes.resources.memory, component.name=MemorySensor, PROVIDED_CONTEXT_TYPE_ENTITY[memory]=#concepts.entities.device|this, component.id=6, PROVIDED_CONTEXT_TYPES=memory, service.id=31}
No services in use.
No exported packages
Imported packages
org.istmusic.mw.context.plugins; version="0.0.0"<file:music-context-0.2.1.0-SNAPSHOT.jar [7]>
org.istmusic.mw.context.util.scheduler; version="0.0.0"<file:music-context-0.2.1.0-SNAPSHOT.jar [7]>
org.osgi.service.component; version="1.0.0"<file:plugins\org.eclipse.osgi.services_3.1.200.v20071203.jar [3]>
cy.ac.ucy.cs.osgi.tutorial7.memory_model; version="0.0.0"<file:MemoryModel.jar [8]>
No fragment bundles
Named class space
MemorySensor; bundle-version="1.0.0"[provided]
No required bundles

osgi> start 9

osgi> Jan 29, 2009 10:05:45 AM cy.ac.ucy.cs.osgi.tutorial8.MemorySensor <init>
INFO: MemorySensor: constructed
Jan 29, 2009 10:05:45 AM org.istmusic.mw.context.manager.ContextManager installPlugin
INFO: CM:Installing: Memory plugin

Notably, the bundle exports (i.e., registers) the IContextPlugin service. Also, when started (i.e., when the plug-in moves in to the ACTIVE state), the middleware detects it and automatically binds to it moving it to the C_INSTALLED state (shown in the extended plugins state diagram shown above). At this point, the plug-in is not started for a simple reason: There is no context listener actively requiring the memory context type, and thus the middleware keeps the plug-in inactive albeit resolved.

Developing context reasoner plugins

Besides the AbstractContextPlugin, the developer can also extend the AbstractContextReasonerPlugin class. As shown in the context plugin class diagram, the AbstractContextReasonerPlugin class extends the AbstractContextPlugin class. In addition to the facilities provided by the latter, the former also allows for receiving and handling context events. In this regard, the corresponding plugin must implement the "contextChanged" method (inherited from the IContextListener interface), in which it should listen to events of its required context types (as defined with "REQUIRED_*" properties in the service descriptor) and react based on them. The development of a context reasoner plug-in is the object of the second homework.

Homework 8.1: Develop a context plug-in to monitor the location. The actual coordinates reported can be simulated (i.e., fixed or random). You can use the location model developed in homework 7.1 of the last tutorial. You are actually encouraged to do so.

Homework 8.2: Develop a context reasoner plug-in that will use the location context type (i.e., as generated from the plug-in of the previous homework) to produce the weather context type. The actual value will be computed as a function of the latitude as follows: "temperature = 5 + (90 - |latitude|) / 1.5". I.e., the temperature is reported as -5°C at the poles and 65°C at the equator (which is of course an exaggeration even with the most pessimistic global warming predictions). The humidity should be constantly reported as 10%. You can use the location and weather models developed in homework 7.1 of the last tutorial. You are actually encouraged to do so.

References

[1]. Nearchos Paspallis, Romain Rouvoy, Paolo Barone, George A. Papadopoulos, Frank Eliassen, Alessandro Mamelli, A Pluggable and Reconfigurable Architecture for a Context-aware Enabling Middleware System, 10th International Symposium on Distributed Objects, Middleware, and Applications (DOA'08), Monterrey, Mexico, Nov 10 - 12, 2008, Springer Verlag LNCS 5331, pp. 553-570

The MUSIC Context Model - Tutorial 7

In this tutorial, we will cover the context model which supports the MUSIC Context System (introduced in the previous tutorial). For a brief view of the objective of these tutorials, check out the introductory post.

The context model is used to enable the following features:
  1. Data encapsulation: In all programming languages, the developers use specialized data structures, like sets and maps, to implement their software. For the purposes of context-aware applications, we provide a model which is used for encapsulating context data, as it is described in [1] and [2].
  2. Context semantics: In order to allow interoperability among individual context providers and context consumers (possibly distributed across different devices), a context model is needed to establish the semantics of each individual context element.
In our approach, the context model achieves both these goals by leveraging a three-layered approach as described in [2]. To do so, we employ technology originating from Semantic Web research in the form of Ontologies.

The MUSIC Context Ontology

The MUSIC Context System provides an Ontology-backed context dialect which can be used to facilitate interoperability between different devices. This dialect also serves as a common point of reference for developers leveraging third-party context plug-ins for their context-aware applications.

The MUSIC context dialect is based on two basic terms: Entities and Scopes. The former are used to determine which entity the context type refers to. For instance, is it a person (and who) or a device (and which). The latter are used to identify the scope of the context type. For instance, a context type might refer to the memory resource of a device. While this context dialect is not restricted in any way, we here present the most basic entities and scopes defined in it, to allow for developers to better understand its logic.

Entities

There are only three entities possible, corresponding to persons (i.e., users), devices and the environment. This is consistent with Dey's definition of context [3]: "[Context is] any information that can be used to characterize the situation of an entity. An entity is a person, place, or object that is considered relevant to the interaction between a user and an application, including the user and applications themselves".
  • #concepts.entities.user
  • #concepts.entities.device
  • #concepts.entities.environment
Furthermore, these entities can be combined with a so-called grounding, which further clarifies the entity. In the case of the user, the grounding can be either the special ID "myself", which is automatically dereferenced by the system to the ID of the user who is logged in the system. Alternatively, the grounding can be set directly to the ID of the user in reference. The ID is typically set to the email address of the user.

For example, when a user with an email address of "nearchos@cs.ucy.ac.cy" is logged in the system, then the following entity:
  • #concepts.entities.user|myself
is automatically dereferenced to:
  • #concepts.entities.user|nearchos@cs.ucy.ac.cy
The "#concepts.entities.user|myself" is the default user entity value, meaning that it is equivalent to "#concepts.entities.user".

Additional details and a description of all the groundings possible are available here.

Scopes

The scopes are the basic identifiers of context information. Once the entity to which the context information corresponds to (i.e., by defining it as described above), then the next step is to define the scope of the context information you will provide.

While there are practically unlimited possibilities on the things you can model with this approach, the default MUSIC Context Ontology defines a limited domain of scopes with the purpose of allowing reuse, interoperability and ease of development. Thus, we here describe these scopes, which can be used to describe mainly basic information about the users, the device resources and the environment.

One of the most fundamental context scopes is the location, which (as we will see in the following section) can be represented either as absolute coordinates or in the form of a physical address.
  • #concepts.scopes.location
This scope can be combined with any of the three possible entity types to indicate the location of either a user, a device or the interaction environment.

Additional details and a more extensive description of the available scope is also provided here.

Representations

The concept of representations is primarily aimed to facilitate Model-Driven Development (MDD) by enabling automatic transformation among many possible representations of the same data-structure. For example, a date can be represented both as a string (e.g., "2009-01-18T10:54:03+02:00") or as a long arithmetic value (e.g., "1232268843773").

As this concept is not a critical part of this series of tutorials, I skip further description and point interested readers to this web-page and to papers [1] and [2].

Context Model data-structures

In our context model, we define the following data-structures:
  • Context elements: These are the basic structures abstracting context information. For instance, of a developer needs to define a weather or a memory context type, then a corresponding context element is typically developed to abstract it. Each context element is associated to a structure, which contains the actual context information in terms of context values.
  • Context data: This data structure is simply used as a container for the actual context values. In this respect, it implements a map which points from a Scope to a Context Value. For example, if the corresponding context element is of weather type, then the contained context values can be of temperature and humidity types.
  • Context values: The context value data-structures are the actual containers of individual context values. For example, a temperature context type is a container of the temperature value.
  • Values: The value structure are elementary data structures such as strings, integers, etc. They are used as containers of context value data. For example, in the case of the temperature context value, it is associated to a float or double value type. Besides elementary context types, arrays (of elementary types) are also supported.
  • Metadata: The metadata are extra-functional information characterizing context elements and context values (such as for example a time-stamp indicating the time when the element or value were created). Similar to context elements, the metadata structures consist of finer-grained metadatum value containers.
  • Metadatum: Similar to context values, but specialized in encapsulating metadata values only. They also leverage value types for encoding the actual data.
The hierarchy of the context model is illustrated in the following class diagram:

The MUSIC Context Model class diagram

This figure graphically illustrates the relationship among the basic concepts of the context model. Also, it depicts the three main Ontology-based concepts: Entities, Scopes and Representations. However, to avoid cluttering in the diagram we omit links denoting relationship between the structures and the Ontology concepts. Furthermore, this figure illustrates a number of elementary Value implementations (however these are not complete; for example the arrays of elementary values are not depicted).

Developing the Memory Context Model bundle

In order to illustrate the use of the context model, in the following we describe how to develop a bundle providing the model for a simple context type: memory. For this purpose, we define a memory context element, enclosing two trivial context values: available memory and memory load.

The development of the memory context model starts with detecting the appropriate entity and scope ontology concepts. In this case, the memory refers to this device (i.e., the device in which the sensor is deployed) and the corresponding scope refers to the memory resource. In this regard, the entity and the scope are defined as follows:
  • Entity: "#concepts.entities.device|this"
  • Scope: "#concepts.scopes.resources.memory"
The code of the new MemoryContextElement class is illustrated in the following:

package cy.ac.ucy.cs.osgi.tutorial7.memory_model;

import org.istmusic.mw.context.model.api.*;
import org.istmusic.mw.context.model.impl.Factory;
import org.istmusic.mw.context.ontologies.DefaultMusicOntologyV0_1;

public class MemoryContextElement implements IContextElement
{
public static final IEntity ENTITY = DefaultMusicOntologyV0_1.ENTITY_DEVICE_THIS;

public static final IScope SCOPE = DefaultMusicOntologyV0_1.SCOPE_RESOURCE_MEMORY;

public static final long MILLISECONDS_BEFORE_EXPIRY = 20000L; // 20 seconds

private final String source;

private final IMetadata metadata;

private final MemoryContextData contextData;

public MemoryContextElement(final String source, final long availableMemory, final double memoryLoad)
{
this.source = source;
this.metadata = Factory.createDefaultMetadata(MILLISECONDS_BEFORE_EXPIRY);
this.contextData = new MemoryContextData(availableMemory, memoryLoad);
}

public IEntity getEntity()
{
return ENTITY;
}

public IScope getScope()
{
return SCOPE;
}

public IRepresentation getRepresentation()
{
return null; // ignore for now
}

public String getSource()
{
return source;
}

public IContextData getContextData()
{
return contextData;
}

public IMetadata getMetadata()
{
return metadata;
}

public String toString()
{
return "(" + ENTITY.getEntityTypeAsShortString() + ", "
+ SCOPE.getScopeAsShortString() + ")->" + contextData;
}
}

The code in this example is straight-forward: The memory context element is constructed by specifying its entity and scope (as constants) and by assigning the memory context data (see the following paragraph on the MemoryContextData) and the metadata (as a default implementation abstracting the creation and expiry timestamp).

The provided availableMemory (long) and memoryLoad (double) values are passed on for the construction of the MemoryContextData. Also, the accessor method for the representation is ignored for now, as we are not tackling inter-representation issues in this series of tutorials.

In the following, we illustrate the implementation code for the memory context data:

package cy.ac.ucy.cs.osgi.tutorial7.memory_model;

import org.istmusic.mw.context.model.api.IContextData;
import org.istmusic.mw.context.model.api.IContextValue;
import org.istmusic.mw.context.model.api.IScope;
import org.istmusic.mw.context.model.api.IValue;

import java.util.Set;
import java.util.HashSet;

public class MemoryContextData implements IContextData
{
private final AvailableMemoryContextValue availableMemoryContextValue;

private final MemoryLoadContextValue memoryLoadContextValue;

public MemoryContextData(final long availableMemory, final double memoryLoad)
{
availableMemoryContextValue = new AvailableMemoryContextValue(availableMemory);
memoryLoadContextValue = new MemoryLoadContextValue(memoryLoad);
}

public IContextValue getContextValue(IScope scopeKey)
{
if(scopeKey == null)
{
throw new IllegalArgumentException("The scopeKey cannot be null");
}

if(scopeKey.equals(AvailableMemoryContextValue.SCOPE))
{
return availableMemoryContextValue;
}
else if(scopeKey.equals(MemoryLoadContextValue.SCOPE))
{
return memoryLoadContextValue;
}

return null;
}

public IValue getValue(IScope scopeKey)
{
IContextValue contextValue = getContextValue(scopeKey);

return contextValue == null ? null : contextValue.getValue();
}

public Set keySet()
{
final Set keySet = new HashSet();

keySet.add(AvailableMemoryContextValue.SCOPE);
keySet.add(MemoryLoadContextValue.SCOPE);

return keySet;
}

public String toString()
{
return "[" + availableMemoryContextValue + ", " + memoryLoadContextValue + "]";
}
}

In this case, the provided arguments are used to initialize the AvailableMemoryContextValue and the MemoryLoadContextValue (both realization of the ContextValue type). The stored values are then accessed via named invocation of the getContextValue method where the specified argument indicates the scope of the corresponding value.

To avoid cluttering, the complete code of the other custom context types is omitted, but is available for examination and reuse in the MemoryModel.jar file., which will be discussed later on.

The resulting context model, along with its associations to the original model, is illustrated in the following diagram:

The extended Memory Context Model class diagram

As illustrated in this figure, each custom type corresponds (i.e., extends) to an equivalent basic model type. The code for the AvailableMemoryContextValue and the MemoryLoadContextValue, although not depicted, is also straight-forward: it provides a reference to the corresponding elementary context type (LongValue and DoubleValue respectively).

Deploying the Memory Context Model bundle

Although it provides no actual functionality, here we describe how the context model bundle is dormed and deployed. Its use is limited to exporting the memory context model classes.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tutorial 7: MemoryModel
Bundle-SymbolicName: MemoryModel
Bundle-Version: 1.0.0
Import-Package: org.istmusic.mw.context.model.api,
org.istmusic.mw.context.model.impl,
org.istmusic.mw.context.model.impl.values,
org.istmusic.mw.context.ontologies
Export-Package: cy.ac.ucy.cs.osgi.tutorial7.memory_model


Notably, the Manifest file in this case does not define an XML service descriptor (as we mentioned already, this bundle does not use or provide any services; it is rather used as a library providing (i.e., exporting) the "cy.ac.ucy.cs.osgi.tutorial7.memory_model" package.

When deployed, the bundle simply exports the corresponding package, as illustrated in the following console output:

osgi> install file:MemoryModel.jar
Bundle id is 8

osgi> bundle 8
file:MemoryModel.jar [8]
Id=8, Status=INSTALLED Data Root=C:\eclipse\configuration\org.eclipse.osgi\bundles\8\data
No registered services.
No services in use.
Exported packages
cy.ac.ucy.cs.osgi.tutorial7.memory_model; version="0.0.0"[exported]
No imported packages
No fragment bundles
No named class spaces
No required bundles

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.2.R34x_v20080826-1230
1 ACTIVE org.eclipse.equinox.ds_1.0.0.v20080427-0830
2 ACTIVE org.eclipse.equinox.util_1.0.0.v20080414
3 ACTIVE org.eclipse.osgi.services_3.1.200.v20071203
4 ACTIVE RunnableProvider_1.0.0
5 ACTIVE RunnableConsumer_1.0.0
6 ACTIVE CLI_Client_1.0.0
7 ACTIVE music-context_0.2.1.0-SNAPSHOT
8 INSTALLED MemoryModel_1.0.0

osgi>

This concludes this tutorial. It is recommended that you try to implement some sample models, as described in the exercises, in order to familiarize yourself with the context model.

Homework 7.1
: Develop a bundle containing the model for a location context type. The created context element should be named LocationContextElement. Argue which entity and scope are the most appropriate, based on the discussion presented here. The context element must refer to a LocationContextData structure, containing descriptive information for the longitude and the latitude (their semantics and value range are described in Wikipedia: longitude and latitude). The resulting context values should be named LongitudeContextValue and LatitudeContextValue. Both must point to a single DoubleValue. The resulting structure must be packaged in a JAR bundle, along with the necessary Manifest file. You must override the "toString" method in the LocationContextElement to generate an easily readable one-line output encoding the location.

Homework 7.2: Develop a bundle containing the model for a weather context type. The created context element should be named WeatherContextElement. Argue which entity and scope are the most appropriate, based on the discussion presented here. The context element must refer to a WeatherContextData structure, containing descriptive information for the temperature and the wind (described by the direction and the power). The resulting context values should be named TemperatureContextValue and WindContextValue. The former must point to a single DoubleValue, and the latter must point to a StringValue and an IntegerValue (in the range 1 to 10). The resulting structure must be packaged in a JAR bundle, along with the necessary Manifest file. You must override the "toString" method in the WeatherContextElement to generate an easily readable one-line output encoding the weather.

References

[1]. Roland Reichle, Michael Wagner, Mohammad Ullah Khan, Kurt Geihs, Massimo Valla, Cristina Fra, Nearchos Paspallis, George A. Papadopoulos, A Context Query Language for Pervasive Computing Environments, 5th IEEE Workshop on Context Modeling and Reasoning (CoMoRea) in conjunction with the 6th IEEE International Conference on Pervasive Computing and Communication (PerCom), Hong Kong, 17–21 March 2008, IEEE Computer Society Press, pp. 434-440

[2].
Michael Wagner, Roland Reichle, Mohammad Ullah Khan, Kurt Geihs, Jorge Lorenzo, Massimo Valla, Cristina Fra, Nearchos Paspallis, George A. Papadopoulos, A Comprehensive Context Modeling Framework for Pervasive Computing Systems, 8th IFIP International Conference on Distributed Applications and Interoperable Systems (DAIS), 4-6 June, 2008, Oslo, Norway, Springer Verlag LNCS 5053, pp. 281-295

[3]. Anind K. Dey, Understanding and Using Context, Personal and Ubiquitous Computing Journal, Volume 5 (1), 2001, pp. 4-7

Wednesday, January 07, 2009

Introduction to the MUSIC Context System - Tutorial 6

Continuing in the next section of the "OSGi and MUSIC Context" series of tutorials, we here introduce the basics of the MUSIC Context System. The MUSIC Context System [1] is an OSGi component which provides automated management for multiple, dynamically-available context providers and consumers. As an OSGi component, the MUSIC Context System exports two services:
  • IContextAccess
  • IContextManagement
The former provides an API for accessing context information both synchronously and asynchronously. Furthermore, it allows for the use of the Context Query Language (CQL) [2]. The latter, provides more advanced access to the internals of the context management component, allowing external components, such as the Distributed Context Management System to function (make context remotely available in this case).

To install the MUSIC Context System, you first need to build or download the corresponding JAR file. You can build the latest version from source code (using Maven), or you can download a recent version of it from here: "music-context-0.2.1.0-SNAPSHOT.jar".

Once you build (or download) and install this JAR file, you can notice a few installed services, as illustrated in the following console output:
osgi> install file:music-context-0.2.1.0-SNAPSHOT.jar
Bundle id is 7

osgi> start 7

osgi> ss

Framework is launched.


id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.2.R34x_v20080826-1230
1 ACTIVE org.eclipse.equinox.ds_1.0.0.v20080427-0830
2 ACTIVE org.eclipse.equinox.util_1.0.0.v20080414
3 ACTIVE org.eclipse.osgi.services_3.1.200.v20071203
4 ACTIVE RunnableProvider_1.0.0
5 ACTIVE RunnableConsumer_1.0.0
6 ACTIVE CLI_Client_1.0.0
7 ACTIVE music-context_0.2.1.0-SNAPSHOT


osgi> services (objectClass=org.istmusic*)
{org.istmusic.mw.context.cache.IContextCacheService}={component.name=context.cache, component.id=5, service.id=28}
Registered by bundle: file:music-context-0.2.1.0-SNAPSHOT.jar [7]
Bundles using service:
file:music-context-0.2.1.0-SNAPSHOT.jar [7]
{org.istmusic.mw.context.cqp.IContextQueryService}={component.name=context.query, component.id=4, service.id=29}
Registered by bundle: file:music-context-0.2.1.0-SNAPSHOT.jar [7]
Bundles using service:
file:music-context-0.2.1.0-SNAPSHOT.jar [7]
{org.istmusic.mw.context.IContextAccess, org.istmusic.mw.context.IContextManagement}={component.name=context.manager, component.id=3, service.id=30}
Registered by bundle: file:music-context-0.2.1.0-SNAPSHOT.jar [7]
No bundles using service.

As you can see here, there are three sub-components defined in the context bundle:
  • Context Cache
  • Context Query [processor]
  • Context Manager
This architecture is also depicted in the following figure:
MUSIC Context System - Middleware ArchitectureAs shown in this figure, the "Context Manager" is the central component, utilizing the services offered by the other two components. For instance, the "Context Cache" component, provides a simple storage service, that is used by both the "Context Manager" and the "Context Query Processor". The latter, offers the CQL service, which is used by the "Context Manager" whenever it needs to handle CQL queries.

From an client-side point-of-view though, what matters are the two services offered by the "Context Manager" and which were mentioned already. In the following, we show their exact definition and briefly explain what they are used for.

The IContextAccess service

As mentioned already, the context access service is used for allowing external components, deployed in OSGi, to leverage the capabilities of the context system. The interface (API) of this service is shown in the following listing:
/**
* This is the main point of interaction with the context service (and the
* context system in general). Any context client requiring to interact
* with the context system can achieve it through this interface.
*/
public interface IContextAccess
{
/**
* Provides synchronous access to the specified context data. It returns
* the last context element stored only.
*/
public IContextElement queryContextLastElement(IEntity entity, IScope scope)
throws ContextException;

/**
* Provides synchronous access to the specified context data.
*/
public IContextDataset queryContext(IEntity entity, IScope scope)
throws ContextException;

/**
* Provides asynchronous access to the specified context data.
*/
public void queryContext(IEntity entity, IScope scope, IContextListener listener)
throws ContextException;

/**
* Provides synchronous access to the context data.
*/
public IContextDataset queryContext(final IContextQuery query)
throws ContextException;

/**
* Provides asynchronous access to the context data. The provided context
* listener is asynchronously contacted once the results of the given
* IContextQuery are available.
*/
public void queryContext(IContextQuery query, IContextListener listener)
throws ContextException;

/**
* Provides asynchronous access to the context data. By providing a the
* IEntity and the IScope of the required data, the provided
* IContextListener reference is asynchronously notified of context
* changes in the specified entity-scope pair automatically.
*/
public void addContextListener(IEntity entity, IScope scope, IContextListener listener)
throws ContextException;

/**
* Provides asynchronous access to the context data. This method is used
* along with the #addContextListener(IEntity, IScope, IContextListener) method
* in order to allow unregistering of a context client from a particular
* entity-scope pair.
*/
public void removeContextListener(IEntity entity, IScope scope, IContextListener listener)
throws ContextException;

/**
* Provides the ability to the context clients to specify which context
* types they require (i.e. because an application was launched, requiring
* additional context types). The identification of a type is done in terms
* of a doublet comprising of an IEntity and a IScope.
*
* When registered as needed, a context type is monitored by the
* context system be means of activating an appropriate context sensor (if
* available). When multiple sensors are available providing the same
* context type, then the system might decide to activate only one or more
* of them, based on the resource availability and the quality of the
* available sensors.
*
* Registering the same (Entity,Scope) doublet twice does not throw an
* exception and does not result in any noticeable change.
*/
public void addNeededContextType(IEntity entity, IScope scope, Object requestor)
throws ContextException;

/**
* Provides the ability to the context clients to specify which context
* types they do not need anymore (i.e. because an application was shut
* down). The identification of a type is done in terms of a doublet
* comprising of an IEntity and a IScope.
*
* Unregistering a non existing (Entity,Scope) doublet does not throw an
* exception and does not result in any noticeable change.
*/
public void removeNeededContextType(IEntity entity, IScope scope, Object requestor)
throws ContextException;
}
As defined in the comments of the corresponding methods, these methods are used to either explicitly request a specific context type, or register a query (formulated in the CQL language [2]) for processing. As the context system needs to be aware of the runtime context needs of the individual applications, it either registers the applications' needs implicitly or it allows for them to express them explicitly (using the last two methods). More details about the functioning of each method can be found in the Javadocs of the "IContextAccess.java" interface.

The IContextManagement service

Unlike context access, the context management service aims for other components which aim at improving or complementing the functionality of the context system. For instance, a component can be used to distribute the local context information across a predefined set of nodes. Another example is of a component which connects to the context system in order to access and visualize relevant information.

In order to enable this kind of functionality, the context management service provides methods which allow to access information about the needed and the provided context types of the node. The interface of the service is illustrated here:
/**
* Provides management-level access concerning the internal state of the MUSIC
* context system.
*/
public interface IContextManagement
{
/**
* Returns a set of EntityScopePairs which correspond to the currently
* required context types.
*/
public Set getCurrentRequiredContextTypes();

/**
* Registers for asynchronous notification of when a new required context
* type (i.e. EntityScopePair) is added or removed.
*/
public void addRequiredContextTypesListener(IContextManagementListener listener);

/**
* Un-registers from asynchronous notification of when a new required
* context type (i.e. EntityScopePair) is added or removed.
*/
public void removeRequiredContextTypesListener(IContextManagementListener listener);

/**
* Returns a Set of EntityScopePairs which correspond to the currently provided
* context types.
*/
public Set getCurrentProvidedContextTypes();

/**
* Registers for asynchronous notification of when a new provided context
* type (i.e. EntityScopePair) is added or removed.
*/
public void addProvidedContextTypesListener(IContextManagementListener listener);

/**
* Un-registers from asynchronous notification of when a new provided
* context type (i.e. EntityScopePair) is added or removed.
*/
public void removeProvidedContextTypesListener(IContextManagementListener listener);

/**
* Returns a set of EntityScopePairs which correspond to the currently
* required context types from remote sources.
*/
public Set getCurrentRequiredRemoteContextTypes();

/**
* Registers for asynchronous notification of when a new required remote
* context type (i.e. EntityScopePair) is added or removed.
*/
public void addRequiredRemoteContextTypesListener(IContextManagementListener listener);

/**
* Un-registers from asynchronous notification of when a new provided
* context type (i.e. EntityScopePair) is added or removed.
*/
public void removeRequiredRemoteContextTypesListener(IContextManagementListener listener);

/**
* Allows external entities (such as the "Distributed Context Manager") to
* raise ContextChangedEvents.
*/
public void contextChanged(ContextChangedEvent event);
}
The methods of this service allow direct (synchronous) and indirect (asynchronous) access to three context types:
  • Required Context Types: Those context types that are needed by the currently deployed (i.e., started) applications
  • Provided Context Types: Those context types that are provided by the currently deployed plug-ins (context sensors and context reasoners)
  • Required Remote Context Types: Those context types that are needed locally (by the deployed applications) but which are not offered by any of the locally deployed plug-ins
The last method allows for other components (besides the context system and the plug-ins) to generate context events.

In the following tutorials, we will describe the architecture of the context plug-ins, and how a component-based application can be designed to exploit context information from the context system.

[1]. Nearchos Paspallis, Romain Rouvoy, Paolo Barone, George A. Papadopoulos, Frank Eliassen, Alessandro Mamelli, A Pluggable and Reconfigurable Architecture for a Context-aware Enabling Middleware System, 10th International Symposium on Distributed Objects, Middleware, and Applications (DOA'08), Monterrey, Mexico, Nov 10 - 12, 2008, Springer Verlag LNCS 5331, pp. 553-570

[2]. Roland Reichle, Michael Wagner, Mohammad Ullah Khan, Kurt Geihs, Massimo Valla, Cristina Fra, Nearchos Paspallis, George A. Papadopoulos, A Context Query Language for Pervasive Computing Environments, 5th IEEE Workshop on Context Modeling and Reasoning (CoMoRea) in conjunction with the 6th IEEE International Conference on Pervasive Computing and Communication (PerCom), Hong Kong, 17–21 March 2008, IEEE Computer Society Press, pp. 434-440

Sunday, January 04, 2009

Component lifecycle support - Tutorial 5

In this part of our series of tutorials, we examine a few details about the component lifecycle support, and also how to update components at runtime.

Component lifecycle support

When a component is installed, it is first resolved and then started. Resolving a component implies that all its static dependencies (i.e., the imported packages of its bundle container) are resolved (i.e., exported by some other, installed bundles).

Furthermore, when using Declarative Services, the dynamic service dependencies of a component must also be resolved in order to be able to start it (i.e., activate it). We demonstrate these two dependencies in the following example.

Revisiting the CLI Client

In this tutorial, we will revise the "CLI_Client" that was developed in the previous tutorial. In this respect, you can simply copy the code used in the previous example into a new folder and make changes as stated in the following.

The source code of the new "CLI_Client" class looks as follows:
package cy.ac.ucy.cs.osgi.tutorial5;

import org.eclipse.osgi.framework.console.CommandProvider;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.osgi.service.component.ComponentContext;

import java.util.logging.Logger;

public class CLI_Client implements CommandProvider
{
private final Logger logger = Logger.getAnonymousLogger();

public CLI_Client()
{
super();

logger.info("CLI_Client: Constructed component");
}

public String getHelp()
{
return "\ttest - Tests interaction with the Runnable service";
}

protected void activate(ComponentContext componentContext)
{
logger.info("CLI_Client: activate");
}

protected void deactivate(ComponentContext componentContext)
{
logger.info("CLI_Client: deactivate");
}

private Runnable runnableService = null;

public void setRunnableService(Runnable runnableService)
{
this.runnableService = runnableService;
}

public void unsetRunnableService(Runnable runnableService)
{
this.runnableService = null;
}

public void _test(CommandInterpreter ci)
{
if(runnableService == null)
{
System.out.println("Testing failed: no Runnable service provider available");
}
else
{
System.out.println("Invoking Runnable service ...");
runnableService.run();
System.out.println("Done!");
}
}
}
The changes are marked in bold, and consist of the following additions:
  • Two new packages were imported (org.osgi.service.component.ComponentContext and java.util.logging.Logger)
  • A logger field was defined, and an info log message was added to the constructor
  • Two methods were defined, with the signature "protected void (de)activate(ComponentContext)"; both of them are simply used to output an info log message
To make things more interesting, we also make a change in the "CLI_Client.xml" service descriptor, so that the referenced service is not optional, but rather mandatory. This is illustrated in the following code, where the "0..1" value was replaced by "1.1" (be reminded that the first digit signifies optionality and the latter multiplicity):
<?xml version="1.0"?>

<component name="CLI_Client">

<implementation class="cy.ac.ucy.cs.osgi.tutorial5.CLI_Client"/>

<service>
<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
</service>

<reference name="runnable_service"
interface="java.lang.Runnable"
bind="setRunnableService"
unbind="unsetRunnableService"
cardinality="1..1"
policy="dynamic"/>

</component>
Next, define the new "MANIFEST.MF" file. Note that the file is exactly the same, except for adding a new, second imported package:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tutorial 5: CLI_Client
Bundle-SymbolicName: CLI_Client
Bundle-Version: 1.0.0
Import-Package: org.eclipse.osgi.framework.console,
org.osgi.service.component
Service-Component: OSGI-INF/CLI_Client.xml

Finally, proceed to build the "CLI_Client.jar" JAR file using the same structure as the one used for the original "CLI_Client" (we keep the name of the JAR file the same as the one of the last tutorial on purpose).

Deploying the new CLI_Client


To deploy the newly developed component, copy the generated JAR file in the deployment folder and replace the one from the previous tutorial. Please note at this point that this has no implications on the running instance of the "CLI_Client" component. To update the actual new component, run the "update" command in the console, as follows:
osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.2.R34x_v20080826-1230
1 ACTIVE org.eclipse.equinox.ds_1.0.0.v20080427-0830
2 ACTIVE org.eclipse.equinox.util_1.0.0.v20080414
3 ACTIVE org.eclipse.osgi.services_3.1.200.v20071203
4 ACTIVE RunnableProvider_1.0.0
5 ACTIVE RunnableConsumer_1.0.0
6 RESOLVED CLI_Client_1.0.0

osgi> update 6

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.2.R34x_v20080826-1230
1 ACTIVE org.eclipse.equinox.ds_1.0.0.v20080427-0830
2 ACTIVE org.eclipse.equinox.util_1.0.0.v20080414
3 ACTIVE org.eclipse.osgi.services_3.1.200.v20071203
4 ACTIVE RunnableProvider_1.0.0
5 ACTIVE RunnableConsumer_1.0.0
6 INSTALLED CLI_Client_1.0.0
At this point, note how the state of the "CLI_Client" component has changed from "RESOLVED" to "INSTALLED". The "CLI_Client" component can be started again using the "start 6" command.
osgi> start 6
Jan 4, 2009 7:05:14 PM cy.ac.ucy.cs.osgi.tutorial5.CLI_Client <init>
INFO: CLI_Client: Constructed component
Jan 4, 2009 7:05:14 PM cy.ac.ucy.cs.osgi.tutorial5.CLI_Client activate
INFO: CLI_Client: activate

osgi> stop 4
Jan 4, 2009 7:05:17 PM cy.ac.ucy.cs.osgi.tutorial5.CLI_Client deactivate
INFO: CLI_Client: deactivate

osgi> start 4

Jan 4, 2009 7:05:21 PM cy.ac.ucy.cs.osgi.tutorial5.CLI_Client <init>
INFO: CLI_Client: Constructed component
Jan 4, 2009 7:05:21 PM cy.ac.ucy.cs.osgi.tutorial5.CLI_Client activate
INFO: CLI_Client: activate
Interestingly, when a component is constructed, activated and deactivated, appropriate log messages appear in the console. This is the result of the OSGi framework constructing the component and invoking the "activate" and "deactivate" methods on it. This is done using a technique commonly referred to as Inversion of Control (IoC).

What is even more interesting though, is that stopping and starting a different service (in this case the "RunnableProvider"), causes the component to be stopped and started automatically. This happens because the service descriptor of this updated component defines a mandatory, one-to-one dependency on the "java.lang.Runnable" service. As component with ID #4 starts and stops, the "java.lang.Runnable" service becomes unavailable and back available. Since the "RunnableProvider" is the single only component that offers this service, the Declarative Services runtime realizes that the "CLI_Client" must be deactivated first, and then activated again when the service is available again.

Homework 5.1: Build a second "java.lang.Runnable" provider, and name it "RunnableProvider2" (other than the name, it can be identical to the original "RunnableProvider"). Then deploy and start it. While both the "RunnableProvider" and the "RunnableProvider2" are both started, along with the new "CLI_Client", try to stop the used provider and note what happens to the "CLI_Client".

Homework 5.2: Discuss with a few words each possible transition in the OSGi bundle's lifecycle state diagram (from OSGi Service Platform Release 4 Version 4.1 Core Specification):
OSGi component lifecycle
Homework 5.1: Read section 2.9 (Incremental Development) in the second chapter of Neil Bartlett's book

Saturday, January 03, 2009

Interacting with the CLI - Tutorial 4

In the next post of this series of tutorials, we describe a way to interact with the Command Line Interface (CLI) of the Equinox OSGi console. Taking advantage of this functionality, we then illustrate the power of Declarative Services by demonstrating how dynamic binding is handled by the components.

Developing the CLI Client

In this example, we leverage a mechanism provided by OSGi which allows interacting with the framework via commands at the console at runtime. To achieve this, we use OSGi's CommandProvider mechanism. The latter, is implemented as a service which is exported by a component that wishes to interact with the framework via CLI, and which is consumed by the framework itself.

The bundle structure is similar to the one used in the previous two tutorials, and is omitted for brevity. The service descriptor on the other hand, has the distinctive that it both provides a service (the "CommandProvider") and it references one (the "java.lang.Runnable"):
<?xml version="1.0"?>

<component name="CLI_Client">

<implementation class="cy.ac.ucy.cs.osgi.tutorial4.CLI_Client"/>

<service>
<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
</service>

<reference name="runnable_service"
interface="java.lang.Runnable"
bind="setRunnableService"
unbind="unsetRunnableService"
cardinality="0..1"
policy="dynamic"/>

</component>
It is worth noting that the reference element in this XML descriptor is identical to the one used in the "ServiceConsumer" described in the previous tutorial. On the other hand, the implementation class is of course adjusted to the new class, and a service element is added to define that this component implements the "CommandProvider" service, and thus is capable of interacting the CLI in the console.

Proceeding to the source code of the CLI_Client component, again you can observe some similarities to the code of the "RunnableConsumer", i.e., the default constructor and the standard setter methods for the "java.lang.Runnable" service :
package cy.ac.ucy.cs.osgi.tutorial4;

import org.eclipse.osgi.framework.console.CommandProvider;
import org.eclipse.osgi.framework.console.CommandInterpreter;

public class CLI_Client implements CommandProvider
{
public CLI_Client()
{
super();
}

public String getHelp()
{
return "\ttest - Tests interaction with the Runnable service";
}

private Runnable runnableService = null;

public void setRunnableService(Runnable runnableService)
{
this.runnableService = runnableService;
}

public void unsetRunnableService(Runnable runnableService)
{
this.runnableService = null;
}

public void _test(CommandInterpreter ci)
{
if(runnableService == null)
{
System.out.println("Testing failed: no Runnable service provider available");
}
else
{
System.out.println("Invoking Runnable service ...");
runnableService.run();
System.out.println("Done!");
}
}
}
The most important observation in this code is that the implementing class in not a POJO anymore (compared to the previous two tutorials). Rather, the class implements the "CommandProvider" interface and even imports two classes:
  • org.eclipse.osgi.framework.console.CommandProvider
  • org.eclipse.osgi.framework.console.CommandInterpreter
Furthermore, a method with the signature "getHelp()" is defined, in order to implement the "CommandProvider" interface. Finally, the class defines a method with the signature "_test(CommandInterpreter)", which is its custom way of interacting with the console (the preceding underscore is a required character for a new command).

Finally, the bundle's MANIFEST is similar to those defined in the previous tutorials, but it introduces the new "Import-Package" parameter type:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tutorial 4: CLI_Client
Bundle-SymbolicName: CLI_Client
Bundle-Version: 1.0.0
Import-Package: org.eclipse.osgi.framework.console
Service-Component: OSGI-INF/CLI_Client.xml

This new parameter type specifies packages that this bundle needs. A similar parameter type, namely "Export-Package", defines packages that are exported by the bundle. At runtime, OSGi evaluates the needs of each bundle against the offered packages, and it resolves only those bundles of which their needed packages are offered by some other, installed bundles. This, in principle, provides a powerful encapsulation mechanism (cf. information hiding), which improves that of plain Java.

Overall, with the new additions we achieve to introduce new functionality to the CLI Client (compared to the "RunnableProvider" of the previous tutorial), in terms of the "test" method that can be invoked in the console. We illustrate this new functionality by deploying, starting and testing the CLI Client.

Deploying and activating the CLI_Client

First, make sure that you have followed the steps described in the previous tutorials to install and activate the "RunnableProvider" and the "RunnableConsumer" (if some components are not started, start them with the "start ID#" command).

Then, proceed to install and activate the CLI_Client. Providing the "bundle 6" command (where 6 is the ID assigned to the CLI Client by the framework), we observe that the component registers the "CommandProvider" service (which is used by the framework for Command Line Interaction as described already) and it uses the "java.lang.Runnable" service, provided by the "RunnableProvider". Furthermore, we note that the bundle imports a package related to OSGi.
osgi> install file:CLI_Client.jar
Bundle id is 6

osgi> start 6

osgi> bundle 6
file:CLI_Client.jar [6]
Id=6, Status=ACTIVE Data Root=C:\eclipse\configuration\org.eclipse.osgi\bundles\6\data
Registered Services
{org.eclipse.osgi.framework.console.CommandProvider}={component.name=CLI_Client, component.id=10, service.id=30}
Services in use:
{java.lang.Runnable}={component.name=RunnableProvider, component.id=9, service.id=29}
No exported packages
Imported packages
org.eclipse.osgi.framework.console; version="1.0.0"<System Bundle [0]>
No fragment bundles
Named class space
CLI_Client; bundle-version="1.0.0"[provided]
No required bundles

osgi> test
Invoking Runnable service ...
RunnableProvider says hi!
Done!

osgi> stop 4

osgi> test
Testing failed: no Runnable service provider available
Notably, when we invoke the custom "test" command, the framework invokes the corresponding method in the "CLI_Client" component, which prints out a message ("Invoking Runnable service ..."), then invokes the service (which results to the output of "RunnableProvider says hi!", as it was specified in the second tutorial) and finally it prints out a third line ("Done!").

This means that the invocation of the service was successful, and resulted to the invocation of the "run()" method all the way from the client side, to the service side. Just to make sure that the dynamic binding works, we can try to stop the "ServiceProvider" and run the "test" command again. The former causes the unbinding of the "java.lang.Runnable" service from the clients, which results to setting it to null and thus the print-out of the "Testing failed: no Runnable service provider available" message.

Finally, to show how a service provider can serve more than one clients simultaneously, we can try starting the service back again (via the "start 4" command), and then inspect the service:
osgi> start 4

osgi> services (objectClass=java.lang*)
{java.lang.Runnable}={component.name=RunnableProvider, component.id=12, service.id=32}
Registered by bundle: file:RunnableProvider.jar [4]
Bundles using service:
file:RunnableConsumer.jar [5]
file:CLI_Client.jar [6]
In this case, we can observe that the service is registered by the "RunnableProvider" component, and is assigned an ID (32 in this case, might be different in your case). Notably, there are two bundles using the service: the "RunnableConsumer" defined in the previous tutorial and the "CLI_Client" defined in this one.

Homework 4.1: Read section 2.10 (Interacting with the Framework) in the second chapter of Neil Bartlett's book.

Homework 4.2: Write a bundle that periodically checks the contents of a directory in the filesystem. Whenever a new file with the extension ".jar" appears in that directory, print out an appropriate message.