OldBeanSessionIntroduction
From Tupelo Wiki
| Table of contents |
Bean Session
Overview
An even higher level of abstraction/portability may be attained by eschewing the usage of Things and working with beans. For our purposes a bean is a java class with a no-arg constructor and standard mutators (a "setter" and "getter") for each property a client wishes to expose. A property may have one of the basic built in classes (int/Integer, long/Long, float/Float, double/Double, boolean/Boolean, URI, URL, Resource, Date, String) as well as a bean (as long as it has been mapped, more on that below). These are supported as single values, arrays and collections.
Description of use cases
When might a client want to use this? Possible use cases are:
Existing Bean classes
If a system already exists and can be represented as beans, then it may be imported to a context. As long as they information can be smuggled into java some way the system can be used to convert it to triples without the user knowing anything at all about RDF.
Existing RDF
The essence of RDF is to make 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.
Setting up mappings
A class mapping of a bean class is a translation of into RDF which allows the bean session to instantiate the bean and deserialize the contents to a context or serialize the contents. This is extremely customizable, although there are many simple convenience API calls. This permits classes that strictly speaking are not beans but have some mutators to be treated like beans, which is practically attainable in many cases. The main ways to create class mappings are programmatically and directly in RDF.
Programmatically
At its simplest, one can use the BeanClassRdfMappingFactory. This has an automatically configured factory which should be sufficient for most purposes. For customization, it is best to read the javadoc. Here is how to carry out the class mapping for a bean, called MyBean, at its absolute simplest:
Collection<BeanClassRdfMapping> beanClassMappings = BeanClassRdfMappingFactory.getFactory().createMapping(MyBean.class);
So what did this do? It created a collection of class mappings. The reason for this is that every bean that is referenced by this bean will also be mapped. A default RDF type is generated as well as default subjects for each predicate. To use this, you just need to take a bean session and add its mappings:
// assumes you've gotten a Context named context BeanSession beanSession = new BeanSession(context); beanSession.addBeanClassRdfMaps(mappings);
Now you may get or set beans and they will be serialized or deserialized correctly.
In RDF directly
To set this in RDF requires you use the bean vocabulary. It is probably easiest to do this programmatically then convert the resulting BeanClassRdfMapping into triples by invoking toTriples. A typical example would be
// assumes you have a context
Collection<BeanClassRdfMapping> beanClassMappings = BeanClassRdfMappingFactory.getFactory().createMapping(MyBean.class);
for(BeanClassRdfMapping x : beanClassMappings){
context.addTriples(x.toTriples());
}
Note that if you call one of the bean session APIs to make the mapping for you, the results are added to the context for you and will be available from the context from that point on, so you need not carry out the mapping more than once.
Tweaking a mapping
A pretty common wish is to have the mappings auto-generated, but to specify the predicates yourself so that others may find them. Here we have a bean called PersonBean. We want this to represent a Foaf person and use those predicates, so here is what we would do. The PersonBean has two properties, a "name" (just a string) and a collection of PersonBeans who are the person's "friends". The signatures for the PersonBean are
public class PersonBean{
public String getName();
public void setName(String name);
public Collection<PersonBean> getFriends();
public void setFriends(Collection<PersonBean> friends);
}
Here is how you would get the mapping
Collection<BeanClassRdfMapping> descriptions = BeanClassRdfMappingFactory.getFactory().createMapping(Vocabulary.Foaf.PERSON, null, new PersonBean().getClass());
Next, grab the first (and only in this case) mapping
BeanClassRdfMapping description = descriptions.iterator().next();
Here is how you can get the properties by name from the description
Property property = description.getProperty("Name");
property.setPredicate(Vocabulary.Foaf.NAME);
repeat for the "friends" property
property = description.getProperty("Friends");
property.setPredicate(Vocabulary.Foaf.KNOWS);
now add it to the set of mappings and you are ready to use it.
getBeanSession().addBeanClassRdfMap(description);
Creating a session
Creating a bean session is extremely easy. All you need is a context:
BeanSession beanSession = new BeanSession(context);
That really is it.
Reading a bean
Once you have a bean session, getting a bean requires that you have the subject and have mapped the bean's class appropriately. Assuming that you want to get an instance of MyBean, a typical call would be
MyBean myBean = (MyBean) beanSession.fetchBean(subject);
How do you get a subject? If the bean has not been registered or saved you can query the bean session:
Resource subject = beanSession.getSubject(bean);
This call will return either the subject of a registered bean or a new subject if the bean has not been registered. If you are trying to access information already in the context, you must find the correct subject from the context. Since every bean corresponds to a thing, the strategies for getting a subject are the same.
What does not work is the following:
MyBean myBean = new MyBean(); // fill in a bunch of properties Resource subject = beanSession.getSubject(myBean);
In which the client assumes that the context may be queried by field.
Saving and rolling back
One of the main benefits from using the bean session is that all of the properties in your bean are converted to RDF and saved. No additional methods or other structures need to be added to your bean class and the aim has been to make it work seamlessly with existing code and data.
Registration and de-registration
To register a bean means to notify the bean session to start tracking the state of your bean. Once registered, subsequent calls to bean session's save() method will serialize all of the properties into RDF and store them in the context. A typical session would look like this:
MyBean myBean = new MyBean(); // Fill in properties. Subject subject = beanSession.register(myBean); //gives you the subject used. // perhaps more operations. beanSession.save();
Note that while you must register the bean, you need not change one line of existing code to have all the changes tracked down and managed on your behalf. Similarly, to deregister a bean tells the bean session to lose all changes and quit managing the state. These two operations allow for a basic type of transaction which is a handy thing to have. Simply deregistering it will lose all pending changes:
beanSession.deregister(subject); //any pending changes are lost
If you want the previous state then refetch it. If you fetch a completely unsaved bean you will get back a blank bean. This would just be equivalent to called the no argument constructor.
Closing a BeanSession
There is nothing specific about closing a bean session. When you are done with it, be sure you have saved any pending changes.
A Small Sample Session
This is a complete session. Here we are using the test class PersonBean which has two properties.
public class PersonBean {
public PersonBean() {super();}
String _name;
public String getName() {return _name;}
public void setName(String name) {_name = name;}
Collection<PersonBean> _friends;
public Collection<PersonBean> getFriends() {return _friends;}
public void setFriends(Collection<PersonBean> friends) {_friends = friends;}
}
and from start to finish, here is how you would use it.
Context context = new MemoryContext(); // easy for testing
BeanSession beanSession = new BeanSession(context);
// make a new person bean. Foo-foo is not a person, but perhaps this is the
// best we can do to represent our data...
PersonBean fooFoo = new PersonBean();
fooFoo.setName("Little bunny foo-foo");
// Tell the session to start managing the bean. Save the subject for future work.
Resource subject = beanSession.mapAndRegister(fooFoo);
// now we can add a friend.
PersonBean peter = new PersonBean();
peter.setName("Peter Cottontail");
LinkedList<PersonBean> friends = new LinkedList<PersonBean>();
friends.add(peter);
fooFoo.setFriends(friends);
beanSession.save();
Since we are kicking the tires, you should realize that the bean "peter" was also serialized in the process. So it is now being managed by the session as well. Issuing
Resource peterSubject = beanSession.getSubject(peter);
will give you the subject. Of course, you don't need to know this specifically, but should you decide to change a property of "peter" this will be noticed by the session when you save, e.g.,
peter.setName("Peter Q. Cottontail IV");
beanSession.save();
now has "peter"'s name updated. In general registering a bean with the session registers a whole network of them. What's more, any unkown beans encountered will be mapped to an RDF type and stored as well, minimizing the work needed.
