private static final String DROP_USER_IF_EXISTS_SQL_CMD = DROP_USER_SQL_CMD + " IF EXISTS %s;";
private static final String DROP_OBJECTS_SQL_COMMENT = "-- drop all the objects before dropping roles";
private static final String CSPACE_JEESERVER_HOME = "CSPACE_JEESERVER_HOME";
+ private static final String CSPACE_UTILS_SCHEMANAME = "utils";
+ private static final String RUNSQLSCRIPTS_SERVICE_NAME = "runsqlscripts";
private ServiceMain() {
// Intentionally blank
// 1
fields.add(field);
}
- addindices.onRepositoryInitialized(JDBCTools.NUXEO_DATASOURCE_NAME, repositoryName, cspaceInstanceId,
+ addindices.onRepositoryInitialized(JDBCTools.NUXEO_DATASOURCE_NAME, repositoryName, cspaceInstanceId, tbt.getShortName(),
null, fields, null);
}
} else {
}
}
+ /**
+ * Search through the service bindings for the RUNSQLSCRIPTS_SERVICE_NAME service. Each tenant can add a set of SQL
+ * that will be run before the other Services' initHandlers.
+ *
+ * @param tenantBindingTypeMap
+ * @throws Exception
+ */
+ private void firePostInitRunSQLScripts(Hashtable<String, TenantBindingType> tenantBindingTypeMap) throws Exception {
+ String cspaceInstanceId = getCspaceInstanceId();
+ for (TenantBindingType tbt : tenantBindingTypeMap.values()) {
+ // Loop through all the services in this tenant
+ List<ServiceBindingType> sbtList = tbt.getServiceBindings();
+ for (ServiceBindingType sbt: sbtList) {
+ if (sbt.getName().equalsIgnoreCase(RUNSQLSCRIPTS_SERVICE_NAME)) {
+ runInitHandler(cspaceInstanceId, tbt, sbt);
+ return;
+ }
+ }
+ }
+ }
+
+ private void runInitHandler(String cspaceInstanceId, TenantBindingType tbt, ServiceBindingType sbt) throws Exception {
+ String repositoryName = null;
+ if (!sbt.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY)) {
+ // Each service can have a different repo domain
+ repositoryName = ConfigUtils.getRepositoryName(tbt, sbt.getRepositoryDomain());
+ }
+
+ // Get the list of InitHandler elements, extract the first one (only one supported right now) and fire it using reflection.
+ List<org.collectionspace.services.config.service.InitHandler> list = sbt.getInitHandler();
+ if (list != null && !list.isEmpty()) {
+ // REM - 12/2012: We might want to think about supporting multiple post-init handlers
+ org.collectionspace.services.config.service.InitHandler handlerType = list.get(0);
+ String initHandlerClassname = handlerType.getClassname();
+ if (Tools.isEmpty(initHandlerClassname)) {
+ return;
+ }
+ logger.trace("Firing post-init handler {}...", initHandlerClassname);
+
+ List<org.collectionspace.services.config.service.InitHandler.Params.Field>
+ fields = handlerType.getParams().getField();
+
+ List<org.collectionspace.services.config.service.InitHandler.Params.Property>
+ props = handlerType.getParams().getProperty();
+
+ Object o = instantiate(initHandlerClassname, IInitHandler.class);
+ if (o instanceof IInitHandler){
+ // The InitHandler may be the default one,
+ // or specialized classes which still implement this interface and are registered in tenant-bindings.xm.
+ IInitHandler handler = (IInitHandler)o;
+ handler.onRepositoryInitialized(JDBCTools.NUXEO_DATASOURCE_NAME, repositoryName, cspaceInstanceId, tbt.getShortName(),
+ sbt, fields, props);
+ }
+ }
+ }
+
public void firePostInitHandlers() throws Exception {
Hashtable<String, TenantBindingType> tenantBindingTypeMap = tenantBindingConfigReader.getTenantBindings();
+
//
- //Loop through all tenants in tenant-bindings.xml
+ // We first need to run the init handler for the 'runsqlscripts' service to allow a tenant to perform
+ // any required tenant specific SQL setup.
+ //
+ firePostInitRunSQLScripts(tenantBindingTypeMap);
+
+ //
+ // Loop through all tenants in tenant-bindings.xml and run each service's initHandler
//
String cspaceInstanceId = getCspaceInstanceId();
for (TenantBindingType tbt : tenantBindingTypeMap.values()) {
//
List<ServiceBindingType> sbtList = tbt.getServiceBindings();
for (ServiceBindingType sbt: sbtList) {
- String repositoryName = null;
- if (sbt.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
- repositoryName = ConfigUtils.getRepositoryName(tbt, sbt.getRepositoryDomain()); // Each service can have a different repo domain
+ // skip the RUNSQLSCRIPTS_SERVICE_NAME since we ran it already
+ if (!sbt.getName().equalsIgnoreCase(RUNSQLSCRIPTS_SERVICE_NAME)) {
+ runInitHandler(cspaceInstanceId, tbt, sbt);
}
- //Get the list of InitHandler elements, extract the first one (only one supported right now) and fire it using reflection.
- List<org.collectionspace.services.config.service.InitHandler> list = sbt.getInitHandler();
- if (list != null && list.size() > 0) {
- org.collectionspace.services.config.service.InitHandler handlerType = list.get(0); // REM - 12/2012: We might want to think about supporting multiple post-init handlers
- String initHandlerClassname = handlerType.getClassname();
- if (Tools.isEmpty(initHandlerClassname)) {
- continue;
- }
- if (ServiceMain.logger.isTraceEnabled()) {
- ServiceMain.logger.trace(String.format("Firing post-init handler %s ...", initHandlerClassname));
- }
-
- List<org.collectionspace.services.config.service.InitHandler.Params.Field>
- fields = handlerType.getParams().getField();
-
- List<org.collectionspace.services.config.service.InitHandler.Params.Property>
- props = handlerType.getParams().getProperty();
-
- //org.collectionspace.services.common.service.InitHandler.Fields ft = handlerType.getFields();
- //List<String> fields = ft.getField();
- Object o = instantiate(initHandlerClassname, IInitHandler.class);
- if (o != null && o instanceof IInitHandler){
- IInitHandler handler = (IInitHandler)o;
- handler.onRepositoryInitialized(JDBCTools.NUXEO_DATASOURCE_NAME, repositoryName, cspaceInstanceId,
- sbt, fields, props);
- //The InitHandler may be the default one,
- // or specialized classes which still implement this interface and are registered in tenant-bindings.xml.
- }
- }
}
}
}
JDBCTools.createNewDatabaseUser(JDBCTools.CSADMIN_DATASOURCE_NAME, repositoryName, cspaceInstanceId, dbType, readerUser, readerPW);
}
// Create the database
- createDatabaseWithRights(dbType, dbName, nuxeoUser, nuxeoPW, readerUser, readerPW);
+ createDatabaseWithRights(dbType, dbName, nuxeoUser, readerUser);
+ createUtilsSchemaWithRights(dbType, nuxeoUser, repositoryName, cspaceInstanceId);
initRepositoryDatabaseVersion(JDBCTools.NUXEO_DATASOURCE_NAME, repositoryName, cspaceInstanceId);
}
nuxeoDBsChecked.add(dbName);
* @param readerPW
* @throws Exception
*/
- private void createDatabaseWithRights(DatabaseProductType dbType, String dbName, String ownerName,
- String ownerPW, String readerName, String readerPW) throws Exception {
- Connection conn = null;
- Statement stmt = null;
- try {
- DataSource csadminDataSource = JDBCTools.getDataSource(JDBCTools.CSADMIN_DATASOURCE_NAME);
- conn = csadminDataSource.getConnection();
- stmt = conn.createStatement();
+ private void createDatabaseWithRights(DatabaseProductType dbType, String dbName, String ownerName, String readerName) throws Exception {
+ String sql = null;
+ try (Connection conn = JDBCTools.getDataSource(JDBCTools.CSADMIN_DATASOURCE_NAME).getConnection();
+ Statement stmt = conn.createStatement()) {
if (dbType == DatabaseProductType.POSTGRESQL) {
// PostgreSQL does not need passwords in grant statements.
- String sql = "CREATE DATABASE " + dbName + " ENCODING 'UTF8' OWNER " + ownerName;
+ sql = "CREATE DATABASE " + dbName + " ENCODING 'UTF8' OWNER " + ownerName;
stmt.executeUpdate(sql);
- if (logger.isDebugEnabled()) {
- logger.debug("Created db: '" + dbName + "' with owner: '" + ownerName + "'");
- }
+ logger.debug("Created db: '{}' with owner: '{}'", dbName, ownerName);
if (readerName != null) {
sql = "GRANT CONNECT ON DATABASE " + dbName + " TO " + readerName;
stmt.executeUpdate(sql);
- if (logger.isDebugEnabled()) {
- logger.debug(" Granted connect rights on: '" + dbName + "' to reader: '" + readerName + "'");
- }
+ logger.debug("Granted connect rights on: '{}' to reader: '{}'", dbName, readerName);
}
// Note that select rights for reader must be granted after
// Nuxeo startup.
- } else if (dbType == DatabaseProductType.MYSQL) {
- String sql = "CREATE database " + dbName + " DEFAULT CHARACTER SET utf8";
- stmt.executeUpdate(sql);
- sql = "GRANT ALL PRIVILEGES ON " + dbName + ".* TO '" + ownerName + "'@'localhost' IDENTIFIED BY '"
- + ownerPW + "' WITH GRANT OPTION";
- stmt.executeUpdate(sql);
- if (logger.isDebugEnabled()) {
- logger.debug("Created db: '" + dbName + "' with owner: '" + ownerName + "'");
- }
- if (readerName != null) {
- sql = "GRANT SELECT ON " + dbName + ".* TO '" + readerName + "'@'localhost' IDENTIFIED BY '"
- + readerPW + "' WITH GRANT OPTION";
- stmt.executeUpdate(sql);
- if (logger.isDebugEnabled()) {
- logger.debug(" Granted SELECT rights on: '" + dbName + "' to reader: '" + readerName + "'");
- }
- }
} else {
- throw new UnsupportedOperationException("createDatabaseWithRights only supports PSQL - MySQL NYI!");
+ throw new UnsupportedOperationException(String.format("DB Type %s not supported", dbType));
}
} catch (Exception e) {
- logger.error("createDatabaseWithRights failed on exception: " + e.getLocalizedMessage());
- throw e; // propagate
- } finally { // close resources
- try {
- if (stmt != null) {
- stmt.close();
- }
- if (conn != null) {
- conn.close();
- }
- } catch (SQLException se) {
- se.printStackTrace();
- }
+ logger.error("createDatabaseWithRights failed on exception:", e);
+ logger.error("The following SQL statement failed using credentials from datasource '{}': {}",
+ JDBCTools.CSADMIN_DATASOURCE_NAME, sql);
+ throw e;
}
}
+ /*
+ * For a specific repo/db, create a schema for misc SQL functions
+ */
+ private void createUtilsSchemaWithRights(DatabaseProductType dbType, String ownerName,
+ String repositoryName, String cspaceInstanceId) throws Exception {
+ String sql = null;
+ try (Connection conn = JDBCTools.getConnection(JDBCTools.NUXEO_DATASOURCE_NAME,
+ repositoryName, cspaceInstanceId);
+ Statement stmt = conn.createStatement()) {
+ if (dbType == DatabaseProductType.POSTGRESQL) {
+ sql = "CREATE SCHEMA IF NOT EXISTS " + CSPACE_UTILS_SCHEMANAME + " AUTHORIZATION " + ownerName;
+ stmt.executeUpdate(sql);
+ logger.debug("Created SCHEMA: '{}' with owner: '{}'", CSPACE_UTILS_SCHEMANAME, ownerName);
+ } else {
+ throw new UnsupportedOperationException("CollectionSpace supports only PostgreSQL database servers.");
+ }
+ } catch (Exception e) {
+ logger.error("createUtilsSchemaWithRights() failed with exception:", e);
+ logger.error("The following SQL statement failed using credentials from datasource '{}': {}",
+ JDBCTools.NUXEO_DATASOURCE_NAME, sql);
+ throw e;
+ }
+ }
+
private void initRepositoryDatabaseVersion(String dataSourceName, String repositoryName, String cspaceInstanceId) throws Exception {
String version = getClass().getPackage().getImplementationVersion();
Connection conn = null;
public void onRepositoryInitialized(String dataSourceName,
String repositoryName,
String cspaceInstanceId,
+ String tenantShortName,
ServiceBindingType sbt,
List<Field> fields,
List<Property> properties) throws Exception {
return;
}
for (String scriptName : scriptNames) {
- String scriptPath = getSqlScriptPath(dataSourceName, repositoryName, scriptName);
+ String scriptPath = getSqlScriptPath(dataSourceName, repositoryName, tenantShortName, scriptName);
if (Tools.isBlank(scriptPath)) {
logger.warn("Could not get path to SQL script.");
logger.warn(CANNOT_PERFORM_TASKS_MESSAGE);
}
scriptContents = getSqlScriptContents(scriptPath);
if (Tools.isBlank(scriptContents)) {
- logger.warn("Could not get contents of SQL script from resource " + scriptPath);
- logger.warn(CANNOT_PERFORM_TASKS_MESSAGE);
- continue;
+ // Since we couldn't find the script in the tenant qualified location, let's look in the shared/common
+ // (non-tenant qualified) location
+ String commonPath = getSqlScriptCommonPath(dataSourceName, repositoryName, scriptName);
+ scriptContents = getSqlScriptContents(commonPath);
+ logger.warn("Could not get contents of SQL script from resource '{}'. Looking here instead: '{}'",
+ scriptPath, commonPath);
+ if (Tools.isBlank(scriptContents)) {
+ logger.warn("Could not get contents of SQL script from resource {}", commonPath);
+ logger.warn(CANNOT_PERFORM_TASKS_MESSAGE);
+ continue;
+ }
}
+
+ logger.info("Running SQL script from Java class path '{}'", scriptPath);
+ logger.trace(scriptContents);
+
runScript(dataSourceName, repositoryName, cspaceInstanceId, scriptContents, "resource path " + scriptPath);
}
// Next, run a second sequence of SQL scripts, where those scripts may be
// stored on disk, in a resources directory within the server directory.
- List<File> scriptFiles = getSqlScriptFiles(dataSourceName, repositoryName);
+ List<File> scriptFiles = getSqlScriptFiles(dataSourceName, repositoryName, tenantShortName);
// Run these scripts in a sequence based on the ascending order of their filenames.
// FIXME: consider adding functionality to specify the locale for filename
// sorting here. (The current sort order is based on the system's default locale.)
return scriptNames;
}
- private String getSqlScriptPath(String dataSourceName, String repositoryName, String scriptName) throws Exception {
+ private String getSqlScriptCommonPath(String dataSourceName,
+ String repositoryName,
+ String scriptName) throws Exception {
+ String scriptPath =
+ DATABASE_RESOURCE_DIRECTORY_NAME
+ + RESOURCE_PATH_SEPARATOR
+ + JDBCTools.getDatabaseProductType(dataSourceName, repositoryName)
+ + RESOURCE_PATH_SEPARATOR
+ + scriptName;
+ return scriptPath;
+ }
+
+ private String getSqlScriptPath(String dataSourceName,
+ String repositoryName,
+ String tenantShortName,
+ String scriptName) throws Exception {
String scriptPath =
DATABASE_RESOURCE_DIRECTORY_NAME
+ RESOURCE_PATH_SEPARATOR
+ JDBCTools.getDatabaseProductType(dataSourceName, repositoryName)
+ RESOURCE_PATH_SEPARATOR
+ + "tenants"
+ + RESOURCE_PATH_SEPARATOR
+ + tenantShortName
+ + RESOURCE_PATH_SEPARATOR
+ scriptName;
return scriptPath;
}
- private String getSqlScriptDirectoryPath(String dataSourceName, String repositoryName) throws Exception {
+ private String getSqlScriptDirectoryPath(String dataSourceName,
+ String repositoryName,
+ String tenantShortName) throws Exception {
String scriptDirectoryPath =
getServerResourcesDirectoryPath()
+ DATABASE_RESOURCE_DIRECTORY_NAME
+ File.separator
+ JDBCTools.getDatabaseProductType(dataSourceName, repositoryName)
+ + File.separator
+ + "tenants"
+ + File.separator
+ + tenantShortName
+ File.separator;
return scriptDirectoryPath;
}
return serverResourcesPath;
}
- private List<File> getSqlScriptFiles(String dataSourceName, String repositoryName) throws Exception {
+ private List<File> getSqlScriptFiles(String dataSourceName,
+ String repositoryName,
+ String tenantShortName) throws Exception {
List<File> sqlScriptFiles = new ArrayList<>();
- File folder = new File(getSqlScriptDirectoryPath(dataSourceName, repositoryName));
+ File folder = new File(getSqlScriptDirectoryPath(dataSourceName, repositoryName, tenantShortName));
if (!folder.isDirectory() || !folder.canRead()) {
return sqlScriptFiles; // Return an empty list of files
}