[Major] Added a DB initialization mechanism

Now PostgreSQL implementation can import a configured dataStore if the persistence handler configuration property ‘allowDataInitOnSchemaCreate’. For this to work, the schema must have a migration status of CREATED, or DROPPED_CREATED on startup, or using the new method PersistenceHandler.performDbInitialization()-method. Further the properties ‘allowSchemaDrop’ and ‘allowSchemaCreate’ must be enabled.
This commit is contained in:
Robert von Burg 2014-09-23 23:57:11 +02:00
parent 461af7b24f
commit 96740a571c
20 changed files with 639 additions and 114 deletions

View File

@ -32,11 +32,11 @@ public enum ComponentState {
throw getIllegalStateEx(newState);
break;
case SETUP:
if (newState != ComponentState.INITIALIZED && newState != STOPPED)
if (newState != ComponentState.INITIALIZED && newState != STOPPED && newState != DESTROYED)
throw getIllegalStateEx(newState);
break;
case INITIALIZED:
if (newState != ComponentState.STARTED && newState != STOPPED)
if (newState != ComponentState.STARTED && newState != STOPPED && newState != DESTROYED)
throw getIllegalStateEx(newState);
break;
case STARTED:

View File

@ -25,6 +25,21 @@ import li.strolch.runtime.StrolchConstants;
*/
public interface RealmHandler {
/**
* Name of the transaction which boots the agent
*/
public static final String SYSTEM_USER_AGENT = "agent"; //$NON-NLS-1$
/**
* Name of the system user which boots the agent
*/
public static final String AGENT_BOOT = "agent_boot"; //$NON-NLS-1$
/**
* Name of the system user which performs DB Initialization
*/
public static final String SYSTEM_USER_DB_INITIALIZER = "db_initializer";
/**
* Returns the names of the configured {@link StrolchRealm StrolchRealms}
*

View File

@ -15,8 +15,6 @@
*/
package li.strolch.agent.impl;
import static ch.eitchnet.utils.helper.StringHelper.DOT;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
@ -38,13 +36,11 @@ import ch.eitchnet.utils.dbc.DBC;
*/
public class DefaultRealmHandler extends StrolchComponent implements RealmHandler {
private static final String SYSTEM_USER_AGENT = "agent"; //$NON-NLS-1$
public static final String AGENT_BOOT = "agent_boot"; //$NON-NLS-1$
public static final String PROP_ENABLE_AUDIT_TRAIL = "enableAuditTrail"; //$NON-NLS-1$
public static final String PROP_ENABLE_AUDIT_TRAIL_FOR_READ = "enableAuditTrailForRead"; //$NON-NLS-1$
public static final String PROP_ENABLE_OBSERVER_UPDATES = "enableObserverUpdates"; //$NON-NLS-1$
public static final String PREFIX_DATA_STORE_MODE = "dataStoreMode"; //$NON-NLS-1$
public static final String PREFIX_DATA_STORE_FILE = "dataStoreFile"; //$NON-NLS-1$
public static final String PROP_REALMS = "realms"; //$NON-NLS-1$
protected Map<String, InternalStrolchRealm> realms;
@ -80,7 +76,7 @@ public class DefaultRealmHandler extends StrolchComponent implements RealmHandle
this.realms = new HashMap<>();
String[] realms = configuration.getStringArray(PROP_REALMS, StrolchConstants.DEFAULT_REALM);
for (String realmName : realms) {
String dataStoreModeKey = makeRealmKey(realmName, PREFIX_DATA_STORE_MODE);
String dataStoreModeKey = StrolchConstants.makeRealmKey(realmName, PREFIX_DATA_STORE_MODE);
String realmMode = configuration.getString(dataStoreModeKey, null);
DataStoreMode dataStoreMode = DataStoreMode.parseDataStoreMode(realmMode);
InternalStrolchRealm realm = dataStoreMode.createRealm(realmName);
@ -89,13 +85,6 @@ public class DefaultRealmHandler extends StrolchComponent implements RealmHandle
super.setup(configuration);
}
public static String makeRealmKey(String realmName, String key) {
String realmKey = key;
if (!realmName.equals(StrolchConstants.DEFAULT_REALM))
realmKey += DOT + realmName;
return realmKey;
}
@Override
public void initialize(ComponentConfiguration configuration) {

View File

@ -24,6 +24,7 @@ import li.strolch.agent.api.ResourceMap;
import li.strolch.persistence.api.PersistenceHandler;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.persistence.inmemory.InMemoryPersistence;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.configuration.ComponentConfiguration;
import ch.eitchnet.privilege.model.Certificate;
import ch.eitchnet.privilege.model.PrivilegeContext;
@ -82,7 +83,7 @@ public class EmptyRealm extends InternalStrolchRealm {
this.resourceMap = new TransactionalResourceMap();
this.orderMap = new TransactionalOrderMap();
String enableAuditKey = DefaultRealmHandler.makeRealmKey(getRealm(),
String enableAuditKey = StrolchConstants.makeRealmKey(getRealm(),
DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL);
if (configuration.getBoolean(enableAuditKey, Boolean.FALSE)) {
this.auditTrail = new TransactionalAuditTrail();

View File

@ -32,6 +32,7 @@ import li.strolch.agent.api.OrderMap;
import li.strolch.agent.api.ResourceMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.StrolchRootElement;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.configuration.ComponentConfiguration;
import org.slf4j.Logger;
@ -78,18 +79,18 @@ public abstract class InternalStrolchRealm implements StrolchRealm {
public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
String propTryLockTimeUnit = DefaultRealmHandler.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME_UNIT);
String propTryLockTime = DefaultRealmHandler.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME);
String propTryLockTimeUnit = StrolchConstants.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME_UNIT);
String propTryLockTime = StrolchConstants.makeRealmKey(this.realm, PROP_TRY_LOCK_TIME);
String enableAuditKey = DefaultRealmHandler.makeRealmKey(getRealm(),
String enableAuditKey = StrolchConstants.makeRealmKey(getRealm(),
DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL);
this.auditTrailEnabled = configuration.getBoolean(enableAuditKey, Boolean.FALSE);
String enableAuditForReadKey = DefaultRealmHandler.makeRealmKey(getRealm(),
String enableAuditForReadKey = StrolchConstants.makeRealmKey(getRealm(),
DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL_FOR_READ);
this.auditTrailEnabledForRead = configuration.getBoolean(enableAuditForReadKey, Boolean.FALSE);
String updateObserversKey = DefaultRealmHandler.makeRealmKey(getRealm(),
String updateObserversKey = StrolchConstants.makeRealmKey(getRealm(),
DefaultRealmHandler.PROP_ENABLE_OBSERVER_UPDATES);
this.updateObservers = configuration.getBoolean(updateObserversKey, Boolean.FALSE);
if (this.updateObservers)

View File

@ -0,0 +1,34 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*/
package li.strolch.agent.impl;
import li.strolch.model.Order;
import li.strolch.model.Resource;
import li.strolch.model.xml.StrolchElementListener;
import li.strolch.persistence.api.OrderDao;
import li.strolch.persistence.api.ResourceDao;
import li.strolch.persistence.api.StrolchTransaction;
public class StoreToDaoElementListener implements StrolchElementListener {
private StrolchTransaction tx;
private ResourceDao resourceDao;
private OrderDao orderDao;
public StoreToDaoElementListener(StrolchTransaction tx) {
this.tx = tx;
this.resourceDao = tx.getPersistenceHandler().getResourceDao(this.tx);
this.orderDao = tx.getPersistenceHandler().getOrderDao(this.tx);
}
@Override
public void notifyResource(Resource resource) {
this.resourceDao.save(resource);
}
@Override
public void notifyOrder(Order order) {
this.orderDao.save(order);
}
}

View File

@ -27,6 +27,7 @@ import li.strolch.model.xml.XmlModelSaxFileReader;
import li.strolch.persistence.api.PersistenceHandler;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.persistence.inmemory.InMemoryPersistence;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.configuration.StrolchConfigurationException;
import ch.eitchnet.privilege.model.Certificate;
@ -39,8 +40,6 @@ import ch.eitchnet.utils.helper.StringHelper;
*/
public class TransientRealm extends InternalStrolchRealm {
public static final String PREFIX_DATA_STORE_FILE = "dataStoreFile"; //$NON-NLS-1$
private ResourceMap resourceMap;
private OrderMap orderMap;
private AuditTrail auditTrail;
@ -88,7 +87,7 @@ public class TransientRealm extends InternalStrolchRealm {
public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
super.initialize(container, configuration);
String key = DefaultRealmHandler.makeRealmKey(getRealm(), PREFIX_DATA_STORE_FILE);
String key = StrolchConstants.makeRealmKey(getRealm(), DefaultRealmHandler.PREFIX_DATA_STORE_FILE);
if (!configuration.hasProperty(key)) {
String msg = "There is no data store file for realm {0}. Set a property with key {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, getRealm(), key);

View File

@ -15,7 +15,13 @@
*/
package li.strolch.persistence.api;
import li.strolch.agent.api.AuditTrail;
import li.strolch.agent.api.OrderMap;
import li.strolch.agent.api.ResourceMap;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.model.Order;
import li.strolch.model.Resource;
import li.strolch.model.audit.Audit;
import ch.eitchnet.privilege.model.Certificate;
/**
@ -23,11 +29,59 @@ import ch.eitchnet.privilege.model.Certificate;
*/
public interface PersistenceHandler {
/**
* Opens a {@link StrolchTransaction} on the given {@link StrolchRealm}. The transaction is the main object to be
* used when accessing and modifying the elements of a realm.
*
* @param realm
* the realm for which the transaction is to be opened
* @param certificate
* the certificate which has access to the realm
* @param action
* the name of the transaction used for auditing
*
* @return the newly created {@link StrolchTransaction}
*/
public StrolchTransaction openTx(StrolchRealm realm, Certificate certificate, String action);
/**
* Returns the {@link OrderDao} for the given transaction. Use this only if you want to bypass certain transaction
* features. Accessing {@link Order Orders} should be done through the {@link OrderMap} accessed from the
* transaction
*
* @param tx
* the transaction for which the {@link OrderDao} is to be returned
*
* @return the {@link OrderDao}
*/
public OrderDao getOrderDao(StrolchTransaction tx);
/**
* Returns the {@link ResourceDao} for the given transaction. Use this only if you want to bypass certain
* transaction features. Accessing {@link Resource Resources} should be done through the {@link ResourceMap}
* accessed from the transaction
*
* @param tx
* the transaction for which the {@link ResourceDao} is to be returned
*
* @return the {@link ResourceDao}
*/
public ResourceDao getResourceDao(StrolchTransaction tx);
/**
* Returns the {@link AuditDao} for the given transaction. Use this only if you want to bypass certain transaction
* features. Accessing {@link Audit Audits} should be done through the {@link AuditTrail} accessed from the
* transaction
*
* @param tx
* the transaction for which the {@link AuditDao} is to be returned
*
* @return the {@link AuditDao}
*/
public AuditDao getAuditDao(StrolchTransaction tx);
/**
* Performs a database specific initialization of the underlying database for each realm.
*/
public void performDbInitialization();
}

View File

@ -45,6 +45,11 @@ public class InMemoryPersistence implements PersistenceHandler {
return daoCache.getAuditDao();
}
@Override
public void performDbInitialization() {
// no-op
}
private synchronized DaoCache getDaoCache(StrolchTransaction tx) {
DaoCache daoCache = this.daoCache.get(tx.getRealmName());
if (daoCache == null) {

View File

@ -66,4 +66,9 @@ public class InMemoryPersistenceHandler extends StrolchComponent implements Pers
public AuditDao getAuditDao(StrolchTransaction tx) {
return this.persistence.getAuditDao(tx);
}
@Override
public void performDbInitialization() {
// no-op
}
}

View File

@ -15,6 +15,7 @@
*/
package li.strolch.runtime;
import static ch.eitchnet.utils.helper.StringHelper.DOT;
import li.strolch.agent.api.ObserverHandler;
import li.strolch.model.StrolchModelConstants;
import li.strolch.persistence.api.PersistenceHandler;
@ -49,4 +50,11 @@ public class StrolchConstants {
* @see StrolchModelConstants#INTERPRETATION_ORDER_REF
*/
public static final String INTERPRETATION_ORDER_REF = StrolchModelConstants.INTERPRETATION_ORDER_REF;
public static String makeRealmKey(String realmName, String key) {
String realmKey = key;
if (!realmName.equals(DEFAULT_REALM))
realmKey += DOT + realmName;
return realmKey;
}
}

View File

@ -31,6 +31,8 @@ import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.helper.StringHelper;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
@ -44,9 +46,13 @@ public class MainStarter {
private static final String OPT_ENV = "env";
private Options options;
private String env;
private File pathF;
private StrolchAgent agent;
public MainStarter() {
Options op = new Options();
Option rootPathOption = new Option("p", OPT_ROOT_PATH, true, "root path to strolch runtime");
rootPathOption.setOptionalArg(false);
@ -61,6 +67,17 @@ public class MainStarter {
}
public int start(String[] args) {
int ret = parseArgs(args);
if (ret != 0)
return ret;
setup();
initialize();
start();
return keepAlive();
}
public int parseArgs(String[] args) {
// create the parser
CommandLineParser parser = new GnuParser();
@ -75,21 +92,46 @@ public class MainStarter {
return 1;
}
String env = line.getOptionValue(OPT_ENV);
String pathS = line.getOptionValue(OPT_ROOT_PATH);
File pathF = new File(pathS);
if (!pathF.exists()) {
logger.info(MessageFormat.format("Path parameter does not exist at: {0}", pathS));
this.env = line.getOptionValue(OPT_ENV);
if (StringHelper.isEmpty(this.env)) {
logger.error("env argument is missing!");
printUsage();
return 1;
}
logger.info("Starting Agent...");
setAgent(new StrolchAgent());
getAgent().setup(env, pathF);
String pathS = line.getOptionValue(OPT_ROOT_PATH);
this.pathF = new File(pathS);
if (!this.pathF.exists()) {
logger.error(MessageFormat.format("Path parameter does not exist at: {0}", pathS));
printUsage();
return 1;
}
return 0;
}
public void setup() {
this.agent = new StrolchAgent();
getAgent().setup(this.env, this.pathF);
}
public void initialize() {
getAgent().initialize();
}
public void start() {
getAgent().start();
}
public void stop() {
getAgent().stop();
}
public void destroy() {
getAgent().destroy();
}
public int keepAlive() {
final AtomicBoolean atomicBoolean = new AtomicBoolean();
@ -128,23 +170,11 @@ public class MainStarter {
}
private void printUsage() {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("java -classpath lib/ -jar <jar.containing.main.class>.jar <options>", this.options);
}
/**
* @return the agent
*/
public StrolchAgent getAgent() {
return this.agent;
}
/**
* @param agent
* the agent to set
*/
public void setAgent(StrolchAgent agent) {
this.agent = agent;
}
}

View File

@ -53,6 +53,8 @@ public class DbConnectionCheck {
String username = connectionInfo.getUsername();
String password = connectionInfo.getPassword();
logger.info("Checking connection " + username + "@" + url);
try (Connection con = DriverManager.getConnection(url, username, password);
Statement st = con.createStatement();) {
@ -68,5 +70,7 @@ public class DbConnectionCheck {
throw new StrolchConfigurationException(msg, e);
}
}
logger.info("All connections OK");
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.postgresql;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public enum DbMigrationState {
NOTHING, CREATED, MIGRATED, DROPPED_CREATED;
}

View File

@ -15,6 +15,9 @@
*/
package li.strolch.persistence.postgresql;
import static li.strolch.persistence.postgresql.PostgreSqlPersistenceHandler.PROP_DB_VERSION;
import static li.strolch.persistence.postgresql.PostgreSqlPersistenceHandler.RESOURCE_DB_VERSION;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
@ -23,13 +26,12 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import li.strolch.exception.StrolchException;
import li.strolch.persistence.api.DbConnectionInfo;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.configuration.StrolchConfigurationException;
import org.slf4j.Logger;
@ -44,79 +46,95 @@ import ch.eitchnet.utils.helper.FileHelper;
@SuppressWarnings(value = "nls")
public class DbSchemaVersionCheck {
private static final String RESOURCE_DB_VERSION = "/db_version.properties";
private static final String PROP_DB_VERSION = "db_version";
private static final String PROP_ALLOW_SCHEMA_CREATION = "allowSchemaCreation";
private static final String PROP_ALLOW_SCHEMA_DROP = "allowSchemaDrop";
private static final Logger logger = LoggerFactory.getLogger(DbSchemaVersionCheck.class);
private Map<String, DbConnectionInfo> connetionInfoMap;
private boolean allowSchemaCreation;
private boolean allowSchemaDrop;
private Map<String, DbMigrationState> dbMigrationStates;
/**
* @param connetionInfoMap
* @param componentConfiguration
* @param allowSchemaCreation
* @param allowSchemaDrop
* @param allowDataInitOnSchemaCreate
*/
public DbSchemaVersionCheck(Map<String, DbConnectionInfo> connetionInfoMap,
ComponentConfiguration componentConfiguration) {
this.connetionInfoMap = connetionInfoMap;
this.allowSchemaCreation = componentConfiguration.getBoolean(PROP_ALLOW_SCHEMA_CREATION, Boolean.FALSE);
this.allowSchemaDrop = componentConfiguration.getBoolean(PROP_ALLOW_SCHEMA_DROP, Boolean.FALSE);
public DbSchemaVersionCheck(boolean allowSchemaCreation, boolean allowSchemaDrop) {
this.allowSchemaCreation = allowSchemaCreation;
this.allowSchemaDrop = allowSchemaDrop;
this.dbMigrationStates = new HashMap<>();
}
public void checkSchemaVersion() {
/**
* @return the dbMigrationStates
*/
public Map<String, DbMigrationState> getDbMigrationStates() {
return this.dbMigrationStates;
}
Collection<DbConnectionInfo> values = this.connetionInfoMap.values();
for (DbConnectionInfo connectionInfo : values) {
String realm = connectionInfo.getRealm();
String url = connectionInfo.getUrl();
String username = connectionInfo.getUsername();
String password = connectionInfo.getPassword();
logger.info(MessageFormat.format("[{0}] Checking Schema version for: {1}@{2}", realm, username, url));
try (Connection con = DriverManager.getConnection(url, username, password);
Statement st = con.createStatement();) {
String expectedDbVersion = getExpectedDbVersion();
// first see if we have any schema
String msg = "select table_schema, table_name, table_type from information_schema.tables where table_name=''{0}'';";
String checkSchemaExistsSql = MessageFormat.format(msg, PROP_DB_VERSION);
try (ResultSet rs = st.executeQuery(checkSchemaExistsSql)) {
if (!rs.next()) {
createSchema(realm, expectedDbVersion, st);
} else {
checkCurrentVersion(realm, st, expectedDbVersion);
}
}
} catch (SQLException e) {
String msg = "Failed to open DB connection to URL {0} due to: {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, url, e.getMessage());
throw new StrolchConfigurationException(msg, e);
}
public void checkSchemaVersion(Map<String, DbConnectionInfo> connectionInfoMap) {
for (DbConnectionInfo connectionInfo : connectionInfoMap.values()) {
DbMigrationState dbMigrationState = checkSchemaVersion(connectionInfo);
dbMigrationStates.put(connectionInfo.getRealm(), dbMigrationState);
}
}
private void checkCurrentVersion(String realm, Statement st, String expectedDbVersion) throws SQLException {
/**
* Returns true if the schema existed or was only migrated, false if the schema was created
*
* @param connectionInfo
*
* @return true if the schema existed or was only migrated, false if the schema was created
*/
public DbMigrationState checkSchemaVersion(DbConnectionInfo connectionInfo) {
String realm = connectionInfo.getRealm();
String url = connectionInfo.getUrl();
String username = connectionInfo.getUsername();
String password = connectionInfo.getPassword();
logger.info(MessageFormat.format("[{0}] Checking Schema version for: {1}@{2}", realm, username, url));
DbMigrationState migrationType;
try (Connection con = DriverManager.getConnection(url, username, password);
Statement st = con.createStatement();) {
String expectedDbVersion = getExpectedDbVersion();
// first see if we have any schema
String msg = "select table_schema, table_name, table_type from information_schema.tables where table_name=''{0}'';";
String checkSchemaExistsSql = MessageFormat.format(msg, PROP_DB_VERSION);
try (ResultSet rs = st.executeQuery(checkSchemaExistsSql)) {
if (!rs.next()) {
migrationType = createSchema(realm, expectedDbVersion, st);
} else {
migrationType = checkCurrentVersion(realm, st, expectedDbVersion);
}
}
return migrationType;
} catch (SQLException e) {
String msg = "Failed to open DB connection to URL {0} due to: {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, url, e.getMessage());
throw new StrolchConfigurationException(msg, e);
}
}
private DbMigrationState checkCurrentVersion(String realm, Statement st, String expectedDbVersion)
throws SQLException {
try (ResultSet rs = st.executeQuery("select id, version from db_version order by id desc;")) {
if (!rs.next()) {
createSchema(realm, expectedDbVersion, st);
return createSchema(realm, expectedDbVersion, st);
} else {
String currentVersion = rs.getString(2);
if (expectedDbVersion.equals(currentVersion)) {
String msg = "[{0}] Schema version {1} is the current version. No changes needed.";
msg = MessageFormat.format(msg, realm, currentVersion);
logger.info(msg);
return DbMigrationState.NOTHING;
} else {
String msg = "[{0}] Schema version is not current. Need to upgrade from {1} to {2}";
msg = MessageFormat.format(msg, realm, currentVersion, expectedDbVersion);
logger.warn(msg);
upgradeSchema(realm, expectedDbVersion, st);
return upgradeSchema(realm, expectedDbVersion, st);
}
}
}
@ -152,10 +170,17 @@ public class DbSchemaVersionCheck {
}
/**
*
* @param realm
* the realm to create the schema for (a {@link DbConnectionInfo} must exist for it)
* @param dbVersion
* the version to upgrade to
* @param st
* the open database {@link Statement} to which the SQL statements will be written
*
* @return true if the schema was created, false if it was not
*/
private void createSchema(String realm, String dbVersion, Statement st) {
private DbMigrationState createSchema(String realm, String dbVersion, Statement st) {
if (!this.allowSchemaCreation) {
String msg = "[{0}] No schema exists, or is not valid. Schema generation is disabled, thus can not continue!";
@ -174,8 +199,17 @@ public class DbSchemaVersionCheck {
}
logger.info(MessageFormat.format("[{0}] Successfully created schema for version {1}", realm, dbVersion));
return DbMigrationState.CREATED;
}
/**
* @param realm
* the realm for which the schema must be dropped (a {@link DbConnectionInfo} must exist for it)
* @param dbVersion
* the version with which to to drop the schema
* @param st
* the open database {@link Statement} to which the SQL statements will be written
*/
private void dropSchema(String realm, String dbVersion, Statement st) {
if (!this.allowSchemaDrop) {
@ -196,10 +230,21 @@ public class DbSchemaVersionCheck {
}
/**
* Upgrades the schema to the given version. If the current version is below the given version, then currently this
* method drops the schema and recreates it. Real migration must still be implemented
*
* @param realm
* the realm to migrate (a {@link DbConnectionInfo} must exist for it)
* @param dbVersion
* the version to upgrade to
* @param st
* the open database {@link Statement} to which the SQL statements will be written
*
* @return true if the schema was recreated, false if it was simply migrated
*/
private void upgradeSchema(String realm, String dbVersion, Statement st) {
private DbMigrationState upgradeSchema(String realm, String dbVersion, Statement st) {
dropSchema(realm, dbVersion, st);
createSchema(realm, dbVersion, st);
return DbMigrationState.DROPPED_CREATED;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*/
package li.strolch.persistence.postgresql;
import static li.strolch.persistence.postgresql.PostgreSqlPersistenceHandler.PROP_ALLOW_SCHEMA_CREATION;
import static li.strolch.persistence.postgresql.PostgreSqlPersistenceHandler.PROP_ALLOW_SCHEMA_DROP;
import java.util.Map;
import java.util.Map.Entry;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.persistence.api.DbConnectionInfo;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.configuration.RuntimeConfiguration;
import li.strolch.runtime.configuration.StrolchConfiguration;
import ch.eitchnet.privilege.model.Certificate;
import ch.eitchnet.privilege.model.PrivilegeContext;
public class PostgreSqlDbInitializer extends PostgreSqlInitializer {
private StrolchAgent agent;
private PostgreSqlPersistenceHandler persistenceHandler;
private RuntimeConfiguration runtimeConfig;
private Certificate certificate;
private boolean allowSchemaCreation;
private boolean allowSchemaDrop;
/**
* @param agent
* @param persistenceHandler
*/
public PostgreSqlDbInitializer(StrolchAgent agent, PostgreSqlPersistenceHandler persistenceHandler,
ComponentConfiguration persistenceConfig) {
super(agent, persistenceHandler);
this.agent = agent;
StrolchConfiguration strolchConfiguration = agent.getStrolchConfiguration();
this.runtimeConfig = strolchConfiguration.getRuntimeConfiguration();
this.persistenceHandler = persistenceHandler;
this.allowSchemaCreation = persistenceConfig.getBoolean(PROP_ALLOW_SCHEMA_CREATION, Boolean.FALSE);
this.allowSchemaDrop = persistenceConfig.getBoolean(PROP_ALLOW_SCHEMA_DROP, Boolean.FALSE);
}
@Override
protected Certificate getCertificate() {
return this.certificate;
}
@Override
public void execute(PrivilegeContext privilegeContext) {
this.certificate = privilegeContext.getCertificate();
// first make sure we can connect to the database
Map<String, DbConnectionInfo> connetionInfoMap = this.persistenceHandler.getConnetionInfoMap();
DbConnectionCheck connectionCheck = new DbConnectionCheck(connetionInfoMap);
connectionCheck.checkConnections();
// first make sure that the data store files exist
for (String realmName : this.agent.getContainer().getRealmNames()) {
// throws exception if does not exist
getDataStoreFile(this.runtimeConfig, this.realmConfig, realmName);
}
// for each connection info:
// - make sure schema exists
// - if it didn't exist, initialize with a set of data defined by the data store file
DbSchemaVersionCheck schemaVersionCheck = new DbSchemaVersionCheck(this.allowSchemaCreation,
this.allowSchemaDrop);
for (Entry<String, DbConnectionInfo> entry : connetionInfoMap.entrySet()) {
String realmName = entry.getKey();
DbConnectionInfo connectionInfo = entry.getValue();
StrolchRealm realm = this.agent.getContainer().getRealm(realmName);
if (realm.getMode().isTransient())
continue;
// check that the schema exists
DbMigrationState migrationType = schemaVersionCheck.checkSchemaVersion(connectionInfo);
// now init the DB if needed
initSchemaFromDataStore(migrationType, realmName);
}
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.postgresql;
import static li.strolch.agent.impl.DefaultRealmHandler.PREFIX_DATA_STORE_FILE;
import static li.strolch.runtime.StrolchConstants.makeRealmKey;
import java.io.File;
import java.text.MessageFormat;
import li.strolch.agent.api.RealmHandler;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.impl.StoreToDaoElementListener;
import li.strolch.model.ModelStatistics;
import li.strolch.model.xml.XmlModelSaxFileReader;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.configuration.RuntimeConfiguration;
import li.strolch.runtime.configuration.StrolchConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.privilege.handler.SystemUserAction;
import ch.eitchnet.privilege.model.Certificate;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public abstract class PostgreSqlInitializer implements SystemUserAction {
protected static final Logger logger = LoggerFactory.getLogger(PostgreSqlInitializer.class);
protected StrolchAgent agent;
protected PostgreSqlPersistenceHandler persistenceHandler;
protected RuntimeConfiguration runtimeConfig;
protected ComponentConfiguration realmConfig;
public PostgreSqlInitializer(StrolchAgent agent, PostgreSqlPersistenceHandler persistenceHandler) {
this.agent = agent;
this.persistenceHandler = persistenceHandler;
StrolchConfiguration strolchConfiguration = agent.getStrolchConfiguration();
this.runtimeConfig = strolchConfiguration.getRuntimeConfiguration();
this.realmConfig = strolchConfiguration.getComponentConfiguration(RealmHandler.class.getSimpleName());
}
protected abstract Certificate getCertificate();
/**
* @param migrationType
* @param realmName
*/
protected void initSchemaFromDataStore(DbMigrationState migrationType, String realmName) {
boolean needsDbInit = checkNeedsDbInit(migrationType);
if (!needsDbInit) {
String msg = "Schema for realm {0} had no migration run. No need for data initialization.";
logger.info(MessageFormat.format(msg, realmName));
return;
}
String msg = "Migration for schema for realm {0} was {1} so need to initialize the data from the databaseStore...";
logger.info(MessageFormat.format(msg, realmName, migrationType));
ModelStatistics statistics;
try (StrolchTransaction tx = this.persistenceHandler.openTx(this.agent.getContainer().getRealm(realmName),
getCertificate(), RealmHandler.SYSTEM_USER_DB_INITIALIZER)) {
File dataStoreF = getDataStoreFile(this.runtimeConfig, this.realmConfig, realmName);
StoreToDaoElementListener listener = new StoreToDaoElementListener(tx);
XmlModelSaxFileReader handler = new XmlModelSaxFileReader(listener, dataStoreF);
handler.parseFile();
statistics = handler.getStatistics();
}
logger.info(MessageFormat.format("Realm {0} initialization statistics: {1}", realmName, statistics));
}
protected boolean checkNeedsDbInit(DbMigrationState migrationType) {
boolean needsDbInit;
switch (migrationType) {
case CREATED:
needsDbInit = true;
break;
case DROPPED_CREATED:
needsDbInit = true;
break;
case MIGRATED:
needsDbInit = false;
break;
case NOTHING:
needsDbInit = false;
break;
default:
needsDbInit = false;
break;
}
return needsDbInit;
}
protected File getDataStoreFile(RuntimeConfiguration runtimeConfiguration,
ComponentConfiguration realmConfiguration, String realmName) {
String dataStoreKey = makeRealmKey(realmName, PREFIX_DATA_STORE_FILE);
File dataStoreF = realmConfiguration.getDataFile(dataStoreKey, null, runtimeConfiguration, true);
return dataStoreF;
}
}

View File

@ -15,7 +15,8 @@
*/
package li.strolch.persistence.postgresql;
import static ch.eitchnet.utils.helper.StringHelper.DOT;
import static li.strolch.agent.api.RealmHandler.SYSTEM_USER_DB_INITIALIZER;
import static li.strolch.runtime.StrolchConstants.makeRealmKey;
import java.sql.Connection;
import java.sql.Driver;
@ -27,6 +28,8 @@ import java.util.Map;
import java.util.Set;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.RealmHandler;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchComponent;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.persistence.api.AuditDao;
@ -36,9 +39,10 @@ import li.strolch.persistence.api.PersistenceHandler;
import li.strolch.persistence.api.ResourceDao;
import li.strolch.persistence.api.StrolchPersistenceException;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.configuration.StrolchConfiguration;
import li.strolch.runtime.configuration.StrolchConfigurationException;
import li.strolch.runtime.privilege.PrivilegeHandler;
import ch.eitchnet.privilege.model.Certificate;
/**
@ -46,9 +50,14 @@ import ch.eitchnet.privilege.model.Certificate;
*/
public class PostgreSqlPersistenceHandler extends StrolchComponent implements PersistenceHandler {
private static final String PROP_DB_URL = "db.url"; //$NON-NLS-1$
private static final String PROP_DB_USERNAME = "db.username"; //$NON-NLS-1$
private static final String PROP_DB_PASSWORD = "db.password"; //$NON-NLS-1$
public static final String PROP_DB_URL = "db.url"; //$NON-NLS-1$
public static final String PROP_DB_USERNAME = "db.username"; //$NON-NLS-1$
public static final String PROP_DB_PASSWORD = "db.password"; //$NON-NLS-1$
public static final String PROP_ALLOW_SCHEMA_CREATION = "allowSchemaCreation";
public static final String PROP_ALLOW_SCHEMA_DROP = "allowSchemaDrop";
public static final String PROP_ALLOW_DATA_INIT_ON_SCHEMA_CREATE = "allowDataInitOnSchemaCreate";
public static final String PROP_DB_VERSION = "db_version";
public static final String RESOURCE_DB_VERSION = "/db_version.properties";
private ComponentConfiguration componentConfiguration;
private Map<String, DbConnectionInfo> connetionInfoMap;
@ -70,14 +79,9 @@ public class PostgreSqlPersistenceHandler extends StrolchComponent implements Pe
if (realm.getMode().isTransient())
continue;
String dbUrlKey = PROP_DB_URL;
String dbUsernameKey = PROP_DB_USERNAME;
String dbPasswordKey = PROP_DB_PASSWORD;
if (!realmName.equals(StrolchConstants.DEFAULT_REALM)) {
dbUrlKey += DOT + realmName;
dbUsernameKey += DOT + realmName;
dbPasswordKey += DOT + realmName;
}
String dbUrlKey = makeRealmKey(realmName, PROP_DB_URL);
String dbUsernameKey = makeRealmKey(realmName, PROP_DB_USERNAME);
String dbPasswordKey = makeRealmKey(realmName, PROP_DB_PASSWORD);
String dbUrl = componentConfiguration.getString(dbUrlKey, null);
String username = componentConfiguration.getString(dbUsernameKey, null);
@ -112,7 +116,15 @@ public class PostgreSqlPersistenceHandler extends StrolchComponent implements Pe
msg = MessageFormat.format(msg, connectionInfo.getRealm(), compliant, driver.getMajorVersion(),
driver.getMinorVersion());
logger.info(msg);
}
/**
* Returns the map of {@link DbConnectionInfo} which can be used in maintenance mode
*
* @return the connetionInfoMap
*/
public Map<String, DbConnectionInfo> getConnetionInfoMap() {
return this.connetionInfoMap;
}
@Override
@ -122,9 +134,27 @@ public class PostgreSqlPersistenceHandler extends StrolchComponent implements Pe
DbConnectionCheck connectionCheck = new DbConnectionCheck(this.connetionInfoMap);
connectionCheck.checkConnections();
DbSchemaVersionCheck schemaVersionCheck = new DbSchemaVersionCheck(this.connetionInfoMap,
this.componentConfiguration);
schemaVersionCheck.checkSchemaVersion();
boolean allowSchemaCreation = componentConfiguration.getBoolean(PROP_ALLOW_SCHEMA_CREATION, Boolean.FALSE);
boolean allowSchemaDrop = componentConfiguration.getBoolean(PROP_ALLOW_SCHEMA_DROP, Boolean.FALSE);
boolean allowDataInitOnSchemaCreate = componentConfiguration.getBoolean(PROP_ALLOW_DATA_INIT_ON_SCHEMA_CREATE,
Boolean.FALSE);
DbSchemaVersionCheck schemaVersionCheck = new DbSchemaVersionCheck(allowSchemaCreation, allowSchemaDrop);
schemaVersionCheck.checkSchemaVersion(this.connetionInfoMap);
// if allowed, perform DB initialization
if (!allowSchemaCreation || !allowSchemaDrop || !allowDataInitOnSchemaCreate) {
logger.info("Data Initialization not enabled, so not checking if needed.");
} else {
Map<String, DbMigrationState> dbMigrationStates = schemaVersionCheck.getDbMigrationStates();
String msg = "Data Initialization is enabled, checking for {0} realms if DB initialization is required...";
logger.info(MessageFormat.format(msg, dbMigrationStates.size()));
PrivilegeHandler privilegeHandler = getContainer().getPrivilegeHandler();
StrolchAgent agent = getContainer().getAgent();
PostgreSqlSchemaInitializer schemaInitializer = new PostgreSqlSchemaInitializer(agent, this,
dbMigrationStates);
privilegeHandler.runAsSystem(SYSTEM_USER_DB_INITIALIZER, schemaInitializer);
}
super.start();
}
@ -168,4 +198,15 @@ public class PostgreSqlPersistenceHandler extends StrolchComponent implements Pe
public AuditDao getAuditDao(StrolchTransaction tx) {
return ((PostgreSqlStrolchTransaction) tx).getAuditDao();
}
@Override
public void performDbInitialization() {
ComponentContainer container = getContainer();
StrolchAgent agent = container.getAgent();
PrivilegeHandler privilegeHandler = container.getPrivilegeHandler();
StrolchConfiguration strolchConfiguration = this.getContainer().getAgent().getStrolchConfiguration();
PostgreSqlDbInitializer sqlDbInitializer = new PostgreSqlDbInitializer(agent, this,
strolchConfiguration.getComponentConfiguration(getName()));
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_DB_INITIALIZER, sqlDbInitializer);
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.postgresql;
import java.util.Map;
import java.util.Map.Entry;
import li.strolch.agent.api.StrolchAgent;
import ch.eitchnet.privilege.model.Certificate;
import ch.eitchnet.privilege.model.PrivilegeContext;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PostgreSqlSchemaInitializer extends PostgreSqlInitializer {
private Map<String, DbMigrationState> dbMigrationStates;
/**
* @param agent
* @param persistenceHandler
* @param dbMigrationStates
*/
public PostgreSqlSchemaInitializer(StrolchAgent agent, PostgreSqlPersistenceHandler persistenceHandler,
Map<String, DbMigrationState> dbMigrationStates) {
super(agent, persistenceHandler);
this.dbMigrationStates = dbMigrationStates;
}
private Certificate certificate;
@Override
public void execute(PrivilegeContext privilegeContext) {
this.certificate = privilegeContext.getCertificate();
// first make sure the data store exists if needed
for (Entry<String, DbMigrationState> entry : this.dbMigrationStates.entrySet()) {
if (checkNeedsDbInit(entry.getValue()))
getDataStoreFile(runtimeConfig, realmConfig, entry.getKey());
}
// then initialize the schemas
for (Entry<String, DbMigrationState> entry : this.dbMigrationStates.entrySet()) {
initSchemaFromDataStore(entry.getValue(), entry.getKey());
}
}
@Override
protected Certificate getCertificate() {
return this.certificate;
}
}

View File

@ -97,4 +97,9 @@ public class XmlPersistenceHandler extends StrolchComponent implements Persisten
public AuditDao getAuditDao(StrolchTransaction tx) {
return new XmlAuditDao(tx);
}
@Override
public void performDbInitialization() {
throw new UnsupportedOperationException("Not yet implemented!");
}
}