001/**
002 * Test1.java
003 * jCOLIBRI2 framework. 
004 * @author Juan A. Recio-Garc�a.
005 * GAIA - Group for Artificial Intelligence Applications
006 * http://gaia.fdi.ucm.es
007 * 28/11/2006
008 */
009package es.ucm.fdi.gaia.jcolibri.test.test1;
010
011import java.util.Collection;
012
013import es.ucm.fdi.gaia.jcolibri.casebase.LinealCaseBase;
014import es.ucm.fdi.gaia.jcolibri.cbraplications.StandardCBRApplication;
015import es.ucm.fdi.gaia.jcolibri.cbrcore.Attribute;
016import es.ucm.fdi.gaia.jcolibri.cbrcore.CBRCaseBase;
017import es.ucm.fdi.gaia.jcolibri.cbrcore.CBRQuery;
018import es.ucm.fdi.gaia.jcolibri.cbrcore.Connector;
019import es.ucm.fdi.gaia.jcolibri.connector.DataBaseConnector;
020import es.ucm.fdi.gaia.jcolibri.exception.ExecutionException;
021import es.ucm.fdi.gaia.jcolibri.method.retrieve.RetrievalResult;
022import es.ucm.fdi.gaia.jcolibri.method.retrieve.NNretrieval.NNConfig;
023import es.ucm.fdi.gaia.jcolibri.method.retrieve.NNretrieval.NNScoringMethod;
024import es.ucm.fdi.gaia.jcolibri.method.retrieve.NNretrieval.similarity.global.Average;
025import es.ucm.fdi.gaia.jcolibri.method.retrieve.NNretrieval.similarity.local.Equal;
026import es.ucm.fdi.gaia.jcolibri.method.retrieve.NNretrieval.similarity.local.Interval;
027import es.ucm.fdi.gaia.jcolibri.method.retrieve.selection.SelectCases;
028
029/**
030 * Test 1 shows how to use a simple data base connector and perform the KNN retrieval. It uses the travel example with cases that only have description
031 * (without solution or result).
032 * <br>
033 * This example uses the DataBase connector that is implemented using the <a href="www.hibernate.org">Hibernate</a> library. 
034 * That library is a Java Data Objects implementation that automatically manages the persistence of 
035 * Java beans in relational data bases.
036 * <br>For an introduction to hibernate see: <a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial.html">http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial.html</a>
037 * <p> 
038 * The DataBase connector in es/ucm/fdi/gaia/jcolibri/test/test1/databaseconfig.xml and that xml defines the configuration of Hibernate and
039 * the mapping of the description of our case with the data base. In this example, all the attributes of the description bean are stored in the same table.
040 * <br>
041 * <ul>
042 * <li><b>databaseconfig.xml</b>.<br>Connector configuration file: indicates the path to the hibernate config file, the name of the class/bean that stores the description and its mapping file.<br>
043 * <pre>
044 * &lt;DataBaseConfiguration&gt;
045 *        &lt;HibernateConfigFile&gt;es/ucm/fdi/gaia/jcolibri/test/test1/hibernate.cfg.xml&lt;/HibernateConfigFile&gt;
046 *        &lt;DescriptionMappingFile&gt;es/ucm/fdi/gaia/jcolibri/test/test1/TravelDescription.hbm.xml&lt;/DescriptionMappingFile&gt;
047 *        &lt;DescriptionClassName&gt;es.ucm.fdi.gaia.jcolibri.test.test1.TravelDescription&lt;/DescriptionClassName&gt;
048 * &lt;/DataBaseConfiguration&gt;
049 * </pre>
050 * 
051 * <li><b>hibernate.cfg.xml</b>.<br>Hibernate configuration file: developers should only modify the database connection settings.<br>
052 * <pre>
053 * &lt;hibernate-configuration&gt;
054 *   &lt;session-factory&gt;
055 *       &lt;!-- Database connection settings --&gt;
056 *       &lt;property name="connection.driver_class"&gt;org.hsqldb.jdbcDriver&lt;/property&gt;
057 *       &lt;property name="connection.url"&gt;jdbc:hsqldb:hsql://localhost/travel&lt;/property&gt;
058 *       &lt;property name="connection.username"&gt;sa&lt;/property&gt;
059 *       &lt;property name="connection.password"&gt;&lt;/property&gt;
060 *              
061 *       &lt;!-- JDBC connection pool (use the built-in) --&gt;
062 *       &lt;property name="connection.pool_size"&gt;1&lt;/property&gt;
063 *
064 *       &lt;!-- SQL dialect --&gt;
065 *       &lt;property name="dialect"&gt;org.hibernate.dialect.HSQLDialect&lt;/property&gt;
066 *
067 *       &lt;!-- Enable Hibernate's automatic session context management --&gt;
068 *       &lt;property name="current_session_context_class"&gt;thread&lt;/property&gt;
069 *
070 *       &lt;!-- Disable the second-level cache  --&gt;
071 *       &lt;property name="cache.provider_class"&gt;org.hibernate.cache.NoCacheProvider&lt;/property&gt;
072 *
073 *       &lt;!-- Echo all executed SQL to stdout --&gt;
074 *      &lt;property name="show_sql"&gt;true&lt;/property&gt;   
075 *    &lt;/session-factory&gt;
076 * &lt;/hibernate-configuration&gt;
077 * </pre>
078 * <p>This test uses the HSQLDB data base server with an example table containing 
079 * the data of the case base. Developers can use any other DBMS changing the 
080 * hibernate configuration file.
081 * <br>
082 * If you use another database change the driver, url and dialect fields. 
083 * For example, to use a MySQL server you should use:
084 * <pre>
085 *       &lt;property name="connection.driver_class"&gt;com.mysql.jdbc.Driver&lt;/property&gt;
086 *       &lt;property name="connection.url"&gt;jdbc:mysql://localhost:3306/travel&lt;/property&gt;
087 *       &lt;property name="dialect"&gt;org.hibernate.dialect.MySQLDialect&lt;/property&gt;
088 * </pre>
089 * For other configuration settings see hiberante documentation: 
090 * <a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/session-configuration.html">http://www.hibernate.org/hib_docs/v3/reference/en/html/session-configuration.html</a>
091 * </p>
092 * </li>
093 * <li><b>TravelDescription.hbm.xml</b>.<br> Hibernate mapping file for the description bean the case (TravelDescription.java).
094 * It stores each attribute of the description in a column of the table Travel.<br>
095 * <pre>
096 * &lt;hibernate-mapping default-lazy="false"&gt;
097 *  &lt;class name="es.ucm.fdi.gaia.jcolibri.test.test1.TravelDescription" table="Travel"&gt;
098 *        &lt;id name="caseId" column="caseId"&gt;
099 *          &lt;generator class="native"/&gt;
100 *        &lt;/id&gt;
101 *        &lt;property name="HolidayType" column="HolidayType"/&gt;
102 *        &lt;property name="Price" column="Price"/&gt;
103 *        &lt;property name="NumberOfPersons" column="NumberOfPersons"/&gt;
104 *        &lt;property name="Region" column="Region"/&gt;
105 *        &lt;property name="Transportation" column="Transportation"/&gt;
106 *        &lt;property name="Duration" column="Duration"/&gt;
107 *        &lt;property name="Season" column="Season"/&gt;
108 *        &lt;property name="Accomodation" column="Accommodation"/&gt;
109 *        &lt;property name="Hotel" column="Hotel"/&gt; 
110 *  &lt;/class&gt;
111 * &lt;/hibernate-mapping&gt;
112 * </pre>
113 * Here we set that TravelDescription is mapped in the Travel table. caseId is the primary key of the table and Hibernate will use a 
114 * native key generator for new cases (there are different ways to create primary keys, for more information see Hiberante documentation).
115 * Each attribute is mapped into a table with the same name. You should notice that here we don't indicate the type of the attributes. 
116 * Hibernate automatically detects the type and converts from/to the database. Anyway if you want to use an unrecognoized type that hibernate does not
117 * understand or create your own one you can do it by implementing the jcolibri.connector.TypeAdaptor interface in your type.
118 * <br>
119 * This is the structure of the table:
120 * <pre>
121 * +----------+-------------+-------+-----------------+--------+----------------+----------+--------+---------------+--------------------------+
122 * | caseId   | HolidayType | Price | NumberOfPersons | Region | Transportation | Duration | Season | Accommodation | Hotel                    |
123 * +----------+-------------+-------+-----------------+--------+----------------+----------+--------+---------------+--------------------------+
124 * | Journey1 | Bathing     |  2498 |               2 | Egypt  | Plane          |       14 | April  | TwoStars      | Hotel White House, Egypt |
125 * | Journey2 | Bathing     |  3066 |               3 | Egypt  | Plane          |       21 | May    | TwoStars      | Hotel White House, Egypt | 
126 * ...
127 * </pre>
128 * The travel.sql file contains the code to create this data base.
129 * </ul>
130 * 
131 * @author Juan A. Recio-Garcia
132 * @version 1.0
133 * @see es.ucm.fdi.gaia.jcolibri.test.test1.TravelDescription
134 * @see jcolibri.connector.DataBaseConnector
135 */
136public class Test1 implements StandardCBRApplication {
137
138        /** Connector object */
139        Connector _connector;
140        /** CaseBase object */
141        CBRCaseBase _caseBase;
142        
143        /* (non-Javadoc)
144         * @see jcolibri.cbraplications.BasicCBRApplication#configure()
145         */
146        public void configure() throws ExecutionException{
147                try{
148                // Create a data base connector
149                _connector = new DataBaseConnector();
150                // Init the ddbb connector with the config file
151                _connector.initFromXMLfile(es.ucm.fdi.gaia.jcolibri.util.FileIO.findFile("es/ucm/fdi/gaia/jcolibri/test/test1/databaseconfig.xml"));
152                // Create a Lineal case base for in-memory organization
153                _caseBase  = new LinealCaseBase();
154                } catch (Exception e){
155                        throw new ExecutionException(e);
156                }
157        }
158        
159
160        
161        /* (non-Javadoc)
162         * @see jcolibri.cbraplications.BasicCBRApplication#preCycle()
163         */
164        public CBRCaseBase preCycle() throws ExecutionException {
165                // Load cases from connector into the case base
166                _caseBase.init(_connector);             
167                // Print the cases
168                //java.util.Collection<CBRCase> cases = _caseBase.getCases();
169                //for(CBRCase c: cases)                 System.out.println(c);
170                return _caseBase;
171        }
172        
173        /* (non-Javadoc)
174         * @see jcolibri.cbraplications.BasicCBRApplication#cycle()
175         */
176        public void cycle(CBRQuery query) throws ExecutionException 
177        {               
178                // First configure the KNN
179                NNConfig simConfig = new NNConfig();
180                // Set the average() global similarity function for the description of the case
181                simConfig.setDescriptionSimFunction(new Average());
182                // The accomodation attribute uses the equal() local similarity function
183                simConfig.addMapping(new Attribute("Accomodation", TravelDescription.class), new Equal());
184                // For the duration attribute we are going to set its local similarity function and the weight
185                Attribute duration = new Attribute("Duration", TravelDescription.class);
186                simConfig.addMapping(duration, new Interval(31));
187                simConfig.setWeight(duration, 0.5);
188                // HolidayType --> equal()
189                simConfig.addMapping(new Attribute("HolidayType", TravelDescription.class), new Equal());
190                // NumberOfPersons --> equal()
191                simConfig.addMapping(new Attribute("NumberOfPersons", TravelDescription.class), new Equal());
192                // Price --> InrecaLessIsBetter()
193                simConfig.addMapping(new Attribute("Price", TravelDescription.class), new Interval(4000));
194                
195                
196                // A bit of verbose
197                System.out.println("Query Description:");
198                System.out.println(query.getDescription());
199                System.out.println();
200                
201                // Execute NN
202                Collection<RetrievalResult> eval = NNScoringMethod.evaluateSimilarity(_caseBase.getCases(), query, simConfig);
203                
204                // Select k cases
205                eval = SelectCases.selectTopKRR(eval, 5);
206                
207                // Print the retrieval
208                System.out.println("Retrieved cases:");
209                for(RetrievalResult nse: eval)
210                        System.out.println(nse);
211                
212
213        }
214
215        /* (non-Javadoc)
216         * @see jcolibri.cbraplications.BasicCBRApplication#postCycle()
217         */
218        public void postCycle() throws ExecutionException {
219                this._caseBase.close();
220
221        }
222
223        /**
224         * Main function
225         */
226        public static void main(String[] args) {
227                // Launch DDBB manager
228                es.ucm.fdi.gaia.jcolibri.test.database.HSQLDBserver.init();
229                // Create the application
230                Test1 test1 = new Test1();
231                try {
232                        // Configure it
233                        test1.configure();
234                        // Run the precycle --> load the cases
235                        test1.preCycle();
236                        
237                        //BufferedReader reader  = new BufferedReader(new InputStreamReader(System.in));                        
238                        //do
239                        {               
240                                // Configure the query. Queries only have description.
241                                TravelDescription queryDesc = new TravelDescription();
242                                queryDesc.setAccomodation("ThreeStars");
243                                queryDesc.setDuration(7);
244                                queryDesc.setHolidayType("Recreation");
245                                queryDesc.setNumberOfPersons(2);
246                                queryDesc.setPrice(700);
247                                
248                                CBRQuery query = new CBRQuery();
249                                query.setDescription(queryDesc);
250                                
251                                // Run a cycle with the query
252                                test1.cycle(query);
253                                
254                                System.out.println("Cycle finished. Type exit to idem or enter to repeat the cycle");
255                        }
256                        //while(!reader.readLine().equals("exit"));
257                        
258                        // Run the postcycle
259                        test1.postCycle();
260
261                        //Shutdown DDBB manager
262                        es.ucm.fdi.gaia.jcolibri.test.database.HSQLDBserver.shutDown();
263                } catch (Exception e) {
264                        System.out.println(e.getMessage());
265                        e.printStackTrace();
266                }
267        }
268
269}