Writing about technology, traveling, politics and more
Thursday, December 17, 2009
Friday, October 16, 2009
IntelliJ IDEA goes open-source
As a long time Java fan and Java developer, I was excited to read this blog announcement this morning. IntelliJ IDEA goes open-source with its IntelliJ IDEA Community Edition.
I started using IntelliJ IDEA while at my first real job, Eternal Systems, Inc., back in 2002. At the time, IDEA was superb and much better than any other Java IDE (probably than any IDE). I have just started playing with the community edition (version 9.0-beta) and it seems that JetBrains has kept up the good work. If you are a Java developer, I highly recommend you test out this IDE. Have a look at this video for a quick glimpse.
While individuals will certainly benefit from this release, I think JetBrains will too. In particular, I believe that programming teachers will pick up this IDE as a recommended tool for their students who make their first steps in programming. With its constant and real-time code inspections, IDEA will be a great educational resource and students will not forget it after they graduate.
Sunday, October 11, 2009
Time for Android!
One of my main resolutions for after I would get done with my PhD thesis was to get deeper into the details of the architecture and the underlying mechanisms of Android. Now that my defense was completed successfully (with only a few minor improvements pending), I am ready for what I believe will be an exciting journey.
As a researcher in software engineering for context-aware systems, Android seems to be a unique opportunity. Here is what I believe makes the Android platform stand out from the rest of the mobile platforms:
As a researcher in software engineering for context-aware systems, Android seems to be a unique opportunity. Here is what I believe makes the Android platform stand out from the rest of the mobile platforms:
- It is open. This is a huge difference from its main competition, particularly the iPhone. While the latter is an excellent platform, and probably the state-of-the-art in the current arena of smart-phone hardware, its future is a bit uncertain. As a closed platform, the iPhone might face great challenges in attracting developers and end-users when other platforms (like the Android) catch-up with it.
- Its architecture is well thought. With a layered structure that facilitates platform independence (i.e., the Linux kernel), powerful common libraries (e.g., realizing accelerated graphics and security protocols) and a runtime layer that allows development in Java (one of the most successful and widely mastered programming languages). With this, building for Android is a breeze and fun process!
- It features reusable and replaceable components. This means that anyone can develop applications that feature web-browser and map views (among others) without having to develop them. It also means that any developer is free to enter an open market and compete with others (including Google itself) on providing the best core applications, like a phone manager or contacts manager.
- It features an open market. While all registered applications must qualify a set of rules in order to be listed, these are much more relaxed and open in comparison to iPhone's market-place, which prohibits listing applications that compete with those from Apple.
- It provides good developer tools. The Android SDK is of free to download and use (as expected), while the developers can also benefit from a great deal of online resources.
- It is Google-backed. As the search giant is the core promoter in the Open Handset Alliance, the Android users can feel safe that the web-apps they are so accustomed to (like GMail, GCalendar, etc) will always be supported on their devices. Furthermore, building on their great legacy with organizing the world's information, the Android users can expect more innovative applications and functionality in their phones, assisting them to organize their own, mobile world.
I recommend the following three tutorials for anyone who needs to get a quick (but very intuitive) look into Android:
Part 1 of 3 - Architecture Overview
Part 2 of 3 - Application lifecycle
Update October 13, 2009: Just read this interesting post on Slashdot.
Labels:
android,
context-aware,
mobile platforms,
research
Tuesday, September 08, 2009
CSI Cyprus
Crime Scene Investigation (CSI) is a quite popular TV series dealing with crimes (of course) and their forensic investigation. The series is quite popular and older seasons of the franchise are broadcasted (and assumingly are popular) in Cyprus too.
Interestingly, Cyprus also serves a central role in the drama's script in episode 25 of the latest season (season 6) in CSI: New York. If you cannot wait for season 6 episode 25 to be released, then try downloading the teaser video from iTunes. You will be amused!
This reminds me of another great moment from the US pop-cult industry. In season 16 of the Simpsons, Homer shouts to a Turk captain: "... you Cyprus-splitting jerks!". I was very proud to share that in my Greek blog back then.
Speaking of Homer, I also watched the teaser for season 21 (!) of the Simpsons (also available in iTunes for free), and it seems that these guys will never run out of ideas! His Odyssey will run a really long time!
Thursday, February 12, 2009
Developing a complete context-aware application (CaMP) - Tutorial 10
This tutorial continues the series started a few weeks back, with a comprehensive example: developing the Context-aware Media Player (CaMP). Here, we describe and apply a development methodology, which details the steps required to design and implement a context-aware application using the MUSIC middleware.
Development methodology
The following steps describe a structured method for designing and implementing context-aware applications using the MUSIC Context System. It is recommended that before you try to follow this methodology, you study the Tutorials on the MUSIC Context System here.
The methodology consists of the following 6 steps:
The Context-aware Media Player is a simple application, which is defined as follows:
"CaMP is a simple media player, capable of playing audio or video media tracks, while exhibiting the following context-aware behavior:
Step 1: Identify the relevant context types; in case some context types are higher level, identify other elementary context types to build on them
The first step is to analyze the relevant context types. In this case, the task it is easy because the application's verbal description is quite straight-forward. The main context type we are interested in is "whether the user is in the room". Thus we define the User-in-the-Room context type. This is a rather high-level context type, so we need to further refine it and decide how we can derive this context type.
While multiple approaches are possible, assume the following one: a user is assumed to have entered the room when his Bluetooth smart-phone is detected while at the same time some motion is detected by the web-camera on the deployment computer. Thus, we define two additional, elementary context types: Bluetooth devices and Motion. These two are combined to infer whether the user has entered (or exited) the room where CaMP is deployed.
The hierarchy of the context types is illustarted in the following figure:

