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