jColibri is a Java framework developed for designing and implementing Cased Based Reasoning (CBR) applications. CBR is the process of solving new problems based on the solutions from past problems and has been developed by Roger Schank together with his students from the Yale University in the early 1980. Since then, CBR has become one of the most successfully applied subfields in Artificial Intelligence.
Due to this fact, the need for tools to build CBR systems and the accumulated practical experience required to apply this technique to real-world problems has become critical.
The jColibri framework’s purpose is to provide a platform for the design and generation of CBR systems that cover these necessities.
Before moving on to jColibri you need to understand the principles behind Cased Based Reasoning, however this is not the purpose of this article. As an example, an auto mechanic who fixes an engine by recalling another car that had similar problems is in fact using case-based reasoning. You can find more information about CBR here.
The jColibri framework was developed by GAIA (Group for Artificial Intelligence Applications) over the course of several years and represents the PhD. Thesis of Juan Recio Garcia. You can find more information about jColibri on the project website and you can also find an English version of the thesis at this link.
Moving on to the interesting stuff, the framework is currently at its 2nd edition, and will be launching the 3rd one very soon. The framework is structured on 2 layers, a white-box layer (intended for developers) and a black-box layer (intended for CBR system designers- will be available in the next major release). We’re going to focus on the white-box layer, more exactly we are going to take a look at how to extent the way jColibri communicates with data bases using Hibernate.
jColibri provides the “Connector” interface that must be implemented in order to communicate to a data base through Hibernate.
Fig. 1: The ‘Connector’ interface
The framework also provides a default implementation in this interface through the “DataBaseConnector” class. This implementation provides code for the initFromXmlfile, retrieveAllCases and the storeCases methods. Even though these methods are enough to get you started using jColibri there are some modifications and extensions that need to be done so you can properly use this framework in a production environment. The rest of the article will describe these modifications and extensions.
First of all, the storeCases (Collection<CBRCase>) needs some modifications. The standard implementation assumes that you will be entering the ids of the cases manually. In real projects that involve communicating with a data base, you don’t let the user choose the id manually, it’s a bad practice and the user shouldn’t be concerned with this kind of information anyway.
Here’s an excerpt from the default implementation of storeCases() :
Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); session.save(c.getDescription()); transaction.commit(); session.close(); |
THE PROBLEM: If you need to perform data base specific from the part of the user the code above will fail at the third line. Calling the session.save () method when making the session globally available will fail telling you that an object with the same id already exists.
The way you fix this is really easy, you just need to replace save () with merge (). This is one of the modifications we’ve added to the connector. An excerpt from the modified method is provided bellow:
Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); session.merge(c.getDescription()); transaction.commit(); session.flush(); session.close(); |
But if you look carefully, and remember that we said jColibri needs users to give the id of the object that needs to be stored, you realize that the storeCases () method becomes just a way of updating some stored objects because we also said that in real world projects, the user should have nothing to do with this kind of information.
THE EXTENSION: with the storeCases () method rendered useless for storing new cases we needed to develop another method that will suite this need.
As you can imagine, this method is very similar to the original default implementation, it has small changes, but these small changes make all the difference.
We named the method storeNewCase () and instead of saving a collection of cases at the same time, we are only saving one case. Basically, what we need to do is to save the first component of the case, retrieve the id it was saved with and then set this id to the rest of the case components and save them as well.
Here is a sample code from the method implementation :
Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); fina Long caseId = (Long) session.save(descr); transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); if(sol != null) { Attribute solId = sol.getIdAttribute(); ServerUtils.setValue(solId, sol, caseId); session.save(sol); } transaction.commit(); session.close(); |
There are some specifications we need to make:
- You can modify the storeCases () method without any further modifications on the components of the framework. This will let you use the method without receiving any errors but you can’t really use it to store new cases.
- In order for you to use the storeNewCase () method added as an extension you also need to modify the default implementation of the CBRCaseBase implementation. The CBRCaseBase interface provides a way of storing cases retrieved from the data base in local memory to make them easier to work with
The case base sits between the application core and the data base connector so all operations executed from the connector must come from a case base implementation.
In our case, we used the LinealCaseBase implementation of the framework and we modified it accordingly. All we needed to do was to add a new method to the default implementation(the learnNewCase () method) .
Here is the code for both the connector and the case base methods.
- The connector class
public void storeCases(Collection<CBRCase> cases) { for(CBRCase c : cases) { Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); session.merge(c.getDescription()); transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); if(c.getSolution() != null) { session.merge(c.getSolution()); transaction.commit(); session.close(); } session = sessionFactory.openSession(); transaction = session.beginTransaction(); if(c.getJustificationOfSolution() != null) { session.merge(c.getJustificationOfSolution()); transaction.commit(); session.close(); } session = sessionFactory.openSession(); transaction = session.beginTransaction(); if(c.getResult() != null) { session.merge(c.getResult()); transaction.commit(); session.close(); } } LOG.info(MSG_CASES_STORED); } /** * @param theCase */ public void storeNewCase(CBRCase theCase) { CaseComponent descr = theCase.getDescription(); CaseComponent sol = theCase.getSolution(); CaseComponent justof =theCase.getJustificationOfSolution(); CaseComponent result = theCase.getResult(); Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); Long caseId = (Long) session.save(descr); transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); if(sol != null) { Attribute solId = sol.getIdAttribute(); try { solId.setValue(sol, caseId); } catch(AttributeAccessException e) { LOG.error(e); } session.save(sol); } transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); if(justof != null) { Attribute justId = justof.getIdAttribute(); try { justId.setValue(justof, caseId); }catch(AttributeAccessException e) { LOG.error(e); } session.save(justof); } transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); if(result !=null) { Attribute resId = result.getIdAttribute(); try { resId.setValue(justof, caseId); } catch(AttributeAccessException e) { LOG.error(e) } session.save(result); } transaction.commit(); session.close(); LOG.info(MSG_CASES_STORED); } |
- The case base class
public String learnNewCase(CBRCase theCase) { final String result = connector.storeNewCase(theCase); this.cases.add(theCase); return result; } |
Finally, there’s another very important peculiarity of what does Cialis that brings it so high above its alternatives. It is the only med that is available in two versions – one intended for use on as-needed basis and one intended for daily use. As you might know, Viagra and Levitra only come in the latter of these two forms and should be consumed shortly before expected sexual activity to ensure best effect. Daily Cialis, in its turn, contains low doses of Tadalafil, which allows to build its concentration up in your system gradually over time and maintain it on acceptable levels, which, consequently, makes it possible for you to enjoy sex at any moment without having to time it.
Very informative and straight-forward article!
April 18, 2011 at 11:39 pmI will use the info, thx.