Creating a Domain Specific Language for OSGi
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 SuperServiceImplThis 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);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.
if (ref != null) {
SuperService s = (SuperService) context.getService(ref);
System.out.prinln(s.msg);
context.ungetService(ref);
}
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 parameterIf 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?
- Login or register to post comments
- 1520 reads
- Flag as offensive
- Printer-friendly version
(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:
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:
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:
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
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.