Step 2: For each elementary context type identify a “context sensor” plug-in and for each high-level context type identify a “context reasoner”
The second step is to define a context plug-in for each context type. This comes naturally from the hierarchy of the context types, as illustrated in the figure above.
To detect the attached Bluetooth devices, we define a Bluetooth Sensor plug-in.
To detect motion in the room, we define the Motion Sensor plug-in
Finally, to detect whether the user has entered or exited the room, we define a context reasoner that takes input from the other two plug-ins; the User-in-the-Room Sensor plug-in
Notably, while the first two sensors have no context input (other than what is sensed from hardware, like the Bluetooth adaptor or the web-camera), the third one depends on input context types only. Thus, the first two can be constructed as context sensors and the third one as context reasoner.
Step 3: For each context plug-in, reuse an existing implementation or proceed to construct a new one
The next step is to reuse or develop a new plug-in for each one identified. As the construction of context sensors has already been covered extensively in Tutorial 8, we will here cover just the development of the User-in-the-Room context reasoner plug-in.
At this point, it should be noted that while a context plug-in is simply required to implement the IContextPlugin interface, it is usually more convenient to extend one of the helper abstract classes which provide default code for much of the required functionality. This functionality is illustrated in the following figure:

For instance, a class extending the AbstractContextPlugin can simply implement the activate() and deactivate() methods to start and stop generating events accordingly. The generated events are delegated to the context middleware via the fireContextChangedEvent() method.
On the other hand, a class extending the AbstractContextReasonerPlugin can simply implement the contextChanged() method to receive and handle context change events. The activate() and deactivate() methods are already implemented, and automatically register the context plug-in for notification of relevant context events (as defined in the XML service descriptor). (In case you need to overload the activate() and deactivate methods, make sure that you delegate the call to the super-class as well, i.e., super.activate() or super.deactivate()).
In both the cases of extending the AbstractContextPlugin and AbstractContextReasonerPlugin, the developers should define the provided (and optionally the required) context types as it is illustrated here:
Developing the User-in-the-Room Context Reasoner Plug-in
To develop the User-in-the-Room context reasoner plug-in, we also follow the steps defined in the methodology.
Step 3.1: Implement (or reuse) the corresponding context model
In this step, we need to define the model for the User-in-the-Room context type. As such, we first identify the required context values. In this case, we can simply abstract this type of information with a simple boolean. Following a process like the one described in Tutorial 7, we conclude to a model as illustrated in the following figure.

