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