The Observation API (hey, it's not the Observable pattern)
As I said in
href="http://weblogs.java.net/blog/fabriziogiudici/archive/2009/02/bluemarine_went.html">February,
I'm going to post on my blog a series about the use of Semantic
Technologies and how they are being used in some of my projects.
Today let's start giving a small domain model and then design a related
abstract API. In the next post I'll show you how to
implement it on the top of a
RDF triple store.
Models
style="font-weight: bold;">
The
API is designed to manage a set of "observations" (of whatever, by
anybody, etc... let's keep it generic in order to respect the "AAA
slogan": Anyone can say Anything about Any topic), so I'm
calling
it "the Observation
API".
Let's introduce the key abstractions:
- An Observer
represents entities capable to make observations. It may be a
physical person, or a device, or anything else makes sense. - An Observation
is made at a certain time and Location,
by one or more Observers
and is composed of one or more ObservationItems. - An ObservationItem
pairs an Observable
with a Cardinality. - An Observable
has no special properties and can be anything. - A Cardinality
may be a single integer number, a close or open range of integers (e.g.
"between 10 and 20", or "more than 5"), or "undefined". - An ObservationSet
is a set of Observations. - A Source
is where a certain datum comes from (i.e. who or what provided it,
inserting into the database).
src="https://bluemarine.dev.java.net/nonav/Blog/20090429/DomainModel.png">
All of the above concepts are going to become classes, so I'm now
referring to them with the code typo convention.
style="font-family: monospace;">Observer,
Observable,
Location
and Source
have a single property exposed through
style="font-family: monospace;">getDisplayName(),
that is the identifier used for rendering the object in a UI. Applying
some common patterns, we add a few more classes for the solution model:
- An
style="font-weight: bold; font-family: monospace;">ObservationManager
style="font-family: monospace;"> provides way
to retrieve and create ObservationSets. - A
style="font-weight: bold; font-family: monospace;">Finder
is used to perform various queries. - An
style="font-weight: bold; font-family: monospace;">Observation.Builder
provides ways to create new Observations.
The
only architectural dependency introduced in the API is related to the
use of components à la NetBeans Platform, whose lookup logic is hidden
behind a ObservationManager.Locator.
As
href="http://weblogs.java.net/blog/jst/archive/2009/04/library_for_inj.html">annotations
has been recently introduced for dependency injection in
the Platform, this class will be removed at some point in
future,
making the API mostly technology-agnostic.
href="https://bluemarine.dev.java.net/nonav/Blog/20090429/it.tidalwave.bluebill.observation.png">
style="border: 0px solid ; width: 800px; height: 638px;"
alt="UML Diagram"
src="https://bluemarine.dev.java.net/nonav/Blog/20090429/it.tidalwave.bluebill.observation-800.png">
(Click on the above diagram for a larger version)
Scenarios and examples
style="font-weight: bold;">
While
I have anticipated that this API is very generic, for the following
scenarios I'm giving the context of birding (or birdwatching) - which
BTW is the reason for which I've designed it.
First of all, code dealing with the Observation API shoud create an
style="font-family: monospace;">ObservationSet:
ObservationManager observationManager = ObservationManager.Locator.findObservationManager();<br>ObservationSet temporarySet = observationManager.createObservationSet();<br>// or<br>ObservationSet persistentSet = observationManager.createObservationSet(backingStore); // backingStore is a generic Object, architecture dependent
ObservationManager
provides a convenience method findOrCreate()
for creating various entities of the API, even though they can be also
instantiated in other ways - let's remember that things such as
style="font-family: monospace;">Observer,
style="font-family: monospace;">Observable,
style="font-family: monospace;">Location and
style="font-family: monospace;">Source are
pretty generic stuff, and their implementation could come from some
other APIs. This is a
very important point: in a real application I don't expect
ObservationManager.findOrCreate()
to be really used alone, if not for ancillary, simple entities, because
in the
real world I'll mostly need some concrete entities with their own
properties and behaviour. But the simple use of this method is ok for
tests (and examples).
src="http://stoppingdown.net/media/photos/800/20070429-0007-800.jpg">
Here it is how I could create a new
style="font-family: monospace;">Observation of
about 100-150 flamingoes and 23 spoonbills:
Date when = dateFormat.parse("29-04-2007");<br>Location castiglione = observationSet.findOrCreate(Location.class, "Castiglione della Pescaia", EmptyInitializer.instance());<br>Observer fabrizio = observationSet.findOrCreate(Observer.class, "Fabrizio Giudici", EmptyInitializer.instance());<br>Observable flamingo = observationSet.findOrCreate(Observable.class, "Flamingo", EmptyInitializer.instance());<br>Observable spoonbill = observationSet.findOrCreate(Observable.class, "Spoonbill", EmptyInitializer.instance());<br> <br>Observation observation = observationSet.createObservation().<br> date(when).<br> location(castiglione).<br> item(spoonbill, Cardinality.valueOf(23)).<br> item(flamingo, Cardinality.rangeOf(100, 150)).<br> observer(fabrizio).<br> build();<br>
Note how a fluent interface has been used for specifying all the data
items for building an Observation.
If this operation is made against a persistence
style="font-family: monospace;">ObservationSet,
the inserted data are made persistent too (eventually at the next
transaction commit; details are opaque to the API). The only required
parameter for findOrCreate(),
besides the entity type, is a string which represents
the display
name of the entity; if an entity of the given type and display name
already
exists, it will be returned and not created. A third argoment is an
implementation of the interface:
public interface Initializer<T> <br> {<br> public void initialize (T entity);<br> }
that
is responsible for initializing the entity status if it is created from
scratch; this will be discussed in a future post, in the meantime you
just need to know that EmptyInitializer.instance()
returns a "no op" initializer that does nothing.
Now, how to perform queries? The starting point is the
style="font-family: monospace;">ObservationSet
of course:
List<Observable>
observables =
observationSet.find(Observable.class).
sort(SortCriterion.DISPLAY_NAME).
from(100).
max(20).
results();
style="font-family: monospace;">
ObservationSet.find(Class<T>)
returns a Finder<T>,
which encapsulates the query logic. It also provides a fluent interface
to specify some generic (optional) parameters of the query; in the
above example the sort criterion and the "paging" of results (from the
100th, for a max of 20 items), which is easily integrable in user
interfaces supporting paging. You can specify criterion for a few
generic attributes of an Observation
(e.g. DISPLAY_NAME,
DATE,
style="font-family: monospace;">LOCATION,
style="font-family: monospace;">OBSERVABLE).
There are no more things to specify here as we are still dealing with
generic entities.
Finders are used in other places of the API too: for
instance, you can do:
Observable
observable = ...;
Finder<ObservationItem>
finder = observable.findObservationItems();
style="font-family: monospace;">
or
Location location = ...;<br>Finder<ObservationItem> finder = location.findObservationItems();
that is, you can query single entities for the related concepts.
Enough for today. Some resources:
- Check out code from
svn co -r 783<a
href="https://bluebill.dev.java.net/svn/bluebill/trunk/src/blueBill-Server/Core/Observation">https://bluebill.dev.java.net/svn/bluebill/trunk/src/blueBill-Server/Core/Observation</a>
- Login or register to post comments
- Printer-friendly version
- fabriziogiudici's blog
- 2334 reads






Comments
by fabriziogiudici - 2009-05-09 00:33
It would make sense to have a range for the Date. Let's say I didn't choose it for the first iterations, even though the application using this code is manipulating some thousands records and, up to the moment, there is not a strict need for it. But of course, at the moment I get JSR-310 in (see comments above) I could use the classes that represents an interval of time.by andreamoz - 2009-05-08 05:54
Hi Fabrizio, a natural (at least in my head) extension of the model could be using a range instead of a date; am I the first fool that thinks of it - maybe because I have completely misunderstood your intent - or have you explicitly decided to keep it this way? I'd like to hear the logic beyond this choice ps: great job... as always!by fabriziogiudici - 2009-05-01 11:47
Of course. Thanks for the correction.by emorning - 2009-05-01 09:44
> > but while I'm confident with JodaTime and soon with JSR 311 > You meant JSR-310 : Date and Time APIby fabriziogiudici - 2009-04-29 16:56
Hi Stephen. My idea is not to use Date any longer (it has disappeared by all my paid projects, of course), but while I'm confident with JodaTime and soon with JSR 311, at the moment I don't know how to persist a date different than Date in the RDF store I've decided to use. I'm pretty sure it's possible, but I've left that for a future iteration. These APIs won't be frozen soon.by scolebourne - 2009-04-29 16:51
Since your API takes in a Date instance to define the date, your API may well yield different results depending on the time zone of the user running the code (perhaps persisted in one time zone and attempted to be read in another).