Step 3.2: Define the MANIFEST and XML descriptor of the Context Plug-in
The MANIFEST for the context reasoner plug-in is defined just like it is defined for any other OSGi component. In our case, the resulting file is illustrated in the following:
Step 3.3: Extend the abstract context sensor or context reasoner
As the User-in-the-Room has been identified as a context reasoner, we extend the AbstractContextReasonerPlugin class. The code of the UserInTheRoomSensor class is illustrated in the following:
Step 3.4: Implement the "activate" and "deactivate" methods to start and stop the generation of context events respectively
In this case, the two methods (activate and deactivate) are not very significant because the context plug-in is triggered by input context events arriving at the contextChanged() method (rather than timed events from a scheduler, like those defined Tutorial 8 for example).
The only noteworthy observation is that the deactivate() method is over-ridden (to initialize some local variables) but the delegation is forwarded to the super class anyway. This is important, as it was discussed already, because it allows for the automatic registration and unregistration with the context middleware.
Step 3.5: Implement the "contextChanged" method to handle received context events [optional step for context reasoners]
As it was already argued, the most interesting part of this class is in the contextChanged() method. In here, received events are analyzed to infer whether the detected motion and the Bluetooth's presence (or absense) should trigger an event. Once it is decided that an event needs to be triggered, the fireContextChangedEvent helper method is invoked.
Step 3.6: Pack everything in a JAR file, as per the OSGi’s bundle packaging specification
This is a mandatory step for every OSGi component. The process to package the plug-in has been described in detail in the previous tutorials.
This concludes the development of the User-in-the-Room context reasoner plug-in. It is assumed that the other two plug-ins are already implemented, and we resume with the development of the CaMP application.
Step 4: Develop the functional aspects of your application
This step has no significant details. Just like in usual software development, we try to reuse existing libraries as much as possible. For example, in the case of the CaMP application, we reuse the Java Media Framework (JMF) [1] libraries by SUN in order to avoid having to develop complex media handling code ourselves.
What is important to notice however, is that with this approach it is possible to build your application with Separation of Concerns [2]. At first, you develop the functional aspect of your application (the media playback code and the GUI in this case) and then you hook-up the context-aware behavior (see next step).
Step 5: Register your application to the relevant context types and implement the code that adapts the application accordingly
To bind a client to the context middleware, one need to follow these steps:
To enable automatic context access (using Declarative Services), the developer needs to define a reference to the IContextAccess service in the service descriptor. For example, consider the service descriptor of the CaMP application:

