Creating a Domain Specific Language for OSGi

Tags:
Probably the most interesting language currently is Scala, "a general purpose programming language designed to express common programming patterns in a concise, elegant, and type-safe way". Scala combines the object-oriented and functional programming paradigms. Besides several very interesting programming constructs, Scala provides a 100% interoperability with Java. Scala code can instantiate Java classes, call methods, etc. Since Java code can also access classes written in Scala, application written in Scala can easily use framework-style libraries where the framework needs to use callbacks provided by the user. In fact, the Scala compiler even generates Java byte code. So at runtime there is no distinction between code written in Java and code written in Scala.

Whereas Scala is probably the most interesting language, the OSGi Technology provides a good candidate for the best framework within the Java system ;-) So what would be more obvious than looking at a combination of them? Of course, since Scala generates Java byte code and the OSGi framework is dedicated to Java this is already possible. Neil Bartlett wrote a nice introduction on how to build an OSGi bundle in Scala.

However, we can do even better! The Scala language syntax provides several mechanisms to create a domain specific language. Lets start with a simple bundle activator written in Scala:
class MyBundleActivator extends BundleActivator {
def start(context: BundleContext) {}
def stop(context: BundleContext) {}
}
Nothing special here. Now lets assume we have the following definitions of a service interface and implementation:
trait SuperService {
def msg: String
}

class SuperServiceImpl extends SuperService {
def msg = "My super message!"
}
To add an instance of the service implementation to the service registry we normally would write:
context.registerService(classOf[SuperService].getName, new SuperServiceImpl, null)
But with some work and leveraging the syntactic elements of Scala, we can write:
context service classOf[SuperService] add new SuperServiceImpl
This doesn't save a lot of typing but is already much more readable. A nice side effect is, that the compiler ensures that the implementation is a subclass of the interface. So this statement would not compile:
context service classOf[SuperService] add new String("A fake super service")
Using this kind of syntax really pays off if we want to access a service:
context service classOf[SuperService] get {s =>
println(s.msg)
}
Again, the compiler ensures that everything is correctly typed. Thanks to the type inference mechanism, we do not need to declare that the type of variable s is SuperService. So we can directly call s.msg and pass it to the println methods. If we would have tried to call a nonexistent method, e.g. msg2, the compiler would have thrown an error. The Java equivalent to the above code is:
ServiceReference ref = context.getServiceReference(SuperService.class);
if (ref != null) {
SuperService s = (SuperService) context.getService(ref);
System.out.prinln(s.msg);
context.ungetService(ref);
}
While the DSL version is much more readable and less error-prone, it also has the advantage of following the RAII design pattern. We do not need to do the null-check and the DSL library ensures to "unget" the service.

So how does this work? First, lets remove some of Scala's syntactic sugar: In Scala, a method that only has one parameter can also be used as an infix operator. So
obj.method(parameter)
can be used as
obj method parameter
If we reverse this rule to the DSL version for using a service we get
context.service(classOf[SuperService]).get({s =>
println(s.msg)
})
The get method is a higher-order function that passes the reference to the service to a closure. You might find this in future versions of Java (BGGA, CICE, FCM, C3S). More interesting however, is the context.service(...) part of the statement. context is an instance of the BundleContext class that clearly does not have a service(...) method. The trick here is to use an implicit conversion from BundleContext to RichBundleContext. The RichBundleContext has the method service(...) which in turn returns an instance of the class ServiceInformation. This class provides the add and get methods:
class RichBundleContext(context: BundleContext) {
def service[T](s: Class[T]) = new ServiceInformation[T](context, s)
}

class ServiceInformation[T](...) {
def add(...) = ...
def get(...) = ...
}

To convert the BundleContext instance to an RichBundleContext we could either use the constructor directly or we could use a factory method:

// constructor
new RichBundleContext(context).service(classOf[SuperService]).get({s =>
println(s.msg)})

// factory method
implicit def bc2rbc(bc: BundleContext): RichBundleContext = {
new RichBundleContext(bc)
}
bc2rbc(context).service(classOf[SuperService]).get({s => println(s.msg)})

You may have noticed that the bc2rbc method is marked "implicit". So as long as this method is in the local scope (e.g. imported) and "implicit", Scala will automatically apply this method. So context.service(...) automatically becomes bc2rbc(context).service(...). Several conditions must match to make this happen. Please take a look at the Scala documentation for more informations.

There are several use-cases for a custom DSL in OSGi:

// sending events
context event "APP/TOPIC" send new MyEvent(...)

// receiving events
context event "APP/TOPIC" receive match {
case MyEvent(e) => ...
case _ => ...
}

// Wire admin
context wire classOf[MyType] connect new MyReceiver


What do you think? Which cases would you like to see covered by a DSL?

 

Average: 5 (2 votes)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

pauls replied on Mon, 2008/03/10 - 8:01am

In general, I think this is an interesting idea, however, somewhat obsolete due to the current IOC frameworks around. iPOJO, DS, DependencyManager, and Spring-DM do go a long way when it comes to avoiding direct interaction with the OSGi service registry. Regarding your last examples, iPOJO does a pretty good job in doing all of this and more declaratively - might be interesting to see whether iPOJO's xml approach could be turned into a more readable DSL?

As an aside, your java code for context service classOf[SuperService] add new String("A fake super service") actually does have the potential for a NPE due to the fact that context.getService(ServiceReference) is allowed to return null if the service is not available anymore :-)

Roman Roelofsen replied on Mon, 2008/03/10 - 8:50am in response to: pauls

Yes, there is some overlap with iPOJO, DS, etc when it comes to an interaction with the service registry. However, there are still several use-cases where you need this direct interaction. For example if you want to register a service in the case of certain events. Additionally, a DSL is helpful whenever you need to use an API, e.g.:

