]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-5761 Added support to create databases as needed for repos on init. Sets up...
authorPatrick Schmitz <pschmitz@berkeley.edu>
Fri, 14 Dec 2012 19:33:40 +0000 (11:33 -0800)
committerPatrick Schmitz <pschmitz@berkeley.edu>
Fri, 14 Dec 2012 19:33:40 +0000 (11:33 -0800)
services/JaxRsServiceProvider/src/main/webapp/META-INF/context.xml
services/common/pom.xml
services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java
services/common/src/main/java/org/collectionspace/services/common/storage/JDBCTools.java
src/main/resources/db/mysql/init_nuxeo_db.sql
src/main/resources/db/postgresql/init_nuxeo_db.sql

index de1031dcb69fe10f8f77a723a33cabafc1ba9e11..e17b748c07fca0fc6e4979c3df2cc793c40a061a 100644 (file)
        <Resource name="jdbc/NuxeoDS"
            auth="Container"
            type="javax.sql.DataSource"
-           username="${db.user}"
-           password="${db.user.password}"
+           username="${db.nuxeo.user}"
+           password="${db.nuxeo.user.password}"
+           driverClassName="${db.jdbc.driver.class}"
+           url="jdbc:${db}://${db.host}:${db.port}/nuxeo"
+           maxActive="8"
+           maxIdle="4"/>       
+
+  <Resource name="jdbc/NuxeoMgrDS"
+    auth="Container"
+    type="javax.sql.DataSource"
+    username="${db.user}"
+    password="${db.user.password}"
+    driverClassName="${db.jdbc.driver.class}"
+    url="jdbc:${db}://${db.host}:${db.port}"
+    maxActive="8"
+    maxIdle="4"/>      
+  
+  <Resource name="jdbc/NuxeoReaderDS"
+           auth="Container"
+           type="javax.sql.DataSource"
+           username="reader"
+           password="read"
            driverClassName="${db.jdbc.driver.class}"
            url="jdbc:${db}://${db.host}:${db.port}/nuxeo"
            maxActive="8"
index ede77b46ac0c1beae34f71de2141d5ec3e96b0a1..413a21da699fcea638991a6bcbd85bbf13a3912a 100644 (file)
             <version>${spring.security.version}</version>\r
             <scope>provided</scope>\r
         </dependency>        \r
+                               <dependency>\r
+                                        <groupId>org.apache.tomcat</groupId>\r
+                                        <artifactId>dbcp</artifactId>\r
+                                        <version>6.0.33</version>\r
+           <scope>provided</scope>\r
+                               </dependency>\r
     </dependencies>\r
 \r
     <repositories>\r
index 2a9cab01082801c568b104c4f89d9c27cde0957c..1bb8c546008cc216bd7145a3aa1bf50cfa6f75c2 100644 (file)
@@ -34,6 +34,7 @@ import org.collectionspace.services.common.storage.DatabaseProductType;
 import org.collectionspace.services.config.ClientType;\r
 import org.collectionspace.services.config.ServiceConfig;\r
 import org.collectionspace.services.config.service.ServiceBindingType;\r
+import org.collectionspace.services.config.tenant.RepositoryDomainType;\r
 import org.collectionspace.services.config.tenant.TenantBindingType;\r
 import org.collectionspace.services.config.types.PropertyItemType;\r
 import org.collectionspace.services.config.types.PropertyType;\r
@@ -41,6 +42,8 @@ import org.collectionspace.services.nuxeo.client.java.NuxeoConnectorEmbedded;
 import org.collectionspace.services.nuxeo.client.java.TenantRepository;\r
 import org.jboss.resteasy.spi.ResteasyProviderFactory;\r
 \r
+import org.apache.tomcat.dbcp.dbcp.BasicDataSource;\r
+\r
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
@@ -147,9 +150,9 @@ public class ServiceMain {
                System.out.println("Resuming cspace services initialization.");\r
        }\r
        \r
-       setDataSources();\r
        setServerRootDir();\r
         readConfig();\r
+       setDataSources();\r
         propagateConfiguredProperties();\r
         //\r
         // Start up and initialize our embedded Nuxeo server instance\r