Finally, the actual code where the binding to the context middleware and the handling of the context events takes place is illustrated here in the following:
The actual context-aware logic is very simple: The received events are analyzed and when an event corresponds to the user entering the room, then the media playback is resumed. Otherwise, if the received event corresponds to the user exiting the room, then the media playback is paused.
Step 6: Pack your application in JAR file as per OSGi's bundle packaging specification
Of course, once the code and the artifacts are finalized, they are all compiled and packaged together in a JAR file, according to the OSGi's packaging specification.
JARs of the used plug-ins, including the source code and an ANT build script, are available here:
References
[1]. SUN Microsystems, Java Media Framework (JMF), http://java.sun.com/javase/technologies/desktop/media/jmf
[2]. Nearchos Paspallis, Frank Eliassen, Svein Hallsteinsen, and George A. Papadopoulos, Developing Self-Adaptive Mobile Applications and Services with Separation-of-Concerns, At Your Service: Service-Oriented Computing from an EU Perspective, E. Di Nitto, A-M. Sassen, O. Traverso and A. Zwegers (eds), MIT Press, June 2009, chapter 6, pp. 129-158
Development methodology
The following steps describe a structured method for designing and implementing context-aware applications using the MUSIC Context System. It is recommended that before you try to follow this methodology, you study the Tutorials on the MUSIC Context System here.
The methodology consists of the following 6 steps:
- Identify the relevant context types; in case some context types are higher-level, identify other elementary context types to build on them
- For each elementary context type identify a “context sensor” plug-in and for each high-level context type identify a “context reasoner”
- For each context plug-in, reuse an existing implementation or proceed to construct a new one
- Develop the functional aspects of your application
- Register your application to the relevant context types and implement the code that adapts the application accordingly
- Pack your application in JAR file as per OSGi's bundle packaging specification
- Implement (or reuse) the corresponding context model
- Define the MANIFEST and XML descriptor of the “context plug-in”
2.1. Define the standard properties like you would do for an OSGi component
2.2. Define the “provided” context types
2.3. Define the “required” context types [optional step for context reasoners] - Extend the abstract context sensor or context reasoner
- Implement the “activate” and “deactivate” methods to start and stop the generation of context events respectively
- Implement the “contextChanged” method to handle received context events [optional step for context reasoners]
- Pack everything in a JAR file, as per the OSGi’s bundle packaging specification
The Context-aware Media Player is a simple application, which is defined as follows:
"CaMP is a simple media player, capable of playing audio or video media tracks, while exhibiting the following context-aware behavior:
- When the user is detected as entering the room (where CaMP is deployed), then resume playback
- When the user is detected as exiting the room, then pause playback"
Step 1: Identify the relevant context types; in case some context types are higher level, identify other elementary context types to build on them
The first step is to analyze the relevant context types. In this case, the task it is easy because the application's verbal description is quite straight-forward. The main context type we are interested in is "whether the user is in the room". Thus we define the User-in-the-Room context type. This is a rather high-level context type, so we need to further refine it and decide how we can derive this context type.
While multiple approaches are possible, assume the following one: a user is assumed to have entered the room when his Bluetooth smart-phone is detected while at the same time some motion is detected by the web-camera on the deployment computer. Thus, we define two additional, elementary context types: Bluetooth devices and Motion. These two are combined to infer whether the user has entered (or exited) the room where CaMP is deployed.
The hierarchy of the context types is illustarted in the following figure:
Step 2: For each elementary context type identify a “context sensor” plug-in and for each high-level context type identify a “context reasoner”
The second step is to define a context plug-in for each context type. This comes naturally from the hierarchy of the context types, as illustrated in the figure above.
To detect the attached Bluetooth devices, we define a Bluetooth Sensor plug-in.
To detect motion in the room, we define the Motion Sensor plug-in
Finally, to detect whether the user has entered or exited the room, we define a context reasoner that takes input from the other two plug-ins; the User-in-the-Room Sensor plug-in
Notably, while the first two sensors have no context input (other than what is sensed from hardware, like the Bluetooth adaptor or the web-camera), the third one depends on input context types only. Thus, the first two can be constructed as context sensors and the third one as context reasoner.
Step 3: For each context plug-in, reuse an existing implementation or proceed to construct a new one
The next step is to reuse or develop a new plug-in for each one identified. As the construction of context sensors has already been covered extensively in Tutorial 8, we will here cover just the development of the User-in-the-Room context reasoner plug-in.
At this point, it should be noted that while a context plug-in is simply required to implement the IContextPlugin interface, it is usually more convenient to extend one of the helper abstract classes which provide default code for much of the required functionality. This functionality is illustrated in the following figure:
For instance, a class extending the AbstractContextPlugin can simply implement the activate() and deactivate() methods to start and stop generating events accordingly. The generated events are delegated to the context middleware via the fireContextChangedEvent() method.
On the other hand, a class extending the AbstractContextReasonerPlugin can simply implement the contextChanged() method to receive and handle context change events. The activate() and deactivate() methods are already implemented, and automatically register the context plug-in for notification of relevant context events (as defined in the XML service descriptor). (In case you need to overload the activate() and deactivate methods, make sure that you delegate the call to the super-class as well, i.e., super.activate() or super.deactivate()).
In both the cases of extending the AbstractContextPlugin and AbstractContextReasonerPlugin, the developers should define the provided (and optionally the required) context types as it is illustrated here:
<?xml version="1.0"?>
<component name="UserInTheRoom" immediate="true">
<implementation class="cy.ac.ucy.cs.osgi.context.user_in_the_room.plugin.UserInTheRoomSensor"/>
<service>
<provide interface="org.istmusic.mw.context.plugins.IContextPlugin"/>
</service>
<property name="PROVIDED_CONTEXT_TYPES" value="user-in-the-room"/>
<property name="PROVIDED_CONTEXT_TYPE_ENTITY[user-in-the-room]" value="#concepts.entities.environment|room"/>
<property name="PROVIDED_CONTEXT_TYPE_SCOPE[user-in-the-room]" value="#concepts.scopes.abstract.user_in_the_room"/>
<property name="REQUIRED_CONTEXT_TYPES" value="motion bluetooth"/>
<property name="REQUIRED_CONTEXT_TYPE_ENTITY[motion]" value="#concepts.entities.environment|room"/>
<property name="REQUIRED_CONTEXT_TYPE_SCOPE[motion]" value="#concepts.scopes.abstract.motion_detected"/>
<property name="REQUIRED_CONTEXT_TYPE_ENTITY[bluetooth]" value="#concepts.entities.device|this"/>
<property name="REQUIRED_CONTEXT_TYPE_SCOPE[bluetooth]" value="#concepts.scopes.resources.network.bluetooth"/>
</component>
In practice, you first define some aliases for each provided (or required) context type, and then you define the entity and the scope for each one of those accordingly.Developing the User-in-the-Room Context Reasoner Plug-in
To develop the User-in-the-Room context reasoner plug-in, we also follow the steps defined in the methodology.
Step 3.1: Implement (or reuse) the corresponding context model
In this step, we need to define the model for the User-in-the-Room context type. As such, we first identify the required context values. In this case, we can simply abstract this type of information with a simple boolean. Following a process like the one described in Tutorial 7, we conclude to a model as illustrated in the following figure.
Step 3.2: Define the MANIFEST and XML descriptor of the Context Plug-in
The MANIFEST for the context reasoner plug-in is defined just like it is defined for any other OSGi component. In our case, the resulting file is illustrated in the following:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: User-in-the-room sensor context plugin
Bundle-SymbolicName: UserInTheRoomSensor
Bundle-Version: 1.0.0
Bundle-ClassPath: .
Import-Package: org.istmusic.mw.context.plugins,
org.istmusic.mw.context.events,
org.istmusic.mw.context.util.scheduler,
org.istmusic.mw.context.model.api,
org.istmusic.mw.context.model.impl,
org.istmusic.mw.context.model.impl.values,
org.istmusic.mw.context.ontologies,
cy.ac.ucy.cs.osgi.context.bluetooth.model,
cy.ac.ucy.cs.osgi.context.motion.model,
javax.swing
Export-Package: cy.ac.ucy.cs.osgi.context.user_in_the_room.model
Service-Component: OSGI-INF/UserInTheRoomSensor.xml
Next, the service descriptor is defined. As it was discuess in the previous paragraphs, this step requires that the provided and required context types are explicitly defined. In the case of the User-in-the-Room plug-in, the service descriptor was already illustrated in the previous paragraphs.Step 3.3: Extend the abstract context sensor or context reasoner
As the User-in-the-Room has been identified as a context reasoner, we extend the AbstractContextReasonerPlugin class. The code of the UserInTheRoomSensor class is illustrated in the following:
package cy.ac.ucy.cs.osgi.context.user_in_the_room.plugin;
import org.istmusic.mw.context.plugins.AbstractContextReasonerPlugin;
import org.istmusic.mw.context.events.ContextChangedEvent;
import org.istmusic.mw.context.model.api.IContextElement;
import cy.ac.ucy.cs.osgi.context.motion.model.MotionContextElement;
import cy.ac.ucy.cs.osgi.context.bluetooth.model.BluetoothContextElement;
import cy.ac.ucy.cs.osgi.context.user_in_the_room.model.UserInTheRoomContextElement;
public class UserInTheRoomSensor extends AbstractContextReasonerPlugin
{
public static final String PLUGIN_ID = "User-in-the-room Sensor";
public static final double THRESHOLD = 0.12d;
public static final long MOTION_VALIDITY_PERIOD = 15000L; // 15 seconds
public UserInTheRoomSensor()
{
super(PLUGIN_ID);
}
private boolean userInTheRoom = false;
private boolean userBluetoothDeviceAttached = false;
public void deactivate()
{
userInTheRoom = false;
userBluetoothDeviceAttached = false;
super.deactivate();
}
private long lastMotionTimestamp = 0L;
public void contextChanged(ContextChangedEvent event)
{
IContextElement [] contextElements = event.getContextDataset().getContextElements();
for(int i = 0; i < contextElements.length; i++)
{
final IContextElement contextElement = contextElements[i];
if(contextElement instanceof MotionContextElement)
{
lastMotionTimestamp = System.currentTimeMillis();
}
else if (contextElement instanceof BluetoothContextElement)
{
BluetoothContextElement bluetoothContextElement = (BluetoothContextElement) contextElement;
userBluetoothDeviceAttached = bluetoothContextElement.contains(UserInTheRoomVisualComponent.DEFAULT_USER_DEVICE_ID);
}
// else ignore
}
final boolean motionDetectedRecently = lastMotionTimestamp + MOTION_VALIDITY_PERIOD > System.currentTimeMillis();
if(motionDetectedRecently && userBluetoothDeviceAttached)
{
if(!userInTheRoom)
{
userInTheRoom = true;
UserInTheRoomContextElement userInTheRoomContextElement = new UserInTheRoomContextElement(PLUGIN_ID, userInTheRoom);
fireContextChangedEvent(this, userInTheRoomContextElement);
}
}
else if(!userBluetoothDeviceAttached)
{
if(userInTheRoom)
{
userInTheRoom = false;
UserInTheRoomContextElement userInTheRoomContextElement = new UserInTheRoomContextElement(PLUGIN_ID, userInTheRoom);
fireContextChangedEvent(this, userInTheRoomContextElement);
}
}
}
}
The most interesting part of this class is the implementation of the contextChanged() method. More details about this code is provided in the following paragraphs.Step 3.4: Implement the "activate" and "deactivate" methods to start and stop the generation of context events respectively
In this case, the two methods (activate and deactivate) are not very significant because the context plug-in is triggered by input context events arriving at the contextChanged() method (rather than timed events from a scheduler, like those defined Tutorial 8 for example).
The only noteworthy observation is that the deactivate() method is over-ridden (to initialize some local variables) but the delegation is forwarded to the super class anyway. This is important, as it was discussed already, because it allows for the automatic registration and unregistration with the context middleware.
Step 3.5: Implement the "contextChanged" method to handle received context events [optional step for context reasoners]
As it was already argued, the most interesting part of this class is in the contextChanged() method. In here, received events are analyzed to infer whether the detected motion and the Bluetooth's presence (or absense) should trigger an event. Once it is decided that an event needs to be triggered, the fireContextChangedEvent helper method is invoked.
Step 3.6: Pack everything in a JAR file, as per the OSGi’s bundle packaging specification
This is a mandatory step for every OSGi component. The process to package the plug-in has been described in detail in the previous tutorials.
This concludes the development of the User-in-the-Room context reasoner plug-in. It is assumed that the other two plug-ins are already implemented, and we resume with the development of the CaMP application.
Step 4: Develop the functional aspects of your application
This step has no significant details. Just like in usual software development, we try to reuse existing libraries as much as possible. For example, in the case of the CaMP application, we reuse the Java Media Framework (JMF) [1] libraries by SUN in order to avoid having to develop complex media handling code ourselves.
What is important to notice however, is that with this approach it is possible to build your application with Separation of Concerns [2]. At first, you develop the functional aspect of your application (the media playback code and the GUI in this case) and then you hook-up the context-aware behavior (see next step).
Step 5: Register your application to the relevant context types and implement the code that adapts the application accordingly
To bind a client to the context middleware, one need to follow these steps:
- Define your client as an OSGi component
- Define a service descriptor with a reference to the “IContextAccess” service
- Implement the “IContextListener” interface and define the code that will handle the asynchronous context notifications
- Pack everything in a JAR file, as per the OSGi’s bundle packaging specification
To enable automatic context access (using Declarative Services), the developer needs to define a reference to the IContextAccess service in the service descriptor. For example, consider the service descriptor of the CaMP application:
<?xml version="1.0"?>
<component name="CaMP">
<implementation class="cy.ac.ucy.cs.camp.CaMP"/>
<reference name="context_access"
interface="org.istmusic.mw.context.IContextAccess"
bind="setContextAccess"
unbind="unsetContextAccess"
cardinality="0..1"
policy="dynamic"/>
</component>
As it is illustrated in this descriptor, the IContextAccess service is referenced and appropriate binding methods are defined. The service descriptor is of course referenced by a Manifest file, as illustrated in the following figure:Finally, the actual code where the binding to the context middleware and the handling of the context events takes place is illustrated here in the following:
package cy.ac.ucy.cs.camp;
import org.osgi.service.component.ComponentContext;
import org.istmusic.mw.context.IContextAccess;
import org.istmusic.mw.context.plugins.IContextPlugin;
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 org.istmusic.mw.context.model.api.IValue;
import org.istmusic.mw.context.model.impl.values.BooleanValue;
import javax.swing.*;
import java.util.logging.Logger;
import java.awt.*;
public class CaMP extends JFrame implements IContextListener
{
public static final String DISCONNECTED = "CaMP - Disconnected from the context middleware";
public static final String CONNECTED = "CaMP - Connected to the context middleware";
public static final IEntity ENTITY = DefaultMusicOntologyV0_1.ENTITY_ENVIRONMENT_ROOM;
public static final IScope SCOPE = DefaultMusicOntologyV0_1.SCOPE_ABSTRACT_USER_IN_THE_ROOM;
private final Logger logger = Logger.getLogger(CaMP.class.getCanonicalName());
private final MediaPlayer mediaPlayer;
private final UnderTheHoodPanel underTheHoodPanel;
private final AboutPanel aboutPanel;
public CaMP()
{
super("CaMP");
setLayout(new BorderLayout());
final JTabbedPane tabbedPane = new JTabbedPane();
add(tabbedPane, BorderLayout.CENTER);
mediaPlayer = new MediaPlayer();
tabbedPane.add("Media player", mediaPlayer.getVisualComponent());
underTheHoodPanel = new UnderTheHoodPanel();
tabbedPane.add("Under the hood", new JScrollPane(underTheHoodPanel));
aboutPanel = new AboutPanel();
tabbedPane.add("About", aboutPanel);
pack();
tabbedPane.updateUI();
setSize(400, 300);
setVisible(false);
}
protected void activate(ComponentContext componentContext)
{
logger.info("CaMP: activate");
setVisible(true);
}
protected void deactivate(ComponentContext componentContext)
{
logger.info("CaMP: deactivate");
// make sure the media player is stopped
mediaPlayer.stop();
setVisible(false);
}
public void setContextAccess(IContextAccess contextAccess)
{
logger.info("CaMP: setContextAccess");
try
{
contextAccess.addContextListener(ENTITY, SCOPE, this);
setTitle(CONNECTED);
}
catch (ContextException ce)
{
logger.severe(ce.getMessage());
}
}
public void unsetContextAccess(IContextAccess contextAccess)
{
logger.info("CaMP: unsetContextAccess");
try
{
contextAccess.removeContextListener(ENTITY, SCOPE, this);
setTitle(DISCONNECTED);
}
catch (ContextException ce)
{
logger.severe(ce.getMessage());
}
}
public void contextChanged(ContextChangedEvent event)
{
final IContextElement[] contextElements = event.getContextDataset().getContextElements();
for(int i = 0; i < contextElements.length; i++)
{
final IContextElement contextElement = contextElements[i];
IValue value = contextElement.getContextData().getContextValue(
DefaultMusicOntologyV0_1.SCOPE_ABSTRACT_USER_IN_THE_ROOM).getValue();
BooleanValue booleanValue = (BooleanValue) value;
if(booleanValue.getBooleanValue().booleanValue())
{
mediaPlayer.start();
}
else
{
mediaPlayer.stop();
}
}
}
}
Just like in the context reasoner plug-ins, the most interesting part here is the implementation of the contextChanged method, defined in the IContextListener interface.The actual context-aware logic is very simple: The received events are analyzed and when an event corresponds to the user entering the room, then the media playback is resumed. Otherwise, if the received event corresponds to the user exiting the room, then the media playback is paused.
Step 6: Pack your application in JAR file as per OSGi's bundle packaging specification
Of course, once the code and the artifacts are finalized, they are all compiled and packaged together in a JAR file, according to the OSGi's packaging specification.
JARs of the used plug-ins, including the source code and an ANT build script, are available here:
- CaMP.jar
- BluetoothSensor.jar
- MotionSensor.jar
- UserInTheRoomSensor.jar
- JMFBundle.jar (needed by the CaMP and the MotionSensor bundles)
[1]. SUN Microsystems, Java Media Framework (JMF), http://java.sun.com/javase/technologies/desktop/media/jmf
[2]. Nearchos Paspallis, Frank Eliassen, Svein Hallsteinsen, and George A. Papadopoulos, Developing Self-Adaptive Mobile Applications and Services with Separation-of-Concerns, At Your Service: Service-Oriented Computing from an EU Perspective, E. Di Nitto, A-M. Sassen, O. Traverso and A. Zwegers (eds), MIT Press, June 2009, chapter 6, pp. 129-158
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:
Finally, we are ready to present the code of the Memory Viewer context client:
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:
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
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.
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:- First, the constructor of the memory viewer bundle is invoked.
- 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).
- 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.
- 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.
- Finally, the memory viewer (bundle 10) receives the corresponding events and prints out to the console the value of the received MemoryContextElement.
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:

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:

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:
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.
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:
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:
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:
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:
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
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:
- 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.
- 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.
- 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).
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.
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").
- 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.
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).
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:
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".
For example, when a user with an email address of "nearchos@cs.ucy.ac.cy" is logged in the system, then the following entity:
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.
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:

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:
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:
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:

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.
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:
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
The context model is used to enable the following features:
- 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].
- 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.
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
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
- #concepts.entities.user|nearchos@cs.ucy.ac.cy
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
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.
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"
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:
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
Labels:
context model,
data structures,
ontology,
semantics
Subscribe to:
Comments (Atom)