The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Fabrizio Giudici

Fabrizio Giudici is a Senior Java Architect with a long J2EE experience and in the latest two years he expanded his interests to Jini and NetBeans. Fabrizio has been running Tidalwave.it, his own consultancy company, since 2001 and has been a technical speaker at JavaOne, JavaPolis, Jazoon, Jini Community Meetings and some italian Java conferences. He started working with Java since the old 1.0 times and after 1.3 he has been committed in demonstrating that Java performance is not an issue, really. After bringing Java to the world of Formula One telemetry, he believes he is on the right path. Fabrizio is a member of the JUG Milano and the NetBeans Dream Team.

 

Fabrizio Giudici's blog

"As" (when an RDF store meets NetBeans Lookup)

Posted by fabriziogiudici on November 22, 2009 at 6:46 AM PST
My latest example of an API that would benefit from support by a RDF store was the “Observation API” and focused on modeling a set of observations made by some subjects and related to some places (I'm keeping birds and birdwatchers in mind, but it's only a special case):



I recall you that a typical example of use is:

ObservationManager observationManager = ObservationManager.Locator.findObservationManager();
ObservationSet observationSet = observationManager.createObservationSet();

Date when = dateFormat.parse("29-04-2007");
Location castiglione = observationSet.findOrCreate(Location.class, "Castiglione della Pescaia", EmptyInitializer.instance());
Observer fabrizio = observationSet.findOrCreate(Observer.class, "Fabrizio Giudici", EmptyInitializer.instance());
Observable flamingo = observationSet.findOrCreate(Observable.class, "Flamingo", EmptyInitializer.instance());
Observable spoonbill = observationSet.findOrCreate(Observable.class, "Spoonbill", EmptyInitializer.instance());
 
Observation observation = observationSet.createObservation().
                                         date(when).
                                         location(castiglione).
                                         item(spoonbill, Cardinality.valueOf(23)).
                                         item(flamingo, Cardinality.rangeOf(100, 150)).
                                         observer(fabrizio).
                                         build();

Now that I have introduced Elmo, you should have a rough idea about how this can be implemented so that a RDF store provides persistence: just figure out classes such as ElmoObservation, ElmoObservable etc... which are concrete implementations of the interfaces in the API and, by properly using @rdf annotations, map to RDF statements. Actually, sooner or later I'll show you the detail of this implementation, but not now.

The topic I'd like to illustrate today is how to use the great flexibility of a RDF store in a Java API.

In my previous post about Elmo, I wrote:

“... while a ER (Entity-Relation) database is usually less flexible than the code managing it (e.g. there's no inheritance, polymorphism, etc...) and there's a sort of strong type system (one table usually matches one entity), a RDF store is usually more flexible than the code managing it, because of the AAA slogan (Anyone can say Anything about Any topic).”.

Thinking of a concrete example, I'm not willing to constrain myself in having that the only property of the “fabrizio” or “flamingo” objects is a simple label: rather, I'd like them to be full-fledged objects, with multiple attributes and behaviours. For instance, an Observer that is a Person could have name, first name, birth date, etc... and and ObservationItem that is a Bird could be part of a classification scheme (taxonomy). Just imagine these two classes (Person, Bird) to be part of their relative APIs, designed in a similar way as the Observation API (or not), and possibly mapped themselves to RDF triples (for instance, FOAF is a standard ontology in the RDF world that has been designed to describe people).

It's a typical extensibility problem for APIs: you have some complete APIs that live on their own, and you want to enhance them by enabling their cooperation, without changing the code, since you don't want to introduce unneeded dependencies (the APIs could anyway be used on their own, or composed in different ways, etc).



How to “glue” these APIs together? Since we have got interfaces, one might be tempted about merging them into an “uber-interface”, but then problems arise out of the implementations. Before starting arguing about multiple inheritance in Java, I advance the objection that even if you were able to inherit from multiple classes, you'd get a static model: you can't change it after you compiled it. I keep in my mind a dynamic model, such as component programming à la OSGi, or the NetBeans Platform (or both), where you have single component binaries (e.g. the Observable API and the FOAF stuff), you just install them into a container (maybe at runtime) and they start cooperating together.

This is one of the battefields or the so-called “mixins”, supported in languages such as JavaFX or Scala (where they are called “traits”). If you look at the Scala example (please have a look at it since it's very simple), you'll see that the solution is asymmetrical: the subclass inherits something from the “true” superclass and additional behaviours from traits. I don't like this for three reasons:
  1. it's still static
  2. as explained it's asymmetric (you need to distinguish traits from regular classes)
  3. you need to declare traits in advance 
As a side note, I'm not saying that Scala can't do better. It can of course. I've just borrowed an example from Scala traits as Java doesn't have mixins.

Elmo provides a similar feature with “behaviours”, that also considers a synonym of “mixin” (it implements them in Java with interfaces and some dynamic bytecode manipulation). It's much better than traits since it is not completely static (behaviour are searched and injected at runtime), but it requires that all the involved classes are registered with Elmo in some way; instead I'd like to work even with regular classes, in case I don't need that all of them are persisted with RDF (moreover, you've surely noted that I'm constraining myself in keeping all the Elmo stuff in implementation details and not exposing through the public API).

The way I'm exploring to achieve those goals is very simple: the old, plain composition. The original classes stay as they are, but their instances get “glued” together into aggregates. With “glue” now I mean that I have a way to navigate from an object to any other in its aggregation, as the animated picture below explains.


I need a really generic glue and the best that I know is the Lookup class / idiom from NetBeans: it's a container of services that can be looked up by their class name and injected at runtime.

Please note that I'm not binding to the NetBeans Platform: Lookup is a simple API that can be used outside of it, in any Java application, by just importing a single JAR file.

For instance, with the following code I retrieve a certain service available in a Lookup:

Lookup lookup = ...;
MyService myService = lookup.lookup(Service.class);

myService.doSomething();

Lookup can work with multiple implementations and in mine it also searches for behaviours provided by Elmo. Now I just need to provide my classes with an extra method, getLookup(). For instance, I can write:

Observer observer = ...
observer.getLookup().lookup(Person.class).setEmail("fabrizio DOT giudici AT tidalwave DOT it");

Since this is a bit verbose, I've implemented a simple shortcut such as:

observer.as(Person.class).setEmail("fabrizio DOT giudici AT tidalwave DOT it");

Furthermore, I like the “as” word since it underlines that the object I'm dealing with has got multiple personalities. I'd like to stress the fact that this is not a static cast such as:

((Person)observer).setEmail("fabrizio DOT giudici AT tidalwave DOT it");

In fact the traditional cast works only if you provide an instance of a class that implements both Observer and Person, that is it has been statically compiled in that way, while as() dynamically searches in the aggregate, that can be modified at runtime, at any time.

Furthermore, as() works also in the reverse way:

Person fabrizio = ...
Observation observation = observationSet.createObservation().
                                         date(when).
                                         location(castiglione).
                                         item(spoonbill, Cardinality.valueOf(23)).
                                         item(flamingo, Cardinality.rangeOf(100, 150)).
                                         observer(fabrizio.as(Observer.class)).
                                         build();

Of course, to me this brings the additional advantage of providing integration with the NetBeans Platform. For instance, the Platform provides support for simplifying the rendering of lists and trees containing objects by means of a delegate called Node; the Visual Library, which is as sort of whiteboard where you an arrange things representing data, does the same by means of a delegate named Widget. Now I can write highly reusable code (note that “anything”):

Node node = anything.as(NodeFactory.class).createNodeDelegate();
Widget widget = anything.as(WidgetFactory.class).createWidgetDelegate();

BTW, this is a pretty good example about why I want to mix also behaviours that are not backed by Elmo: Node and Widget are UI-related classes that in general don't need persistence (or at least they don't need the same persistence as the domain object they refer to). 

What happens if the required personality can't be found? Well, as part of the dynamicity I've injected into your application, I must check preconditions. This means using conditionals or exception catching (for instance, in case of failure getLookup().lookup(Something.class) returns null, while I choose to have as(Something.class) to throw an exception). Alternatively you could use the Null Object pattern and have a dynamically generated, do-nothing object to be returned (this works well with methods returning a void, while it's not always easy to define a meaningful result for others, even though zero often is good).

Last but not least, there's the readability thing. I find the above code sketches pretty readable, but I know that not everybody will agree. Amen to that ;-) My main priority is to have a better design, and a dynamic one is better than a static one. There are actually cases where this approach is not completely DRY (Don't Repeat Yourself) - consider, for instance, a Displayable class with a getDisplayName() method. I'm using it and it's pretty useful, for instance, to retrieve the string to render in a ListCellRenderer or such:

String text = person.as(Displayable.class).getDisplayName();
// render text

This is not DRY, as the “display” english word is repeated twice. I don't think that there's anything to do in Java if we want to keep the dynamicity (that is, the fact that the object providing the display name is not known at compilation time). In other languages, such as Groovy, I could write:

var text = person.getDisplayName();

and arrange things so that the method is properly searched for and found.

Consider that these are corner cases; often the required personality has got more than a single method, and the DRY violation is less apparent or there's no violation at all (this is evident for things rich in semantics such as Person or Bird, and generically speaking, a domain model class). The Groovy example, anyway, is less flexible as it can't deal with the (admittedly rare) case in which multiple personalities have got methods with the same signature.

For my personal taste, the only thing that I'd like to get rid of is the .class suffix. I'd like to write

observer.as(Person).setEmail("fabrizio DOT giudici AT tidalwave DOT it");

and I wonder whether the javac compiler could just be made a bit smarter so it can guess it when it's missing. Maybe for JDK7?

In any case, as usual I have working code showing off this design. It still needs some polish and I'm preparing a screencast to explain how to use it. Stay tuned.
AttachmentSize
As.002.jpg21.54 KB
Comments
Comments are listed in date ascending order (oldest first)
Syndicate content