@@ -343,7 +346,11 @@ public class ServiceMain {
      * our instance of embedded Nuxeo, we can find our datasources.  Therefore, we need to preserve the datasources in these\r
      * static members.\r
      */\r
-    private void setDataSources() throws NamingException {\r
+    private void setDataSources() throws NamingException, Exception {\r
+       final String DB_EXISTS_QUERY_PSQL = \r
+                       "SELECT 1 AS result FROM pg_database WHERE datname=?";\r
+       final String DB_EXISTS_QUERY_MYSQL = \r
+                       "SELECT 1 AS result FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME=?";\r
        //\r
        // As a side-effect of calling JDBCTools.getDataSource(...), the DataSource instance will be\r
        // cached in a static hash map of the JDBCTools class.  This will speed up lookups as well as protect our\r
@@ -351,10 +358,205 @@ public class ServiceMain {
        //\r
        DataSource cspaceDataSource = JDBCTools.getDataSource(JDBCTools.CSPACE_REPOSITORY_NAME);\r
        DataSource nuxeoDataSource = JDBCTools.getDataSource(JDBCTools.NUXEO_REPOSITORY_NAME);\r
+       DataSource nuxeoMgrDataSource = JDBCTools.getDataSource(JDBCTools.NUXEO_MANAGER_DATASOURCE_NAME);\r
+       DataSource nuxeoReaderDataSource = JDBCTools.getDataSource(JDBCTools.NUXEO_READER_DATASOURCE_NAME);\r
+       \r
+       // We need to fetch the user name and password from the nuxeoDataSource, to do grants below\r
+       org.apache.tomcat.dbcp.dbcp.BasicDataSource tomcatDataSource =\r
+                       (org.apache.tomcat.dbcp.dbcp.BasicDataSource)nuxeoDataSource;\r
+       // Get the template URL value from the JNDI datasource and substitute the databaseName\r
+       String nuxeoUser = tomcatDataSource.getUsername();\r
+       String nuxeoPW = tomcatDataSource.getPassword();\r
+       // HACK - this should come from another DataSource\r
+       tomcatDataSource =\r
+                       (org.apache.tomcat.dbcp.dbcp.BasicDataSource)nuxeoReaderDataSource;\r
+       // Get the template URL value from the JNDI datasource and substitute the databaseName\r
+       String readerUser = tomcatDataSource.getUsername();\r
+       String readerPW = tomcatDataSource.getPassword();\r
+       \r
        //\r
        // Set our AuthN's datasource to be the cspaceDataSource\r
        //\r
        AuthN.setDataSource(cspaceDataSource);\r
+\r
+       // Get the NuxeoDS info and create the necessary databases.\r
+       // Consider the tenant bindings to find and get the data sources for each tenant.\r
+       // There may be only one, one per tenant, or something in between.\r
+       DatabaseProductType dbType = JDBCTools.getDatabaseProductType(); // only returns PG or MYSQL\r
+       String dbExistsQuery = (dbType==DatabaseProductType.POSTGRESQL)?\r
+                                                               DB_EXISTS_QUERY_PSQL : DB_EXISTS_QUERY_MYSQL;\r
+\r
+       Hashtable<String, TenantBindingType> tenantBindings =\r
+                       tenantBindingConfigReader.getTenantBindings();\r
+       HashSet<String> nuxeoDBsChecked = new HashSet<String>();\r
+       PreparedStatement pstmt = null;\r
+       Statement stmt = null;\r
+               Connection conn = null;\r
+               \r
+       try {\r
+               conn = nuxeoMgrDataSource.getConnection();\r
+                       // First check and create the roles as needed. (nuxeo and reader)\r
+\r
+               \r
+               pstmt = conn.prepareStatement(dbExistsQuery); // create a statement\r
+                       stmt = conn.createStatement();\r
+                       \r
+               for (TenantBindingType tenantBinding : tenantBindings.values()) {\r
+                       String tId = tenantBinding.getId();\r
+                       String tName = tenantBinding.getName();\r
+                       List<RepositoryDomainType> repoDomainList = tenantBinding.getRepositoryDomain();\r
+                       for (RepositoryDomainType repoDomain : repoDomainList) {\r
+                               String repoName = repoDomain.getName();\r
+                               String dbName = /* repoDomain.getRepositoryName()?? */ "nuxeo";\r
+                               if(nuxeoDBsChecked.contains(dbName)) {\r
+                                       if (logger.isDebugEnabled()) {\r
+                                               logger.debug("Another user of db: "+dbName+": Repo: "+repoName+" and tenant: "\r
+                                                               +tName+" (id:"+tId+")");\r
+                                       }\r
+                               } else {\r
+                                       if (logger.isDebugEnabled()) {\r
+                                               logger.debug("Need to prepare db: "+dbName+" for Repo: "+repoName+" and tenant: "\r
+                                                               +tName+" (id:"+tId+")");\r
+                                       }\r
+\r
+                                       pstmt.setString(1, dbName);                     // set dbName param\r
+                               ResultSet rs = pstmt.executeQuery();\r
+                               // extract data from the ResultSet\r
+                               boolean dbExists = rs.next(); \r
+                               rs.close();\r
+                               if(dbExists) {\r
+                                               if (logger.isDebugEnabled()) {\r
+                                                       logger.debug("Database: "+dbName+" already exists.");\r
+                                               }\r
+                               } else {\r
+                                       // Create the user as needed\r
+                                       createUserIfNotExists(conn, dbType, nuxeoUser, nuxeoPW);\r
+                                       createUserIfNotExists(conn, dbType, readerUser, readerPW);\r
+                                       // Create the database\r
+                                       createDatabaseWithRights(conn, dbType, dbName, nuxeoUser, nuxeoPW, readerUser, readerPW);\r
+                               }\r
+                                       nuxeoDBsChecked.add(dbName);\r
+                               }\r
+                       } // Loop on repos for tenant\r
+               } // Loop on tenants\r
+       } catch(SQLException se) {\r
+               //Handle errors for JDBC\r
+               se.printStackTrace();\r
+       } catch(Exception e) {\r
+               //Handle errors for Class.forName\r
+               e.printStackTrace();\r
+       } finally {   //close resources\r
+               try {\r
+                       if(stmt!=null) {\r
+                               stmt.close();\r
+                       }\r
+               } catch(SQLException se2) {\r
+                       // nothing we can do\r
+               }\r
+               try{\r
+                       if(conn!=null) {\r
+                               conn.close();\r
+                       }\r
+               }catch(SQLException se){\r
+                       se.printStackTrace();\r
+               }\r
+       }\r
+    }\r
+    \r
+    private void createUserIfNotExists(Connection conn, DatabaseProductType dbType,\r
+               String username, String userPW) throws Exception {\r
+       PreparedStatement pstmt = null;\r
+       Statement stmt = null;\r
+       final String USER_EXISTS_QUERY_PSQL = \r
+                       "SELECT 1 AS result FROM pg_roles WHERE rolname=?";\r
+       String userExistsQuery;\r
+       if(dbType==DatabaseProductType.POSTGRESQL) {\r
+               userExistsQuery = USER_EXISTS_QUERY_PSQL;\r
+       } else {\r
+               throw new UnsupportedOperationException("CreateUserIfNotExists only supports PSQL - MySQL NYI!");\r
+       }\r
+       try {\r
+               pstmt = conn.prepareStatement(userExistsQuery); // create a statement\r
+               pstmt.setString(1, username);                   // set dbName param\r
+               ResultSet rs = pstmt.executeQuery();\r
+               // extract data from the ResultSet\r
+               boolean userExists = rs.next();\r
+               rs.close();\r
+               if(userExists) {\r
+                       if (logger.isDebugEnabled()) {\r
+                               logger.debug("User: "+username+" already exists.");\r
+                       }\r
+               } else {\r
+                       stmt = conn.createStatement();\r
+                       String sql = "CREATE ROLE "+username+" WITH PASSWORD '"+userPW+"' LOGIN";\r
+                       stmt.executeUpdate(sql);\r
+                       // Really should do the grants as well. \r
+                       if (logger.isDebugEnabled()) {\r
+                               logger.debug("Created Users: '"+username+"' and 'reader'");\r
+                       }\r
+               }\r
+       } catch(Exception e) {\r
+               logger.error("createUserIfNotExists failed on exception: " + e.getLocalizedMessage());\r
+               throw e;        // propagate\r
+       } finally {   //close resources\r
+               try {\r
+                       if(pstmt!=null) {\r
+                               pstmt.close();\r
+                       }\r
+                       if(stmt!=null) {\r
+                               stmt.close();\r
+                       }\r
+               } catch(SQLException se) {\r
+                       // nothing we can do\r
+               }\r
+       }\r
+    }\r
+    \r
+    private void createDatabaseWithRights(Connection conn, DatabaseProductType dbType, String dbName,\r
+               String ownerName, String ownerPW, String readerName, String readerPW) throws Exception {\r
+       Statement stmt = null;\r
+       try {\r
+                       stmt = conn.createStatement();\r
+               if(dbType==DatabaseProductType.POSTGRESQL) {\r
+                       // Postgres does not need passwords.\r
+                       String sql = "CREATE DATABASE "+dbName+" ENCODING 'UTF8' OWNER "+ownerName;\r
+                       stmt.executeUpdate(sql);\r
+                       sql = "GRANT CONNECT ON DATABASE nuxeo TO "+readerName;\r
+                       stmt.executeUpdate(sql);\r
+                       if (logger.isDebugEnabled()) {\r
+                               logger.debug("Created db: '"+dbName+"' with owner: '"+ownerName+"'");\r
+                               logger.debug(" Granted connect rights on: '"+dbName+"' to reader: '"+readerName+"'");\r
+                       }\r
+                       // Note that select rights for reader must be granted after Nuxeo startup.\r
+               } else if(dbType==DatabaseProductType.MYSQL) {\r
+                       String sql = "CREATE database "+dbName+" DEFAULT CHARACTER SET utf8";\r
+                       stmt.executeUpdate(sql);\r
+                       sql = "GRANT ALL PRIVILEGES ON "+dbName+".* TO '"+ownerName+"'@'localhost' IDENTIFIED BY '"\r
+                                       +ownerPW+"' WITH GRANT OPTION";\r
+                       stmt.executeUpdate(sql);\r
+                       sql = "GRANT SELECT ON "+dbName+".* TO '"+readerName+"'@'localhost' IDENTIFIED BY '"\r
+                                       +readerPW+"' WITH GRANT OPTION";\r
+                       stmt.executeUpdate(sql);\r
+                       if (logger.isDebugEnabled()) {\r
+                               logger.debug("Created db: '"+dbName+"' with owner: '"+ownerName+"'");\r
+                               logger.debug(" Granted SELECT rights on: '"+dbName+"' to reader: '"+readerName+"'");\r
+                       }\r
+               } else {\r
+                       throw new UnsupportedOperationException("createDatabaseWithRights only supports PSQL - MySQL NYI!");\r
+               }\r
+       } catch(Exception e) {\r
+               logger.error("createDatabaseWithRights failed on exception: " + e.getLocalizedMessage());\r
+               throw e;        // propagate\r
+       } finally {   //close resources\r
+               try {\r
+                       if(stmt!=null) {\r
+                               stmt.close();\r
+                       }\r
+               } catch(SQLException se) {\r
+                       // nothing we can do\r
+               }\r
+       }\r
+\r
     }\r
     \r
     private void setServerRootDir() {\r
index ab5909642c41e1d3bf83bcdf24c5659a2d3cb736..82ba8f66bc6e853c72b56e1a3ea1e0b5811efd10 100644 (file)
@@ -43,6 +43,9 @@ public class JDBCTools {
        public static HashMap<String, DataSource> cachedDataSources = new HashMap<String, DataSource>();\r
     public static String CSPACE_REPOSITORY_NAME = "CspaceDS";\r
     public static String NUXEO_REPOSITORY_NAME = "NuxeoDS";\r
+    public static String NUXEO_MANAGER_DATASOURCE_NAME = "NuxeoMgrDS";\r
+    public static String NUXEO_READER_DATASOURCE_NAME = "NuxeoReaderDS";\r
+    public static String NUXEO_USER_NAME = "nuxeo";\r
     //\r
     // Private constants\r
     //\r
@@ -212,7 +215,9 @@ public class JDBCTools {
        if(DBProductName==null) {\r
                Connection conn = null;\r
                try {\r
-                   conn = getConnection(getDefaultRepositoryName()); //FIXME: REM - getDefaultRepositoryName returns the Nuxeo repo name -we should be using the "cspace" repo name\r
+                       // Nuxeo database may not yet exist, so use teh cspace db, which must exist.\r
+                   //conn = getConnection(getDefaultRepositoryName()); //FIXME: REM - getDefaultRepositoryName returns the Nuxeo repo name -we should be using the "cspace" repo name\r
+                   conn = getConnection(CSPACE_REPOSITORY_NAME);\r
                    DBProductName = conn.getMetaData().getDatabaseProductName();\r
                } catch (Exception e) {\r
                } finally {\r
index d482a9bff6a16ec04f0390a10f2d678fa618e57a..9e36793c1364c5c4266c0f32bd7ba6d631d071e4 100644 (file)
@@ -2,23 +2,28 @@
 -- recreate nuxeo database
 --
 DROP database IF EXISTS nuxeo;
-CREATE database nuxeo DEFAULT CHARACTER SET utf8;
+
+-- All the rest of what is commented out below is now handled at startup
+-- by the services web-app
+
+-- CREATE database nuxeo DEFAULT CHARACTER SET utf8;
 
 
 --
 -- grant privileges to users on nuxeo database
 --
-GRANT ALL PRIVILEGES ON nuxeo.* TO '@DB_NUXEO_USER@'@'localhost' IDENTIFIED BY '@DB_NUXEO_PASSWORD@' WITH GRANT OPTION;
+-- GRANT ALL PRIVILEGES ON nuxeo.* TO '@DB_NUXEO_USER@'@'localhost' IDENTIFIED BY '@DB_NUXEO_PASSWORD@' WITH GRANT OPTION;
 --
 -- Grant privileges to read-only user on Nuxeo, for reporting. 
 --
-GRANT SELECT ON nuxeo.* TO 'reader'@'localhost' IDENTIFIED BY 'read';
+-- GRANT SELECT ON nuxeo.* TO 'reader'@'localhost' IDENTIFIED BY 'read';
 --
 -- Grant privileges to remote read-only users on Nuxeo, for reporting. 
 -- These should be changed to reflect your domain. Avoid specifying
 -- 'reader'@'%' (while simple and flexible, this is a potential security hole).
 --
-GRANT SELECT ON nuxeo.* TO 'reader'@'%.berkeley.edu' IDENTIFIED BY 'read';
-GRANT SELECT ON nuxeo.* TO 'reader'@'%.movingimage.us' IDENTIFIED BY 'read';
+-- GRANT SELECT ON nuxeo.* TO 'reader'@'%.berkeley.edu' IDENTIFIED BY 'read';
+-- GRANT SELECT ON nuxeo.* TO 'reader'@'%.movingimage.us' IDENTIFIED BY 'read';
+
 FLUSH PRIVILEGES;
 
index 92648440b9c6c98567dcf336331cc50266a56d09..c0b999c17b275cb7b49338c95449b23a29a9cb5d 100644 (file)
@@ -4,18 +4,21 @@ DROP database IF EXISTS nuxeo;
 DROP USER IF EXISTS nuxeo;
 DROP USER IF EXISTS reader;
 
-CREATE ROLE @DB_NUXEO_USER@ WITH PASSWORD '@DB_NUXEO_PASSWORD@' LOGIN;
-CREATE ROLE reader WITH PASSWORD 'read' LOGIN;
+-- All the rest of what is commented out below is now handled at startup
+-- by the services web-app
+
+-- CREATE ROLE @DB_NUXEO_USER@ WITH PASSWORD '@DB_NUXEO_PASSWORD@' LOGIN;
+-- CREATE ROLE reader WITH PASSWORD 'read' LOGIN;
 
 --
 -- recreate nuxeo database
 --
-CREATE DATABASE nuxeo ENCODING 'UTF8' OWNER @DB_NUXEO_USER@;
+-- CREATE DATABASE nuxeo ENCODING 'UTF8' OWNER @DB_NUXEO_USER@;
 
 --
 -- Grant privileges to read-only user on Nuxeo, for reporting. 
 --
-GRANT CONNECT ON DATABASE nuxeo TO reader;
+-- GRANT CONNECT ON DATABASE nuxeo TO reader;
 
 -- GRANT SELECT ON ALL TABLES IN SCHEMA public TO reader;
 -- This must be run by hand, after the system has already started up,