/**
* Copyright (C) 2012-2017 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* - Apache License, version 2.0
* - Apache Software License, version 1.0
* - GNU Lesser General Public License, version 3
* - Mozilla Public License, versions 1.0, 1.1 and 2.0
* - Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* License version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
package org.n52.sos.ds.hibernate;
import geodb.GeoDB;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.jdbc.Work;
import org.hibernate.mapping.Table;
import org.hibernate.spatial.dialect.h2geodb.GeoDBDialect;
import org.n52.sos.cache.ctrl.ScheduledContentCacheControllerSettings;
import org.n52.sos.config.sqlite.SQLiteSessionFactory;
import org.n52.sos.ds.ConnectionProviderException;
import org.n52.sos.ds.Datasource;
import org.n52.sos.exception.ConfigurationException;
import org.n52.sos.ogc.ows.OwsExceptionReport;
import org.n52.sos.service.Configurator;
import org.n52.sos.service.SosContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* @since 4.0.0
*
*/
public class H2Configuration {
private static final Logger LOG = LoggerFactory.getLogger(H2Configuration.class);
private static final String HIBERNATE_CONNECTION_URL = SQLiteSessionFactory.HIBERNATE_CONNECTION_URL;
private static final String HIBERNATE_CONNECTION_DRIVER_CLASS =
SQLiteSessionFactory.HIBERNATE_CONNECTION_DRIVER_CLASS;
private static final String HIBERNATE_DIALECT = SQLiteSessionFactory.HIBERNATE_DIALECT;
private static final String H2_DRIVER = "org.h2.Driver";
private static final String H2_CONNECTION_URL = "jdbc:h2:mem:sos;DB_CLOSE_DELAY=-1;MULTI_THREADED=true";
private static Properties properties = new Properties() {
private static final long serialVersionUID = 3109256773218160485L;
{
put(HIBERNATE_CONNECTION_URL, H2_CONNECTION_URL);
put(HIBERNATE_CONNECTION_DRIVER_CLASS, H2_DRIVER);
put(HIBERNATE_DIALECT, GeoDBDialect.class.getName());
put(SessionFactoryProvider.HIBERNATE_RESOURCES, getResources());
put(Datasource.class.getCanonicalName(), MockDatasource.class.getCanonicalName());
}
private List<String> getResources() {
List<String> resources = Lists.newLinkedList();
// core
resources.add("mapping/core/Codespace.hbm.xml");
resources.add("mapping/core/FeatureOfInterest.hbm.xml");
resources.add("mapping/core/FeatureOfInterestType.hbm.xml");
resources.add("mapping/core/ObservableProperty.hbm.xml");
resources.add("mapping/core/Offering.hbm.xml");
resources.add("mapping/core/Procedure.hbm.xml");
resources.add("mapping/core/ProcedureDescriptionFormat.hbm.xml");
resources.add("mapping/core/Unit.hbm.xml");
resources.add("mapping/core/ObservationConstellation.hbm.xml");
resources.add("mapping/core/ObservationType.hbm.xml");
// transactional module
resources.add("mapping/transactional/RelatedFeature.hbm.xml");
resources.add("mapping/transactional/RelatedFeatureRole.hbm.xml");
resources.add("mapping/transactional/ResultTemplate.hbm.xml");
resources.add("mapping/transactional/ValidProcedureTime.hbm.xml");
resources.add("mapping/transactional/TFeatureOfInterest.hbm.xml");
resources.add("mapping/transactional/TObservableProperty.hbm.xml");
resources.add("mapping/transactional/TOffering.hbm.xml");
resources.add("mapping/transactional/TProcedure.hbm.xml");
// old observation concept
// resources.add("mapping/old/observation/Observation.hbm.xml");
// resources.add("mapping/old/observation/ObservationInfo.hbm.xml");
// resources.add("mapping/old/spatialFilteringProfile/SpatialFitleringProfile.hbm.xml");
// series observation concept, needs changes in tests
resources.add("mapping/series/observation/Series.hbm.xml");
resources.add("mapping/series/observation/SeriesObservation.hbm.xml");
resources.add("mapping/series/observation/SeriesObservationInfo.hbm.xml");
resources.add("mapping/series/observation/SeriesObservationTime.hbm.xml");
resources.add("mapping/series/observation/SeriesValue.hbm.xml");
resources.add("mapping/series/observation/SeriesValueTime.hbm.xml");
return resources;
}
};
private static final Object LOCK = new Object();
private static H2Configuration instance;
private File tempDir;
private Configuration configuration;
private String[] createScript;
private String[] dropScript;
public static void assertInitialized() {
synchronized (LOCK) {
if (instance == null) {
try {
instance = new H2Configuration();
} catch (final IOException ex) {
throw new RuntimeException(ex);
} catch (final OwsExceptionReport ex) {
throw new RuntimeException(ex);
} catch (final ConnectionProviderException ex) {
throw new RuntimeException(ex);
}
}
}
}
public static Session getSession() {
H2Configuration.assertInitialized();
try {
return (Session) Configurator.getInstance().getDataConnectionProvider().getConnection();
} catch (final ConnectionProviderException ex) {
throw new RuntimeException(ex);
}
}
public static void returnSession(final Session session) {
if (session != null) {
Configurator.getInstance().getDataConnectionProvider().returnConnection(session);
}
}
public static void recreate() {
synchronized (LOCK) {
if (instance == null) {
throw new IllegalStateException("Database is not initialized");
}
Session session = null;
Transaction transaction = null;
try {
session = getSession();
transaction = session.beginTransaction();
session.doWork(new Work() {
@Override
public void execute(final Connection connection) throws SQLException {
Statement stmt = null;
try {
stmt = connection.createStatement();
for (final String cmd : instance.getDropScript()) {
stmt.addBatch(cmd);
}
for (final String cmd : instance.getCreateScript()) {
stmt.addBatch(cmd);
}
stmt.executeBatch();
} finally {
if (stmt != null) {
stmt.close();
}
}
}
});
transaction.commit();
} catch (final HibernateException e) {
if (transaction != null) {
transaction.rollback();
}
throw e;
} finally {
returnSession(session);
}
}
}
public static void truncate() {
synchronized (LOCK) {
if (instance == null) {
throw new IllegalStateException("Database is not initialized");
}
final Iterator<Table> tableMappings = instance.getConfiguration().getTableMappings();
final List<String> tableNames = new LinkedList<String>();
GeoDBDialect dialect = new GeoDBDialect();
while (tableMappings.hasNext()) {
tableNames.add(tableMappings.next().getQuotedName(dialect));
}
Session session = null;
Transaction transaction = null;
try {
session = getSession();
transaction = session.beginTransaction();
session.doWork(new Work() {
@Override
public void execute(final Connection connection) throws SQLException {
Statement stmt = null;
try {
stmt = connection.createStatement();
stmt.addBatch("SET REFERENTIAL_INTEGRITY FALSE");
for (final String table : tableNames) {
stmt.addBatch("DELETE FROM " + table);
}
stmt.addBatch("SET REFERENTIAL_INTEGRITY TRUE");
stmt.executeBatch();
} finally {
if (stmt != null) {
stmt.close();
}
}
}
});
transaction.commit();
} catch (final HibernateException e) {
if (transaction != null) {
transaction.rollback();
}
throw e;
} finally {
returnSession(session);
}
}
}
private H2Configuration() throws IOException, OwsExceptionReport, ConnectionProviderException {
init();
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
cleanup();
}
}));
}
private void cleanup() {
try {
final Configurator configurator = Configurator.getInstance();
if (configurator != null) {
configurator.cleanup();
}
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
try {
final File directory = getTempDir();
if (directory != null && directory.exists()) {
for (File file : directory.listFiles()) {
if (file.exists()) {
FileUtils.forceDelete(file);
}
}
FileUtils.forceDelete(directory);
}
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
private void setDefaultSettings() {
ScheduledContentCacheControllerSettings.CACHE_UPDATE_INTERVAL_DEFINITION.setDefaultValue(0);
}
private File getTempDir() {
return tempDir;
}
private void setTempDir(final File aTempDir) {
tempDir = aTempDir;
}
private void createTempDir() throws IOException {
setTempDir(File.createTempFile("hibernate-test-case", ""));
getTempDir().delete();
FileUtils.forceMkdir(getTempDir());
SosContextListener.setPath(getTempDir().getAbsolutePath());
}
private void createConfigurator() throws ConfigurationException {
Configurator.createInstance(properties, getTempDir().getAbsolutePath());
}
private void prepareDatabase() {
Connection conn = null;
Statement stmt = null;
try {
Class.forName(H2_DRIVER);
conn = DriverManager.getConnection(H2_CONNECTION_URL);
GeoDB.InitGeoDB(conn);
stmt = conn.createStatement();
configuration = new Configuration().configure("/sos-hibernate.cfg.xml");
@SuppressWarnings("unchecked")
List<String> resources = (List<String>) properties.get(SessionFactoryProvider.HIBERNATE_RESOURCES);
for (String resource : resources) {
configuration.addResource(resource);
}
final GeoDBDialect dialect = new GeoDBDialect();
createScript = getCreateSrcipt(configuration.generateSchemaCreationScript(dialect));
dropScript = getDropScript(configuration.generateDropSchemaScript(dialect));
for (final String s : createScript) {
LOG.debug("Executing {}", s);
stmt.execute(s);
}
} catch (final ClassNotFoundException ex) {
throw new RuntimeException(ex);
} catch (final SQLException ex) {
throw new RuntimeException(ex);
} catch (MappingException ex) {
throw new RuntimeException(ex);
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (final SQLException ex) {
}
}
if (conn != null) {
try {
conn.close();
} catch (final SQLException ex) {
}
}
}
}
private String[] getCreateSrcipt(String[] generateSchemaCreationScript) {
List<String> finalScript = Lists.newArrayList();
Set<String> nonDublicates = Sets.newHashSet();
Set<String> nonDuplicateCreate = Sets.newHashSet();
for (final String s : generateSchemaCreationScript) {
if (!nonDublicates.contains(s)) {
if (s.toLowerCase().startsWith("create table")) {
String substring = s.substring(0, s.indexOf("("));
if (!nonDuplicateCreate.contains(substring)) {
nonDuplicateCreate.add(substring);
LOG.debug("Executing {}", s);
finalScript.add(s);
}
} else {
LOG.debug("Executing {}", s);
finalScript.add(s);
}
nonDublicates.add(s);
}
}
return finalScript.toArray(new String[finalScript.size()]);
}
private String[] getDropScript(String[] generateDropSchemaScript) {
Set<String> nonDuplicates = Sets.newHashSet();
List<String> finalScript = Lists.newArrayList();
for (String string : generateDropSchemaScript) {
if (!nonDuplicates.contains(string)) {
finalScript.add(string);
nonDuplicates.add(string);
}
}
return finalScript.toArray(new String[finalScript.size()]);
}
private void init() throws ConfigurationException, IOException {
setDefaultSettings();
createTempDir();
prepareDatabase();
createConfigurator();
}
public Configuration getConfiguration() {
return configuration;
}
public String[] getCreateScript() {
return createScript;
}
public String[] getDropScript() {
return dropScript;
}
}