View Javadoc

1   /*
2    * Copyright 2006 - 2012 Christina Bohk and Roland Ewald
3    *  
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at 
7    *  
8    *  http://www.apache.org/licenses/LICENSE-2.0
9    *  
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package p3j.database.hibernate;
17  
18  import java.sql.Connection;
19  import java.sql.DriverManager;
20  import java.sql.SQLException;
21  import java.util.List;
22  import java.util.logging.Level;
23  
24  import org.hibernate.SessionFactory;
25  import org.hibernate.Transaction;
26  import org.hibernate.cfg.Configuration;
27  import org.hibernate.classic.Session;
28  import org.hibernate.criterion.Order;
29  import org.hibernate.criterion.Restrictions;
30  import org.hibernate.tool.hbm2ddl.SchemaExport;
31  import org.jamesii.SimSystem;
32  import org.jamesii.core.data.DBConnectionData;
33  import org.jamesii.core.util.StopWatch;
34  
35  import p3j.database.IP3MDatabase;
36  import p3j.database.IProjectionResultsIterator;
37  import p3j.experiment.results.ResultsOfTrial;
38  import p3j.gui.misc.P3JConfigFile;
39  import p3j.misc.IProgressObserver;
40  import p3j.misc.MatrixDimension;
41  import p3j.misc.Misc;
42  import p3j.misc.math.Matrix;
43  import p3j.misc.math.Matrix2D;
44  import p3j.pppm.ProjectionModel;
45  import p3j.pppm.parameters.Parameter;
46  import p3j.pppm.parameters.ParameterAssignment;
47  import p3j.pppm.parameters.ParameterInstance;
48  import p3j.pppm.parameters.Parameters;
49  import p3j.pppm.parameters.Population;
50  import p3j.pppm.sets.Set;
51  import p3j.pppm.sets.SetType;
52  
53  /**
54   * Implementation of {@link IP3MDatabase} based on Hibernate.
55   * 
56   * Created: August 17, 2008
57   * 
58   * @author Christina Bohk
59   * @author Roland Ewald
60   * 
61   */
62  public class P3MDatabase implements IP3MDatabase {
63  
64    /** Path to hibernate configuration file. */
65    private static String hibernateConfigFile = Misc.DEFAULT_HIBERNATE_CONFIG_FILE;
66  
67    /** The default flush frequency (once every x commits). */
68    private static final int DEFAULT_FLUSH_FREQ = 50;
69  
70    /** Flag to determine the flushing policy of the database. */
71    private boolean alwaysFlush = true;
72  
73    /** Hibernate configuration. */
74    private final Configuration config;
75  
76    /** Factory to create sessions. */
77    private SessionFactory sessionFactory;
78  
79    /** Current Hibernate session. */
80    private Session session;
81  
82    /** Session to store results. */
83    private Session resultStorageSession;
84  
85    /** Counts how many results have been stored so far. */
86    private int resultStorageCounter;
87  
88    /**
89     * Gives the frequency with which the session to store the results will be
90     * flushed and cleared.
91     */
92    private int resultStorageFlushFrequency = DEFAULT_FLUSH_FREQ;
93  
94    /**
95     * Constructor using the default configuration file.
96     */
97    public P3MDatabase() {
98      this(P3MDatabase.hibernateConfigFile);
99    }
100 
101   /**
102    * Constructor for custom configuration.
103    * 
104    * @param configurationFile
105    *          the name of the configuration file
106    */
107   public P3MDatabase(String configurationFile) {
108     config = new Configuration().configure(configurationFile);
109   }
110 
111   @Override
112   public void init(DBConnectionData dbConn, P3JConfigFile configFile) {
113     getConfig()
114         .setProperty("hibernate.connection.username", dbConn.getUser())
115         .setProperty("hibernate.connection.url", dbConn.getURL())
116         .setProperty(Misc.PREF_HIBERNATE_DRIVER_PROPERTY, dbConn.getDriver())
117         .setProperty(Misc.PREF_HIBERNATE_DIALECT_PROPERTY,
118             Misc.HIBERNATE_DIALECTS.get(configFile.get(Misc.PREF_DB_TYPE)));
119 
120     if (!dbConn.getPassword().isEmpty())
121       getConfig().setProperty("hibernate.connection.password",
122           dbConn.getPassword());
123 
124     sessionFactory = getConfig().buildSessionFactory();
125   }
126 
127   @Override
128   public void open() {
129     if (session == null) {
130       session = sessionFactory.openSession();
131     }
132   }
133 
134   @Override
135   public void clear() {
136     SimSystem.report(Level.INFO, "CLEARING DB...");
137     SchemaExport export = new SchemaExport(getConfig());
138     export.create(true, true);
139   }
140 
141   @Override
142   public void close() {
143     if (session != null) {
144       session.close();
145     }
146     if (sessionFactory != null) {
147       sessionFactory.close();
148     }
149   }
150 
151   /**
152    * DB-write hook for switching flushing on or off.
153    */
154   protected void dbChanged() {
155     if (alwaysFlush) {
156       session.flush();
157     }
158   }
159 
160   /**
161    * Saves an object with hibernate.
162    * 
163    * @param o
164    *          the object to be saved
165    */
166   public void save(Object o) {
167     Transaction t = session.beginTransaction();
168     session.save(o);
169     t.commit();
170     dbChanged();
171   }
172 
173   /**
174    * Deletes an object with hibernate.
175    * 
176    * @param o
177    *          the object to be deleted
178    */
179   public void delete(Object o) {
180     Transaction t = session.beginTransaction();
181     session.delete(o);
182     t.commit();
183     dbChanged();
184   }
185 
186   // Parameter
187 
188   @Override
189   public Parameter newParameter(String name, int sortIndex, boolean genDep,
190       MatrixDimension height, MatrixDimension width, Population population) {
191     Parameter param = getParameter(name);
192     if (param == null) {
193       param = new Parameter(sortIndex, genDep, name, height, width, population);
194       save(param);
195     }
196     return param;
197   }
198 
199   @Override
200   public Parameter getParameter(String name) {
201     List<Parameter> parameters = Misc.autoCast(session.createQuery(
202         "from Parameter where name='" + name + "'").list());
203     if (parameters.size() > 1) {
204       throw new ConstraintException(
205           "Ambiguity: there are two parameters with the same name '" + name
206               + "'");
207     }
208     if (parameters.size() == 1) {
209       return parameters.get(0);
210     }
211     return null;
212   }
213 
214   @Override
215   public List<Parameter> getAllParameters() {
216     List<Parameter> parameters = Misc.autoCast(session.createQuery(
217         "from Parameter").list());
218     return parameters;
219   }
220 
221   @Override
222   public boolean deleteParameter(Parameter parameter) {
223     delete(parameter);
224     return true;
225   }
226 
227   // ParameterInstance
228 
229   @Override
230   public ParameterInstance newParameterInstance(int comparisonIndex,
231       Parameter param, int generation) {
232     ParameterInstance instance = getParameterInstance(param, generation);
233     if (instance == null) {
234       instance = new ParameterInstance(comparisonIndex, param, generation);
235       save(instance);
236     }
237     return instance;
238   }
239 
240   @Override
241   public ParameterInstance getParameterInstance(Parameter param, int generation) {
242     List<ParameterInstance> instances = Misc.autoCast(session
243         .createCriteria(ParameterInstance.class)
244         .add(Restrictions.eq("parameter", param))
245         .add(Restrictions.eq("generation", generation)).list());
246     if (instances.size() > 1) {
247       throw new ConstraintException(
248           "Ambiguity: there are two parameter instances for parameter '"
249               + param.getName() + "' and generation " + generation);
250     }
251     if (instances.size() == 1) {
252       return instances.get(0);
253     }
254     return null;
255   }
256 
257   @Override
258   public List<ParameterInstance> getAllParameterInstances() {
259     List<ParameterInstance> instances = Misc.autoCast(session.createQuery(
260         "from ParameterInstance").list());
261     return instances;
262   }
263 
264   @Override
265   public boolean deleteParameterInstance(ParameterInstance instance) {
266     delete(instance);
267     return true;
268   }
269 
270   // TODO: Add method to remove orphaned matrices from the database.
271   @Override
272   public Matrix newMatrix(Matrix2D value) {
273     Matrix matrix = new Matrix(value);
274     save(matrix);
275     return matrix;
276   }
277 
278 
279   @Override
280   public boolean deleteMatrix(Matrix matrix) {
281     delete(matrix);
282     return true;
283   }
284 
285   // ParameterAssignment
286 
287   @Override
288   public ParameterAssignment newParameterAssignment(
289       ParameterInstance paramInstance, String name, String description,
290       double probability, double deviation, Matrix2D value) {
291     Matrix matrix = newMatrix(value);
292     ParameterAssignment assignment = new ParameterAssignment(paramInstance,
293         name, description, probability, deviation, matrix);
294     save(assignment);
295     return assignment;
296   }
297 
298   @Override
299   public List<ParameterAssignment> getAllParameterAssignments(
300       ParameterInstance param) {
301     List<ParameterAssignment> assignments = Misc.autoCast(session
302         .createCriteria(ParameterAssignment.class)
303         .add(Restrictions.eq("paramInstance", param)).list());
304     return assignments;
305   }
306 
307   @Override
308   public boolean deleteParameterAssignment(ParameterAssignment assignment) {
309     delete(assignment);
310     return true;
311   }
312 
313   @Override
314   public void saveParameterAssignment(ParameterAssignment assignment) {
315     save(assignment);
316   }
317 
318   // Set
319 
320   @Override
321   public Set newSet(List<ParameterInstance> defParams, String name,
322       String desc, double prob) {
323     Set set = new Set(defParams, name, desc, prob);
324     save(set);
325     return set;
326   }
327 
328   @Override
329   public void saveSet(Set set) {
330     save(set);
331   }
332 
333   @Override
334   public List<Set> getAllSets() {
335     return Misc.autoCast(session.createCriteria(Set.class).list());
336   }
337 
338   @Override
339   public boolean deleteSet(Set set) {
340     delete(set);
341     return true;
342   }
343 
344   // SetType
345 
346   @Override
347   public SetType newSetType(String name, String description) {
348     SetType setType = new SetType(name, description);
349     save(setType);
350     return setType;
351   }
352 
353   @Override
354   public List<SetType> getAllSetTypes() {
355     List<SetType> setTypes = Misc.autoCast(session
356         .createCriteria(SetType.class).list());
357     return setTypes;
358   }
359 
360   @Override
361   public void saveSetType(SetType setType) {
362     save(setType);
363   }
364 
365   @Override
366   public boolean deleteSeType(SetType setType) {
367     delete(setType);
368     return true;
369   }
370 
371   // Projection
372 
373   @Override
374   public void newProjection(ProjectionModel projection) {
375     // Initializing all parameter instances
376     Parameters params = new Parameters(projection.getSubPopulationModel());
377     List<Parameter> parameters = params.getParams();
378     List<ParameterInstance> allInstances = projection
379         .getAllParameterInstances();
380     int comparisonIndex = 0;
381     for (int j = 0; j < projection.getGenerations(); j++) {
382       for (int i = 0; i < parameters.size(); i++) {
383         Parameter parameter = parameters.get(i);
384         if (parameter.isGenerationDependent()) {
385           allInstances
386               .add(newParameterInstance(++comparisonIndex, parameter, j));
387         } else if (j == 0) {
388           allInstances.add(newParameterInstance(++comparisonIndex, parameter,
389               -1));
390         }
391       }
392     }
393     projection.init();
394     save(projection);
395   }
396 
397   @Override
398   public boolean deleteProjection(ProjectionModel projection,
399       IProgressObserver observer) {
400     deleteAllResults(projection, observer);
401     if (observer != null) {
402       observer.addWaypoints(1);
403       observer.incrementProgress("Deleting projection...");
404     }
405     delete(projection);
406     return true;
407   }
408 
409   @Override
410   public List<ProjectionModel> getAllProjections() {
411     List<ProjectionModel> projections = Misc.autoCast(session.createQuery(
412         "from ProjectionModel").list());
413     return projections;
414   }
415 
416   @Override
417   public ProjectionModel getProjectionByID(int id) {
418     List<ProjectionModel> pModels = Misc.autoCast(session
419         .createCriteria(ProjectionModel.class).add(Restrictions.eq("ID", id))
420         .list());
421     if (pModels.size() > 1) {
422       throw new ConstraintException(
423           "DB consistency: Two duplicate projection models with the same ID '"
424               + id + "' !");
425     }
426     if (pModels.size() == 1) {
427       return pModels.get(0);
428     }
429     return null;
430   }
431 
432   @Override
433   public void saveProjection(ProjectionModel projection) {
434     save(projection);
435   }
436 
437   /**
438    * Test connection to database.
439    * 
440    * @param dbConnData
441    *          the database connection data
442    * 
443    * @return the exception (null in case of success)
444    */
445   public static Exception testConnection(DBConnectionData dbConnData) {
446     Connection c = null;
447     try {
448       c = DriverManager.getConnection(dbConnData.getURL(),
449           dbConnData.getUser(), dbConnData.getPassword());
450       if (c == null) {
451         throw new IllegalArgumentException(
452             "Connection created by driver manager is null.");
453       }
454     } catch (Exception ex) {
455       return ex;
456     } finally {
457       try {
458         if (c != null) {
459           c.close();
460         }
461       } catch (SQLException e) {
462         return e;
463       }
464     }
465     return null;
466   }
467 
468   /**
469    * Test connection to database.
470    * 
471    * @param dbURL
472    *          the URL of the database
473    * @param dbUserName
474    *          the DB user name
475    * @param dbPassword
476    *          the DB password
477    * 
478    * @return the exception
479    */
480   public static Exception testConnection(String dbURL, String dbUserName,
481       String dbPassword) {
482     return testConnection(new DBConnectionData(dbURL, dbUserName, dbPassword,
483         null));
484   }
485 
486   @Override
487   public synchronized void saveTrialResults(ResultsOfTrial resultOfTrial) {
488     if (resultStorageSession == null) {
489       resultStorageSession = sessionFactory.openSession();
490     }
491     StopWatch sw = new StopWatch();
492     sw.start();
493     Transaction resultStorageTransaction = resultStorageSession
494         .beginTransaction();
495     resultStorageSession.save(resultOfTrial);
496     resultStorageTransaction.commit();
497     resultStorageCounter++;
498     if (resultStorageCounter % resultStorageFlushFrequency == 0) {
499       resultStorageSession.flush();
500       resultStorageSession.clear();
501     }
502     sw.stop();
503     SimSystem.report(Level.INFO,
504         "Time for result storage in database:" + sw.elapsedMilliseconds());
505   }
506 
507   @Override
508   public List<ResultsOfTrial> getAllResults(ProjectionModel projection) {
509     List<ResultsOfTrial> results = Misc.autoCast(session
510         .createCriteria(ResultsOfTrial.class)
511         .add(Restrictions.eq("projection", projection))
512         .addOrder(Order.asc("ID")).list());
513     return results;
514   }
515 
516   @Override
517   public void deleteResult(ResultsOfTrial resultOfTrial) {
518     delete(resultOfTrial);
519   }
520 
521   @Override
522   public void deleteAllResults(ProjectionModel projection,
523       IProgressObserver observer) {
524     List<ResultsOfTrial> results = getAllResults(projection);
525 
526     if (observer != null) {
527       observer.addWaypoints(results.size());
528     }
529 
530     while (!results.isEmpty()) {
531       ResultsOfTrial result = results.remove(results.size() - 1);
532       Transaction t = session.beginTransaction();
533       session.delete(result);
534       t.commit();
535       session.evict(result);
536       if (observer != null) {
537         observer.incrementProgress("Deleted result with ID " + result.getID());
538         if (observer.isCancelled()) {
539           observer.taskCanceled();
540           break;
541         }
542       }
543     }
544     dbChanged();
545   }
546 
547   @Override
548   public IProjectionResultsIterator getResultIterator(ProjectionModel projection) {
549     return new ProjectionResultsIterator(sessionFactory, projection.getID());
550   }
551 
552   @Override
553   public void clearCache(Object o) {
554     session.evict(o);
555     session.flush();
556   }
557 
558   /**
559    * Gets the hibernate configuration file.
560    * 
561    * @return the hibernate configuration file
562    */
563   public static String getHibernateConfigFile() {
564     return hibernateConfigFile;
565   }
566 
567   /**
568    * Sets the hibernate configuration file.
569    * 
570    * @param hibernateConfigFile
571    *          the new hibernate configuration file
572    */
573   public static void setHibernateConfigFile(String hibernateConfigFile) {
574     P3MDatabase.hibernateConfigFile = hibernateConfigFile;
575   }
576 
577   public Configuration getConfig() {
578     return config;
579   }
580 }