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