context event "TOPIC" ! new MyEvent

is much more readable than doing an event service lookup, null check, sending, "ungetting", ...

Of course, this DSL is an alternative to the XML approach of Spring, DS, etc. But because its dedicated to OSGi I would always prefer the DSL rather than "yet another XML syntax". Additionally, I get type-checking for free and can validate my configuration a priori.

This DSL does not need to be a competitor to iPOJO. Instead, iPOJO could offer a configuration in XML and DSL. Since iPOJO adds more to OSGi than just the configuration (e.g. the component model) I really would like to see a combination.

Regarding your comment on a possible NPE, I assume you meant the Java code for

context service classOf[SuperService] get {s =>
  println(s.msg)
}

And yes, thanks for pointing this out. So the Java equivalent would be even longer than the DSL version!

 

Clement Escoffier replied on Mon, 2008/03/10 - 9:52am

First, to improve Karl's comment ( ;-) ), iPOJO provides a quiet simple programming model hiding all interactions with the OSGi framework. In addition, iPOJO is extensible, and so is able to manage the event interaction that you described.

To achieve this, iPOJO instances are executed inside a container. This container is composed by "handlers". A handler is a special POJO which aims to manage one non-functional concern. As you mentioned, it could be an event based interaction (refer to http://felix.apache.org/site/ipojo-event-handler.html).

By using this mechanism, the POJO stills a very simple class despite it uses event based interactions. IPOJO handlers can be developed separately from the iPOJO core and so allow customizing containers for any requirement (persistence, security, event, wire admin ...).

I agree that iPOJO component type description (using XML) is not very elegant despite it tries to be the simplest as possible. For example, to provide a service just write:

<component classname="...MyClassName">
<provides/>
</component>

It is not necessary to describe provided interface as they can be computed by iPOJO (the POJO must implement the service interface). By using the same mechanism, requiring a service is quiet simple:

<component classname="...MyClassName">
<requires field="myService"/>
</component>

The service objects will automatically be injected inside the “myService” field. No need to describe the required interface as iPOJO can compute it automatically. The POJO can just use the myService field as any other field of the class.

So, to go further, we can use an "external" handler (developped and provided separately) to allow receiving events:

<component className="...MyClassName">
<ev:subscriber topic="mytopic" callback="receive"/>
</component>

Event published on the “mytopic” topic will be received automatically. At each time a new event is published, the receive method is called.

Secondly, you’re proposing a DSL to develop OSGi applications. Perhaps I'm wrong but a DSL should be in terms of the business logic of your application, not in technical terms such as pointed out

http://www-adele.imag.fr/Les.Publications/intConferences/SCC2007Mar.pdf

pauls replied on Mon, 2008/03/10 - 10:07am

I agree that this is not really my understanding of a DSL either - this is more like syntactic sugar. 

Interesting to note that iPOJO however, actually does make for a foundation of a DSL because of its service level dependencies...

Roman Roelofsen replied on Mon, 2008/03/10 - 10:30am in response to: clement.escoffier

Clement, there is no need to defend iPOJO since I said nothing bad about it ;-) But you must seperate between the features offered by iPOJO and the way to configure them. Otherwise you will compare apples and oranges. Take for example the handler mechanism you described. There is no reason not to use this handler with a DSL. Well written, the handler API could be even used as the DSL. That was the whole point of my article.

IMHO, a DSL does not need to be business oriented. It is a language dedicated to special domain (here OSGi) that, of course, can be a technical one. So instead of dealing with objects and methods we are creating a fluent interface for dealing with services, events, etc. The code

context event "TOPIC" ! new MyEvent

is a good example for a fluent interface. Here: Sending an event. While iPOJO helps receiving events, it does not help sending events.

You are free to think different about the term DSL (all IMHO here, of course ;-) but I think this article gives a good overview.

 

 

Clement Escoffier replied on Mon, 2008/03/10 - 12:23pm in response to: roman.roelofsen

Your article about DSL gives a good overview. IMO, DSL are really useful when they abstract technical details. But, of course, all languages are a DSL (just sometimes finding the domain is difficult).

In fact, what I tried to point out is that component (type) description is a kind of technical DSL too. More precisely, in the case of iPOJO, it's a composition of DSLs: each handler provides its own DSL for its own purpose and they are composed together. I don't believe that one DSL only is enough to describe any OSGi applications (in an efficient way). So, do you plan to investigate how adding other features to your DSL?

IMO, the challenge is to ease the development of OSGi applications to be modular, and dynamic. So for this purpose, I see two different interesting directions (it is not exhaustive) : simplify the development model and externalize a maximum of non-functional concerns, and provide integrated tools (i.e. IDE, SCM, ...) providing guidelines, generation tools, and architecture styles.

BTW, iPOJO is able to sends event too. This is not often used as the Event Admin service is very simple to use. The handler intercept when a field is set. The set value is considered as the event (either your field is an Event, either your value is added to an event created by the handler). It has the advantage to externalize the synchronous/asynchronous publication, the topic...

Richard Hall replied on Tue, 2008/03/11 - 1:32pm

I think the point that this DSL could be used as configuration language for iPOJO is valid, especially since iPOJO separates out the configuration language for just this purpose (e.g., currently it supports manifest entries, an XML file, and annotations).

The real issue is whether you prefer to learn yet another language or yet another XML markup format. That is probably a personal choice.

The type safety aspect of the DSL is nice, but since iPOJO is integrated into the Maven build process, it at least is able to perform type checking during the packaging process, so you are not completely out in the cold with respect to type checking even if you select the XML approach.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.