Saturday, October 25, 2008

iPOJO 1.0.0 and the future

I recently released the version 1.0.0 of the iPOJO framework. This release is a major milestone for iPOJO.

iPOJO was created two years ago in the continuation of the Service Binder effort. The initial goal was to provide an easy way to create dynamic service-based applications on the top of OSGi without losing the OSGi philosophy (small, universal middleware).
At the same time, I was involved in several projects where I realized that iPOJO must focus on different goals:
  • Must be simple… (Avoids redundancies, provides annotations)
  • Must be extensible to tackle specific requirements
  • Must provide a way to design applications
The first requirement comes from the OSGi development model. OSGi is very powerful, but the learning curve is slow and long. A lot of mechanisms (mostly about class loading and threads) must be understood. Hiding or simplifying those mechanisms was a stringent requirement.

The first project using iPOJO was the design and the implementation of a residential gateway. However, we quickly understood that developing such kind of application requires some specific technical services such as a MOM allowing event-based interactions, a scheduler (i.e. Cron) to automate periodic task triggering, a way to administrate applications remotely… Providing such technical services is not too difficult. However, automating the interaction with those technical services was more challenging. From this observation, we decide to provide an extension mechanism allowing adding such kind of features without modifying the core of the project. So, iPOJO can be small as well as providing a lot of functionalities.

Finally, we realize that we need a way to design applications. Traditional ways to design applications are somewhat limited. Generally they are limited to static applications (with no possible evolution at runtime) and the architecture description is lost just after that the application is deployed. After a long work with Richard S. Hall, we propose and implement a new way to design applications on the top of OSGi. iPOJO composite allows isolating services, support dynamism … This is the most innovative part of the iPOJO project.
The iPOJO 1.0.0 release is the result of these two years of development, research, interrogations and experimentations. The iPOJO framework is composed by:
  • A core system providing basics functionalities (requiring/providing services, lifecycle, instance introspections…)
  • A full integration with the Configuration Admin
  • A way to design and execute dynamic applications
  • Several external handler extending core capabilities with JMX administration, Event Admin interactions, Temporal service dependency, whiteboard and extender patterns …
The future of iPOJO is also exciting. With the support of my ‘future former’ group, extensions such as distribution and deployment are under development. The distribution framework allows iPOJO instances to interact with remote services (using any protocol) and can be exposed remotely. The deployment support computes OBR descriptions from metadata providing features easing the deployment. Improvements such as providing a control and creation API are also investigated. The future version will see the arriving of new ‘handlers’ handling persistence and scheduling as well as a better integration with the junit4osgi framework.

All those stuffs are really promising… Isn’t it?

Tuesday, October 21, 2008

iPOJO on Android

Android is the Google OS for mobile phone (and the future GPhone). Android provides a Java-like virtual machine: Dalvik.

So, why not trying to execute an iPOJO-based application on the top of Android?
The idea was to embed an OSGi/iPOJO framework on Android and then to deploy an application on the top of the framework.

You can download the application in two parts:
the Android application embedding Felix/iPOJO : here
the SpellChecker application : here

The application is designed as follow:
  • The Android application creates and starts a OSGI/iPOJO framework (Felix + iPOJO + FileInstall)
  • The Android application tracks a ViewFactory service. This service is used to display the application GUI (the service allows creating the GUI main component).
  • The SpellChecker application (10 minutes tutorial) is deployed on OSGi, and use iPOJO. The only difference with the one form the 10 minutes tutorial is the GUI that uses Android HMI component (rather than Swing)
SpellChecker application bundles are deployed thanks to FileInstall. A specific folder is monitored in order to install/update/uninstall jar files contained in this folder.


Embedding Felix and iPOJO inside an Android Application


First, we need to create an Android application. This application creates a new Felix when the application is instantiated:

public synchronized void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
PrintStream out = new PrintStream(new OutputStream(){
ByteArrayOutputStream output = new ByteArrayOutputStream();
@Override
public void write(int oneByte) throws IOException {
output.write(oneByte);
if (oneByte == '\n') {
Log.v("out", new String(output.toByteArray()));
output = new ByteArrayOutputStream();
}
}});
System.setErr(out);
System.setOut(out);
m_configMap = new StringMap(false);
m_configMap.put(FelixConstants.LOG_LEVEL_PROP,String.valueOf(Logger.LOG_DEBUG));
m_configMap.put(DirectoryWatcher.DEBUG, "1");
// Configure the Felix instance to be embedded.
m_configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true");
File bundles = new File(FELIX_BUNDLES_DIR);
if (!bundles.exists()) {
if (!bundles.mkdirs()) {
throw new IllegalStateException("Unable to create bundles dir");
}
}
m_configMap.put(DirectoryWatcher.DIR, bundles.getAbsolutePath());
// Add core OSGi packages to be exported from the class path
// via the system bundle.
m_configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, ANDROID_FRAMEWORK_PACKAGES);
// Explicitly specify the directory to use for caching bundles.
try {
m_cache = File.createTempFile("felix-cache", null);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
m_cache.delete();
m_cache.mkdirs();
m_configMap.put(BundleCache.CACHE_PROFILE_DIR_PROP, m_cache.getAbsolutePath());
}

