]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
DRYD-871: Fixed issue where single tenant deployments had incorrect Nuxeo datasource...
authorRichard Millet <remillet@yahoo.com>
Sat, 9 May 2020 05:56:56 +0000 (22:56 -0700)
committerRichard Millet <remillet@yahoo.com>
Sat, 9 May 2020 05:56:56 +0000 (22:56 -0700)
3rdparty/nuxeo/nuxeo-server/9.10-HF30/config/proto-datasource-config.xml
services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java
services/common/src/main/java/org/collectionspace/services/common/storage/JDBCTools.java
services/config/src/main/java/org/collectionspace/services/common/config/ConfigUtils.java
services/config/src/main/resources/tenant.xsd

index 4887c1673f8e6c90968d74eda054ce87b28e498c..1df6521fea8996ef74b262c4de2cc93bc6afc389 100644 (file)
@@ -2,31 +2,21 @@
 <component name="org.nuxeo.runtime.datasource.testsci.contrib">
        
   <extension target="org.nuxeo.runtime.datasource" point="datasources">
-       <datasource name="jdbc/testsci_domain" driverClassName="org.postgresql.Driver"
+    <datasource name="jdbc/place_holder" driverClassName="org.postgresql.Driver"
       maxPoolSize="100" minPoolSize="5" blockingTimeoutMillis="10000"
-      url="jdbc:postgresql://localhost:5432/testsci_domain" validationQuery=""
-      username="nuxeo6" password="nuxeo6"
+      url="jdbc:postgresql://localhost:5432/place_holder" validationQuery=""
+      username="place_holder" password="place_holder"
       accessToUnderlyingConnectionAllowed="true" >
     </datasource>
-    <link name="jdbc/repository_testsci_domain" global="jdbc/testsci_domain" type="javax.sql.DataSource" />
 
        <!--
-               These links need to be moved into the context.xml file and become <Resource> instead of <link>
+               These links are for various Nuxeo EP services
        -->
-    <link name="jdbc/repository_default" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/repository_default" global="jdbc/default" type="javax.sql.DataSource"/>
-    <link name="jdbc/NuxeoDS" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/nxsqldirectory" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/nxrelations-default-jena" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/comment-relations" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/nxaudit-logs" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/nxjbpm" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/placeful_service_ds" global="jdbc/default" type="javax.sql.DataSource" />
-    <link name="jdbc/nxwebwidgets" global="jdbc/default" type="javax.sql.DataSource" />
+    <link name="jdbc/TenantDS" global="jdbc/default" type="javax.sql.DataSource" />
     <link name="jdbc/nxuidsequencer" global="jdbc/default" type="javax.sql.DataSource" />
-       
-       <!-- These properties are passed into the CSpace code that generates the final version
-       of this Nuxeo configuration file.
+
+    <!--
+        These properties are passed into the CSpace code that generates the final version of this Nuxeo configuration file.
        -->
        <property name="ServerName">@DB_SERVER_HOSTNAME@</property>
        <property name="JDBCOptions">@DB_JDBC_OPTIONS@</property>
index 4c8af469421160d0170aadd32041e9856610037e..8c1d1a048d48387bee87cfbaacec852d57d4d29c 100644 (file)
@@ -60,7 +60,9 @@ import org.collectionspace.services.nuxeo.listener.CSEventListener;
 import org.collectionspace.services.nuxeo.listener.AbstractCSEventListenerImpl;
 
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.dom4j.Attribute;
 import org.dom4j.Document;
+import org.dom4j.tree.DefaultElement;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -1249,6 +1251,11 @@ public class ServiceMain {
                        logger.info(String.format("Using prototype Nuxeo server configuration file at path %s",
                                        prototypeNuxeoDatasourceFile.getAbsolutePath()));
                }
+               
+               //
+               // If multiple active tenants, set the "default" repository for Nuxeo services to use.
+               //
+               setDefaultNuxeoRepository();
 
                //
                // For each tenant config we find, create the xml datasource config file and fill in the correct values.
