Bean Session Tutorial
From Tupelo Wiki
| Table of contents |
Bean Session
(A useful link we'll put right here, due to popular request, is the glossary).
What is it?
Many software systems use the Java bean as their standard data structure. These are portable and very simple to work with. RDF is a useful method for modeling data and it is a natural step to describe the contents of a java bean in RDF. However, converting the information into RDF can be quite taxing. A very useful tool, of course, would be some software that could largely automate this conversion to or from RDF along with generating a reasonable vocabulary. Fortunately, this is what the bean session is designed to do. Hence a user get the benefits of RDF without having to write a complex vocabulary or write the serialization/deserialization code.
The APIs for BeanSession (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/BeanSession.html) are made to be quite similar to ThingSession (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/ThingSession.html). A BeanSession is backed by a context and allows the user to register beans and save changes to them with a single command. It also allows for retrieving beans. In cases where a given bean is not available in the local JVM, a proxy for the bean may be used. See the section below for more information about using proxies. Also a glossary is provided.
In a nutshell with the bean session you can
- Avoid knowing much about RDF and get most of the benefits of it
- Convert any beans to/from RDF on the fly
- Store your beans as RDF in a context for others to use
- Have all the nasty details of vocabulary generation done automatically
This is the short list and there is much more.
Some motivating examples
Existing Bean classes
If a system either uses beans or can have its data represented as beans, then the data may be serialized to RDF and put into a context. Bean session can be used to serialize the data to triples without the user knowing anything at all about RDF.
Existing RDF
The attraction of RDF is that it makes information as portable as possible. If information exists in a context, it may be represented by any convenient bean that the user might desire. It is, of course, irrelevant how the information gets into the context, so it is quite possible to have multiple sources for the data which can easily be managed by a bean. If the client wishes, no Java classes even need to be written since the data may be put into a bean proxy.
Basic bean session operations
This section is concerned with the various operations that are needed to work with beans. Each step is required, but there are several convenience methods that may be used.
Creating a bean session
All that is required is a context
BeanSession beanSession = new BeanSession(context);
Mapping bean classes
Mapping a bean class means to write an RDF description of the properties and their types. This is how the bean session knows which properties may be associated with the data. Note that if the stored standard data type of the data is not the same as the mapping it will be reasonably converted, that is to say, an attempt to parse it to a the required type will be made. Not all types are convertible though. For example, dates stored as strings need to be in ISO 8601 format. Other formats probably won't be interpreted correctly. A workaround for this is the bean pre/post-processor (see below).
Since mapping is effectively a type of introspection, there is a rather full and rather complex API that goes with it, the full details of which are in the bean class RDF mapping tutorial. You only need to be aware of this API if you have some very specific needs. The most basic way to get a bean mapped is to use the BeanClassRdfMappingFactory, which will look through all the methods and automatically generate a vocabulary for you:
Collection<BeanClassRdfMapping> beanClassMappings = BeanClassRdfMappingFactory.getFactory().createMapping(myBean.class);
This returns a collection because it allows for bean-valued properties. All referenced beans will have a mapping done for them. In order to use the mappings you need to add them to the bean session:
beanSession.addBeanClassRdfMaps(beanClassMappings);
This will also put them in the bean session's context, so this only needs to be done once. Again, the description of the bean and its properties is in RDF and will be stored in the same context as the data itself.
Fetching beans
To retrieve or fetch a bean from a context, you need to just supply the subject and cast:
MyBean myBean = (MyBean)beanSession.fetchBean(subject);
Remember that if the class is unavailable an exception will be thrown. In such cases it is advisable to consider using bean proxies. If you fetch a bean, then it is automatically registered with the session.
Covenience methods
To make a mapping (if needed), register a bean and have a subject created for you, simply use
Resource subject = beanSession.mapAndRegister(myBean);
This automatically generates a default vocabulary for the bean.
Registration and deregistration
To register a bean is to notify the session to track changes to the bean. Part of registering a bean is to associate a unique RDF subject with it. An invocation looks like
Resource subject = beanSession.register(myBean);
Now you have the subject in hand. If you require a specific subject, then you may also register the bean and supply the subject. Here is an example where the subject is created then used:
Resource mySubject = Resource.uriRef("http://fee.fie.fo/fum/mySpecialSubject");
beanSession.register(mySubject, myBean);
You may also deregister a bean, i.e., tell the session to lose any pending changes and quit tracking the bean:
beanSession.deregister(subject);
If you have to repeat this for a large number of beans, it is probably best to simply recreate the session from the context and register all the beans you wish to track. Be sure to use the correct subjects.
Subjects
A special note about subjects is in order. If you are unfamiliar with them, this section is for you. When we say "subject" we do indeed mean "RDF subject". This will be the subject associated with the data henceforth. To retrieve this information you must supply the same subject again. If you do not have the subject (for instance you are trying to retrieve existing data from a context) then you must use some other mechanism to determine the subject. At this point, the bean session does not have any way to perform such queries on a context, since Tupelo has other mechanisms to do this. If you need help, then look at the documentation for Unifier (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/Unifier.html) and TripleMatcher (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/TripleMatcher.html).
There is a method to get a subject in bean session:
public Resource getSubject (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/BeanSession.html#getSubject(java.lang.Object))(Object bean)
This does one of two things
- If the bean has been registered, it will return the subject for that bean.
- If the bean has not been registered, it will create a new subject for this bean, which may be used to register the bean.
Pre/Post-processing
There are two points at which additional processing on a bean may be done: At save and at fetch. For instance, possible uses are
- Notifying a service for every bean that is saved could be done this way, as could registering every bean that is deserialized.
- Data clean up, e.g., making sure any date fields are correct (ISO 8601 format if stored as strings), or double checking data integrity.
- Off-writing fields that are transitory, so they are not put into the repository. For example, a client would not want to save a password. (Off-writing a property simply means storing a given property value someplace and removing its value from the bean.)
Note that any and all post/pre-processing is completely optional. If you have no need of it, ignore it. Since beans may reference other beans etc., a single fetch or save may involved many, many other beans that the client does not have access too (such all the beans in an array). These processors will be applied to every bean, explicitly referenced by the client or not.
For saving, implement the interface BeanPreprocessor, which has two methods:
public void preprocess(Object bean); public void postprocess(Object bean);
A single instance of an implementation is used by a bean session. If you had a processor you would call
public void setBeanPreprocessor(BeanPreprocessor beanPreprocessor);
on your beanSession instance. It is assumed the preprocess and postprocess methods return the original instance of the bean, since beans are tracked by instance in the session.
Before every bean is saved the preprocess method is invoked on it. Immediately afterwards, the postprocess method is invoked. This lets the client do any last minute clean-up or state checking before saving.
For processing after a fetch (it makes no sense to preprocess before a fetch) use the FetchBeanPostprocessor which has a single method
public void postprocess(Object bean);
Every bean that is fetched will have this method invoked on it immediately after deserialization.
Saving and rolling back
To save an individual bean, you must use the subject:
beanSession.save(subject);
To save every bean known to the session, simply invoke
beanSession.save();
This permits you to have a pseudo-transaction for managing your beans. If you need to rollback a set of changes, the easiest way is to simply create a new bean session on the same context.
We say pseudo-transaction because the open world assumption of RDF makes true transactions impossible. For most practical purposes, however, bean session will suffice.
Various internals
This section collects articles on some of the internals of bean session. These are aimed primarily at people who, for instance, want to work with the underlying RDF directly.
Bean Entries (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/beans/BeanEntryThing.html)
Map Entries (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/beans/MapEntryThing.html)
File Entries (http://dlt-dev.ncsa.uiuc.edu/javadoc/t2/current/org/tupeloproject/kernel/beans/FileEntryThing.html)