When the application starts, the Felix Framework is started with two Activators :
  • one installing bundles from resources
  • one installing bundles from a folder (FileInstall)
Then a service tracker is used to track arrivals and departures of the ViewFactory (provided by the SpellChecker GUI).

public synchronized void onStart() {
super.onStart();
setContentView(new View(this));
Resources res = getResources();
try {
List<BundleActivator> activators = new ArrayList<BundleActivator>();

// Plugs the bundle installer (install bundle from application resources)
activators.add(new Installer(res));
// Plugs the FileInstall activator
activators.add(new FileInstall());

m_felix = new Felix(m_configMap, activators);
m_felix.start();
} catch (BundleException ex) {
throw new IllegalStateException(ex);
}
try {
m_tracker = new ServiceTracker(m_felix.getBundleContext(),
m_felix.getBundleContext().createFilter("(" + Constants.OBJECTCLASS
+ "=" + ViewFactory.class.getName() + ")"),
new ServiceTrackerCustomizer() {

@Override
public Object addingService(ServiceReference ref) {
System.out.println("======= Service found !");
final ViewFactory fac =
(ViewFactory) m_felix.getBundleContext().getService(ref);
if (fac != null) {
runOnUiThread(new Runnable() {
public void run() {
setContentView(fac.create(ApacheFelix.this));
}
});
}
return fac;
}

@Override
public void modifiedService(ServiceReference ref,
Object service) {
removedService(ref, service);
addingService(ref);
}

@Override
public void removedService(ServiceReference ref,
Object service) {
m_felix.getBundleContext().ungetService(ref);
runOnUiThread(new Runnable() {
public void run() {
setContentView(new View(ApacheFelix.this));
}
});
}});
m_tracker.open();
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
}

Note that the iPOJO bundle is deployed from application resource. The bundle was “dexed” and placed in the res/raw folder of the application project. Then, you can load the bundle with:

public void start(BundleContext arg0) throws Exception {

InputStream is = res.openRawResource(R.raw.ipojo);

Bundle bundle = arg0.installBundle(IPOJO_HTTP_PATH, is);
bundle.start();
}
Before the be installed, the application must be exported in a .apk file.
SpellChecker application
The application is basically the same than the one form the 10 minutes tutorial. There are two differences:
  • Bundles are “dexed”
  • The GUI bundle is implemented with Android GUI components
Another difference is about the optionality of the dependency on SpellChecker service. This dependency is now optional, and use two bind/unbind methods:
public synchronized void bindSpellChecker(SpellChecker sp) {
checker = sp;


if (activity != null) {
activity.runOnUiThread(new Runnable() {
public void run() {
// Enable button
m_button.setEnabled(true);
m_result.setText("Enter words, and click on 'check'");
m_main.invalidate();
System.out
.println("==> Spell checker GUI receives
a new spell checker ...");
}
});
}
}

public synchronized void unbindSpellChecker(SpellChecker sp) {
checker = null;
if (activity != null) {
activity.runOnUiThread(new Runnable() {
public void run() {
// Enable button
m_button.setEnabled(false);
m_result.setText("No Spellchecker available");
m_main.invalidate();
System.out
.println("=====> Spell checker GUI was
unbind from the spell checker");
}
});
}

Note that GUI modifications MUST be done in the UI Thread.


Starting the emulator


The application uses a SD card storage. So creates an SDCard iso:

mksdcard size filename

Then, launch the emulator (emulator must be accessible from your path) with:

emulator -shell -sdcard dev/android/sdcard1.iso

The emulator starts. When the shell is ready, change the permission access on /data/dalvik-cache:

chmod 777 /data/dalvik-cache

These permissions are needed to correctly load bundles.

Installing the application


To install the application (the .apk file), I use the Eclipse plugin (the apk file can be unsigned) or with the adb command (the apk file must be signed).
Once signed, you can deploy your application with:

adb install application.apk

Once installed, the application is available in the Android emulator menu

If you launch the application, a black screen appears … The Spellchecker application is not deployed!
So, to deploy the application, the bundles files must be placed in the /data/felix/bundles folder.
adb push SpellCheckGui\ for\ Android.jar /data/felix/bundles/SpellcheckGui.jar gui.jar
cd bundles
adb push spell.services.jar /data/felix/bundles/spell.services.jar

If you launch the application, the GUI appears, but you can’t use the check button. No spell checker services are available. Push the others jars in the same folder.
adb push spell.checker.jar \
/data/felix/bundles/spell.checker.jar
adb push spell.english.jar \
/data/felix/bundles/spell.english.jar

Once done, the check button becomes available. If you remove a the English dictionary, the check button becomes disabled:
adb shell rm /data/felix/bundles/spell.english.jar
adb push spell.english.jar /data/felix/bundles/spell.english.jar


Conclusion


This very simple application has shown that it is possible to create dynamic applications on the top of Android with OSGi and iPOJO. It sounds really promising …



References