@@ -1278,7 +1285,7 @@ public class ServiceMain {
                                        datasourceConfigDoc = (Document) prototypeConfigDoc.clone();
                                        // Update this config file by inserting values pertinent to the
                                        // current repository.
-                                       datasourceConfigDoc = updateRepositoryDatasourceDoc(datasourceConfigDoc, repositoryName, this.getCspaceInstanceId());
+                                       datasourceConfigDoc = updateRepositoryDatasourceDoc(datasourceConfigDoc, repositoryName, tbt.getRepositoryDomain(), this.getCspaceInstanceId());
                                        logger.debug("Updated Nuxeo datasource config file contents=\n" + datasourceConfigDoc.asXML());
 
                                        // Write this config file to the Nuxeo server config directory.
@@ -1291,6 +1298,137 @@ public class ServiceMain {
                }
        }
 
+       private boolean isImpliedDefaultRepository(RepositoryDomainType repositoryDomain) {
+               boolean result = false;
+
+               String repoName = repositoryDomain.getRepositoryName();
+               if (repoName == null || repoName.equals(ConfigUtils.DEFAULT_NUXEO_REPOSITORY_NAME)) {
+                       result = true;
+               }
+
+               return result;
+       }
+
+       private void setDefaultNuxeoRepository() throws Exception {
+               boolean moreThanOne = false;
+               boolean defaultIsSet = false;
+               String defaultRepositoryName = null;
+               
+               Hashtable<String, TenantBindingType> tenantBindingTypeMap = tenantBindingConfigReader.getTenantBindings();
+               
+               //
+               // Ensure we have at least one tenant binding and at least one corresponding repository domain 
+               //
+               if (tenantBindingTypeMap.values().size() == 0) {
+                       String msg = "At least one tenant binding must be configured.";
+                       throw new Exception(msg);
+               }
+               
+               //
+               // If we have just one tenant, make its (or one of its) repository domain(s) the default one.
+               //
+               if (tenantBindingTypeMap.values().size() == 1) {
+                       TenantBindingType tbt = (TenantBindingType) tenantBindingTypeMap.values().toArray()[0];
+                       List<RepositoryDomainType> repositoryDomainList = tbt.getRepositoryDomain();
+                       for (RepositoryDomainType repositoryDomain : repositoryDomainList) {
+                       if (repositoryDomain.isDefaultRepository() && defaultIsSet == false) {
+                               defaultIsSet = true;
+                               defaultRepositoryName = repositoryDomain.getRepositoryName();
+                       } else if (repositoryDomain.isDefaultRepository() && defaultIsSet == true) {
+                               moreThanOne = true;
+                               String msg = String.format("The tenant '%s' configuration is declaring '%s' the default repository.  However, '%s' is already the default repository.  Please ensure only one tenant is configured to be the default repository.",
+                                               tbt.getName(), repositoryDomain.getRepositoryName(), defaultRepositoryName);
+                               logger.error(msg);
+                       }
+                       }
+                       
+                       if (moreThanOne == true) {
+                               String msg = String.format("The tenant '%s' has more than one repository domain configured to be the default.  Please configure only one default repository.", 
+                                               tbt.getName());
+                               throw new Exception(msg);
+                       }
+                       
+                       //
+                       // If the only active tenant is not explicitly configuring a repository domain as default, do so now.
+                       //
+                       if (defaultIsSet == false) {
+                               RepositoryDomainType repositoryDomain = repositoryDomainList.get(0);
+                               repositoryDomain.setDefaultRepository(Boolean.TRUE);
+                               defaultIsSet = true;
+                               defaultRepositoryName = repositoryDomain.getRepositoryName();
+                       }
+                       
+                       if (logger.isDebugEnabled()) {
+                               String msg = String.format("The tenant '%s' has configured the default repository to be '%s'.", 
+                                               tbt.getName(), defaultRepositoryName);
+                               logger.debug(msg);
+                       }
+                       
+                       return;
+               }
+               
+               //
+               // If we have multiple tenants, figure out which one is declaring the default repository.
+               //
+               for (TenantBindingType tbt : tenantBindingTypeMap.values()) {
+                       List<RepositoryDomainType> repositoryDomainList = tbt.getRepositoryDomain();
+                       for (RepositoryDomainType repositoryDomain : repositoryDomainList) {
+                       if (repositoryDomain.isDefaultRepository() && defaultIsSet == false) {
+                               repositoryDomain.setDefaultRepository(Boolean.TRUE);
+                               defaultIsSet = true;
+                               defaultRepositoryName = repositoryDomain.getRepositoryName();
+                       } else if (repositoryDomain.isDefaultRepository() && defaultIsSet == true) {
+                               moreThanOne = true;
+                               String msg = String.format("The tenant '%s' configuration is declaring itself as the default repository domain.  However, another tenant has already declared '%s' to be the default repository.  Please ensure only one tenant is configured to have the default repository.",
+                                               tbt.getName(), defaultRepositoryName);
+                               logger.error(msg);
+                       }
+                       }
+               }
+               
+               //
+               // If more than one tenant has declared itself the default repository domain then
+               // throw an exception
+               //
+               if (moreThanOne == true) {
+                       String msg = "More than one tenant is configured to be the repository domain.  Please configure only one default repository.";
+                       throw new Exception(msg);
+               }
+               
+               //
+               // If no tenant has declared itself the default repository, look for an "implied" default.  Tenants configured with
+               // no repository name are inferred to be the default repository.  If more than one tenant is configured without a repository name,
+               // we'll use the first one found.
+               //
+               if (defaultIsSet == false) {
+               for (TenantBindingType tbt : tenantBindingTypeMap.values()) {
+                       List<RepositoryDomainType> repositoryDomainList = tbt.getRepositoryDomain();
+                       for (RepositoryDomainType repositoryDomain : repositoryDomainList) {
+                               if (isImpliedDefaultRepository(repositoryDomain) && defaultIsSet == false) {
+                                       repositoryDomain.setDefaultRepository(Boolean.TRUE);
+                                       defaultIsSet = true;
+                                       defaultRepositoryName = repositoryDomain.getRepositoryName();
+                               } else if (isImpliedDefaultRepository(repositoryDomain) && defaultIsSet == true) {
+                                       moreThanOne = true;
+                                       String msg = String.format("The tenant '%s' configuration implies (no repository name was defined) it is the default repository domain.  However, another tenant's domain has implied '%s' is the default repository.  Please ensure only one tenant defines the default repository.",
+                                                       tbt.getName(), defaultRepositoryName);
+                                       logger.error(msg);
+                               }
+                       }
+               }
+               }
+
+               //
+               // As of v6.0, this is just a warning.  However, future versions may require a default repository.
+               //
+               if (defaultIsSet == false) {
+                       logger.warn("No tenant's configuration explicitly declared nor implied its repository to be the default.  Future versions of CollectionSpace may require a default repository.");
+               } else if (logger.isDebugEnabled()) {
+                       String msg = String.format("The default repository has been set to '%s'.", defaultRepositoryName);
+                       logger.debug(msg);
+               }
+       }
+
        private File getProtoElasticsearchConfigFile() throws Exception {
                File result = new File(getCspaceServicesConfigDir() + File.separator
                                + getNuxeoProtoElasticsearchConfigFilename());
@@ -1505,8 +1643,10 @@ public class ServiceMain {
     /*
      * This method is filling out the proto-datasource-config.xml file with tenant specific repository information.
      */
-    private Document updateRepositoryDatasourceDoc(Document repoConfigDoc, String repositoryName,
+    private Document updateRepositoryDatasourceDoc(Document repoConfigDoc, String repositoryName, List<RepositoryDomainType> repoDomainList,
                String cspaceInstanceId) {
+
+       boolean isDefaultRepository = ConfigUtils.containsDefaultRepository(repoDomainList);
         String databaseName = JDBCTools.getDatabaseName(repositoryName, cspaceInstanceId);
 
         repoConfigDoc = XmlTools.setAttributeValue(repoConfigDoc, "/component", "name",
@@ -1550,12 +1690,20 @@ public class ServiceMain {
         repoConfigDoc = XmlTools.setAttributeValue(repoConfigDoc,
                        ConfigUtils.DATASOURCE_EXTENSION_POINT_XPATH + "/datasource", "password", password);
 
-        // Set the <link> element's name attribute
-        repoConfigDoc = XmlTools.setAttributeValue(repoConfigDoc,
-                       ConfigUtils.DATASOURCE_EXTENSION_POINT_XPATH + "/link", "name", "jdbc/repository_" + repositoryName);
-        // Set the <link> element's global attribute
-        repoConfigDoc = XmlTools.setAttributeValue(repoConfigDoc,
-                       ConfigUtils.DATASOURCE_EXTENSION_POINT_XPATH + "/link", "global", datasoureName);
+        //
+        // Adjust various Nuxeo components' datasource links to use the tenant repository.
+        // In a multi-tenant deployment, it is unclear if there will be name clashes -i.e., two or more tenants declaring
+        // themselves to be the datasource for a Nuxeo component.
+        //
+        List<DefaultElement> linkNodes = XmlTools.getElementNodes(repoConfigDoc, ConfigUtils.DATASOURCE_EXTENSION_POINT_XPATH + "/link");
+        for (DefaultElement node : linkNodes) {
+               Attribute nameAttribute = node.attribute("name");
+               if (nameAttribute.getValue().equals(ConfigUtils.CS_TENANT_DATASOURCE_VALUE)) {
+                       nameAttribute.setValue("jdbc/repository_" + repositoryName);
+               }
+               Attribute globalAttribute = node.attribute("global");
+                       globalAttribute.setValue(datasoureName);
+        }
 
         return repoConfigDoc;
     }
index b62dfd6025518094451fbd252869abd9303282ba..311bf37cba47c3d6fcf2747b23fa1201e2d03c86 100644 (file)
@@ -125,7 +125,7 @@ public class JDBCTools {
                        try {
                                envCtx.close();
                        } catch (Exception e) {
-                               logger.error("Error getting DataSource for: " + dataSourceName, e);
+                               logger.trace("Error getting DataSource for: " + dataSourceName, e);
                        }
                    }
                }
index e218d69f3e234ec2a0384e1902de016b99958d96..3792c2df563c3c275fa5bd4ef886825fc15c20e1 100644 (file)
@@ -15,6 +15,7 @@ public class ConfigUtils {
     public static final String COMPONENT_EXTENSION_XPATH = "/component" + EXTENSION_XPATH;
     public static final String DATASOURCE_EXTENSION_POINT_XPATH = String.format(COMPONENT_EXTENSION_XPATH, "datasources");
     public static final String REPOSITORY_EXTENSION_POINT_XPATH = String.format(COMPONENT_EXTENSION_XPATH, "repository");
+    public static final String CS_TENANT_DATASOURCE_VALUE = "jdbc/TenantDS";
     public static final String CONFIGURATION_EXTENSION_POINT_XPATH = String.format(COMPONENT_EXTENSION_XPATH, "configuration");
     public static final String ELASTICSEARCH_INDEX_EXTENSION_XPATH = String.format(EXTENSION_XPATH, "elasticSearchIndex");
     public static final String ELASTICSEARCH_EXTENSIONS_EXPANDER_STR = "%elasticSearchIndex_extensions%";
@@ -43,6 +44,24 @@ public class ConfigUtils {
                
        return result;
     }
+    
+    /*
+     * Returns 'true' if the tenant declares the default repository.
+     */
+    public static boolean containsDefaultRepository(List<RepositoryDomainType> repoDomainList) {
+       boolean result = false;
+
+               if (repoDomainList != null && repoDomainList.isEmpty() == false) {
+                       for (RepositoryDomainType repoDomain : repoDomainList) {
+                               if (repoDomain.isDefaultRepository() == true) {
+                                       result = true;
+                                       break;
+                               }
+                       }
+               }
+   
+       return result;
+    }
         
     public static String getRepositoryName(TenantBindingType tenantBindingType, String domainName) {
                String result = null;
@@ -60,7 +79,7 @@ public class ConfigUtils {
                } else {
                        logger.error(String.format("There was no domain name specified on a call to getRepositoryName() method."));
                }
-               
+
                if (result == null && logger.isTraceEnabled()) {
                        logger.trace(String.format("Could not find the repository name for tenent name='%s' and domain='%s'",
                                        tenantBindingType.getName(), domainName));
index 844ca79b34a5154558bd29b50bac1a77e81ecb23..0cd5bf5ff1248ed11e712742efd74c3b4be95035 100644 (file)
@@ -82,6 +82,7 @@
                <xs:attribute name="name" type="xs:string" use="required"/>
                <xs:attribute name="storageName" type="xs:string" use="required"/>
                <xs:attribute name="repositoryName" type="xs:string" use="optional" default="default"/>
+               <xs:attribute name="defaultRepository" type="xs:boolean" use="optional" default="false"/>
                <xs:attribute name="repositoryClient" type="xs:string" use="optional" default="nuxeo-java"/>
        </xs:complexType>