]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-6973: Reducing the number of workflow permissions to help speed up boot/restar...
authorremillet <remillet@yahoo.com>
Tue, 12 Jul 2016 19:43:14 +0000 (12:43 -0700)
committerremillet <remillet@yahoo.com>
Tue, 12 Jul 2016 19:43:14 +0000 (12:43 -0700)
services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java
services/common/src/main/java/org/collectionspace/services/common/CollectionSpaceServiceContextListener.java
services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java
services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java
services/common/src/main/java/org/collectionspace/services/common/config/TenantBindingConfigReaderImpl.java
services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java

index 48f79b626f5a866fa546636bd843b9c4f5fc29a1..6d32ab644d10b6a50ba76b38f46f54d2440b593f 100644 (file)
@@ -5,6 +5,7 @@ import javax.servlet.ServletContextEvent;
 import org.jboss.resteasy.core.Dispatcher;
 import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap;
 import org.collectionspace.services.common.ResourceMap;
+import java.util.Date;
 
 public class CSpaceResteasyBootstrap extends ResteasyBootstrap {
        
@@ -14,13 +15,13 @@ public class CSpaceResteasyBootstrap extends ResteasyBootstrap {
                // This call to super instantiates and initializes our JAX-RS application class.
                // The application class is org.collectionspace.services.jaxrs.CollectionSpaceJaxRsApplication.
                //
-               System.out.println("[INFO] Starting up the CollectionSpace Services' JAX-RS application.");
+               System.out.println(String.format("%tc [INFO] Starting up the CollectionSpace Services' JAX-RS application.", new Date()));
                        super.contextInitialized(event);
                        CollectionSpaceJaxRsApplication app = 
                                (CollectionSpaceJaxRsApplication)deployment.getApplication();
                        Dispatcher disp = deployment.getDispatcher();
                        disp.getDefaultContextObjects().put(ResourceMap.class, app.getResourceMap());
-                       System.out.println("[INFO] CollectionSpace Services' JAX-RS application started.");
+               System.out.println(String.format("%tc [INFO] CollectionSpace Services' JAX-RS application started.", new Date()));
                } catch (Throwable e) {
                        e.printStackTrace();
                }
index 794aa11e42fa8edca3fb9f1417b9dcc048f9a2c7..07deb97a11bd3709073b403adaa846737ed10d80 100644 (file)
@@ -38,14 +38,28 @@ public class CollectionSpaceServiceContextListener implements ServletContextList
         } catch (Throwable e) {
             e.printStackTrace();
             //fail here
+            System.err.println("[ERROR] ***");
             System.err.println("[ERROR] The CollectionSpace Services could not initialize.  Please see the log files for details.");
-            throw new RuntimeException(e);
+            System.err.println("[ERROR] ***");
+//            throw new RuntimeException(e);
         }
     }
 
     @Override
     public void contextDestroyed(ServletContextEvent event) {
-        //ServiceMain.getInstance().release();
-        JpaStorageUtils.releaseEntityManagerFactories();
+        ServiceMain instance = null;
+        
+        try {
+               ServiceMain.getInstance();
+        } catch (Throwable t) {
+               // Do nothing.  Error already logged by the Services layer
+        } finally {
+               if (instance != null) {
+                       instance.release();
+               } else {
+                       System.err.println("ERROR: The CollectionSpace Services layer failed to startup successfully.  Look in the tomcat logs and cspace-services logs for details.");
+               }
+               JpaStorageUtils.releaseEntityManagerFactories();
+        }
     }
 }
index 6100e1cee21a55e67f32db6c4e22c282d4497a5e..cbf820b6ead44186056c1c823631b67011386047 100644 (file)
@@ -42,6 +42,7 @@ import org.collectionspace.services.config.types.PropertyItemType;
 import org.collectionspace.services.config.types.PropertyType;
 import org.collectionspace.services.nuxeo.client.java.NuxeoConnectorEmbedded;
 import org.collectionspace.services.nuxeo.client.java.TenantRepository;
+
 import org.apache.commons.io.FileUtils;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.dom4j.Document;
@@ -55,7 +56,19 @@ import org.slf4j.LoggerFactory;
  */
 public class ServiceMain {
 
-    final Logger logger = LoggerFactory.getLogger(ServiceMain.class);
+    final static Logger logger = LoggerFactory.getLogger(ServiceMain.class);
+    
+    /**
+     * For some reason, we have trouble getting logging from this class directed to
+     * the tomcat/catalina console log file.  So we do it explicitly with this method.
+     * 
+     * @param str
+     */
+    private static void mirrorToStdOut(String str) {
+       System.out.println(str);
+       ServiceMain.logger.info(str);
+    }
+
     /**
      * volatile is used here to assume about ordering (post JDK 1.5)
      */
@@ -179,21 +192,24 @@ public class ServiceMain {
         //
         if (getClientType().equals(ClientType.JAVA)) {
             nuxeoConnector = NuxeoConnectorEmbedded.getInstance();
+            mirrorToStdOut("\nStarting Nuxeo platform...");
             nuxeoConnector.initialize(
                        getServerRootDir(),
                        getServicesConfigReader().getConfiguration().getRepositoryClient(),
                        ServiceMain.servletContext);
+            mirrorToStdOut("Nuxeo platform started successfully.\n");
         } else {
                //
                // Exit if we don't have the correct/known client type
                //
                throw new RuntimeException("Unknown CollectionSpace services client type: " + getClientType());
         }
+                
         //
         // Create all the default user accounts and permissions.  Since some of our "cspace" database config files
         // for Spring need to be created at build time, the "cspace" database already will be suffixed with the
         // correct 'cspaceInstanceId' so we don't need to pass it to the JDBCTools methods.
-        //
+        //        
                try {
                        AuthorizationCommon.createDefaultWorkflowPermissions(tenantBindingConfigReader);
                        String cspaceDatabaseName = getCspaceDatabaseName();
@@ -215,8 +231,42 @@ public class ServiceMain {
                logger.error("handlePostNuxeoInitDBTasks failed with exception(s): " + e.getLocalizedMessage(), e);
         }
         */
+               showTenantStatus();
     }
-
+    
+    private void showTenantStatus() {
+       Hashtable<String,TenantBindingType> tenantBindingsList = tenantBindingConfigReader.getTenantBindings(true);
+       mirrorToStdOut("++++++++++++++++ Summary - CollectionSpace tenant status. ++++++++++++++++++++++++");
+       String headerTemplate = "%10s %10s %30s %60s %10s";
+       String headerUnderscore = String.format(headerTemplate,
+                       "______",
+                       "__",
+                       "____",
+                       "____________",
+                       "_______");
+       String header = String.format(headerTemplate,
+                       "Status",
+                       "ID",
+                       "Name",
+                       "Display Name",
+                       "Version");
+       mirrorToStdOut(header);
+       mirrorToStdOut(headerUnderscore);
+                       
+       for (String tenantId : tenantBindingsList.keySet()) {
+               TenantBindingType tenantBinding = tenantBindingsList.get(tenantId);
+               String statusLine = String.format(headerTemplate,
+                               tenantBinding.isCreateDisabled() ? "Disabled" : "Active",
+                               tenantBinding.getId(),
+                               tenantBinding.getName(),
+                               tenantBinding.getDisplayName(),
+                               tenantBinding.getVersion());
+               mirrorToStdOut(statusLine);
+       }
+       // footer
+       mirrorToStdOut("++++++++++++++++ ........................................ ++++++++++++++++++++++++");
+    }
+    
     /**
      * release releases all resources occupied by service layer infrastructure
      * but not necessarily those occupied by individual services
@@ -338,8 +388,8 @@ public class ServiceMain {
                     if (Tools.isEmpty(initHandlerClassname)) {
                         continue;
                     }
-                    if (logger.isInfoEnabled()) {
-                        logger.info(String.format("Firing post-init handler %s ...", initHandlerClassname));
+                    if (ServiceMain.logger.isDebugEnabled()) {
+                       ServiceMain.logger.debug(String.format("Firing post-init handler %s ...", initHandlerClassname));
                     }
 
                     List<org.collectionspace.services.config.service.InitHandler.Params.Field>
@@ -508,41 +558,42 @@ public class ServiceMain {
 
         // First check and create the roles as needed. (nuxeo and reader)
         for (TenantBindingType tenantBinding : tenantBindings.values()) {
-                String tId = tenantBinding.getId();
-                String tName = tenantBinding.getName();
-                List<RepositoryDomainType> repoDomainList = tenantBinding.getRepositoryDomain();
-                for (RepositoryDomainType repoDomain : repoDomainList) {
-                        String repoDomainName = repoDomain.getName();
-                        String repositoryName = repoDomain.getRepositoryName();
-                        String cspaceInstanceId = getCspaceInstanceId();
-                        String dbName = JDBCTools.getDatabaseName(repositoryName, cspaceInstanceId);
-                        if (nuxeoDBsChecked.contains(dbName)) {
-                                if (logger.isDebugEnabled()) {
-                                        logger.debug("Another user of db: " + dbName + ": Repo: " + repoDomainName
-                                                        + " and tenant: " + tName + " (id:" + tId + ")");
-                                }
-                        } else {
-                                if (logger.isDebugEnabled()) {
-                                        logger.debug("Need to prepare db: " + dbName + " for Repo: " + repoDomainName
-                                                        + " and tenant: " + tName + " (id:" + tId + ")");
-                                }
-                                boolean dbExists = JDBCTools.hasDatabase(dbType, dbName);
-                                if (dbExists) {
-                                        if (logger.isDebugEnabled()) {
-                                                logger.debug("Database: " + dbName + " already exists.");
-                                        }
-                                } else {
-                                        // Create the user as needed
-                                        JDBCTools.createNewDatabaseUser(JDBCTools.CSADMIN_DATASOURCE_NAME, repositoryName, cspaceInstanceId, dbType, nuxeoUser, nuxeoPW);
-                                        if (readerUser != null) {
-                                            JDBCTools.createNewDatabaseUser(JDBCTools.CSADMIN_DATASOURCE_NAME, repositoryName, cspaceInstanceId, dbType, readerUser, readerPW);
-                                        }
-                                        // Create the database
-                                        createDatabaseWithRights(dbType, dbName, nuxeoUser, nuxeoPW, readerUser, readerPW);
-                                }
-                                nuxeoDBsChecked.add(dbName);
+            String tId = tenantBinding.getId();
+            String tName = tenantBinding.getName();
+            
+            List<RepositoryDomainType> repoDomainList = tenantBinding.getRepositoryDomain();
+            for (RepositoryDomainType repoDomain : repoDomainList) {
+                String repoDomainName = repoDomain.getName();
+                String repositoryName = repoDomain.getRepositoryName();
+                String cspaceInstanceId = getCspaceInstanceId();
+                String dbName = JDBCTools.getDatabaseName(repositoryName, cspaceInstanceId);
+                if (nuxeoDBsChecked.contains(dbName)) {
+                    if (logger.isDebugEnabled()) {
+                            logger.debug("Another user of db: " + dbName + ": Repo: " + repoDomainName
+                                            + " and tenant: " + tName + " (id:" + tId + ")");
+                    }
+                } else {
+                    if (logger.isDebugEnabled()) {
+                            logger.debug("Need to prepare db: " + dbName + " for Repo: " + repoDomainName
+                                            + " and tenant: " + tName + " (id:" + tId + ")");
+                    }
+                    boolean dbExists = JDBCTools.hasDatabase(dbType, dbName);
+                    if (dbExists) {
+                        if (logger.isDebugEnabled()) {
+                                logger.debug("Database: " + dbName + " already exists.");
+                        }
+                    } else {
+                        // Create the user as needed
+                        JDBCTools.createNewDatabaseUser(JDBCTools.CSADMIN_DATASOURCE_NAME, repositoryName, cspaceInstanceId, dbType, nuxeoUser, nuxeoPW);
+                        if (readerUser != null) {
+                            JDBCTools.createNewDatabaseUser(JDBCTools.CSADMIN_DATASOURCE_NAME, repositoryName, cspaceInstanceId, dbType, readerUser, readerPW);
                         }
-                } // Loop on repos for tenant
+                        // Create the database
+                        createDatabaseWithRights(dbType, dbName, nuxeoUser, nuxeoPW, readerUser, readerPW);
+                    }
+                    nuxeoDBsChecked.add(dbName);
+                }
+            } // Loop on repos for tenant
         } // Loop on tenants
                 
         return nuxeoDBsChecked;
@@ -721,7 +772,7 @@ public class ServiceMain {
             serverRootDir = "."; //assume server is started from server root, e.g. server/cspace
             String msg = String.format("System property '%s' was not set.  Using '%s' instead.",
                        SERVER_HOME_PROPERTY, serverRootDir);
-            logger.warn(msg);
+            mirrorToStdOut(msg);
         }
     }
 
index e7e99b4259217aae274207abe4e86e7acd8a3e00..01c03ceb53f205e339f7cc4795b462b639614df6 100644 (file)
@@ -8,6 +8,7 @@ import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.UUID;
@@ -31,7 +32,6 @@ import org.collectionspace.services.authorization.perms.ActionType;
 import org.collectionspace.services.authorization.perms.EffectType;
 import org.collectionspace.services.authorization.perms.Permission;
 import org.collectionspace.services.authorization.perms.PermissionAction;
-
 import org.collectionspace.services.client.Profiler;
 import org.collectionspace.services.client.RoleClient;
 import org.collectionspace.services.client.workflow.WorkflowClient;
@@ -45,7 +45,6 @@ import org.collectionspace.services.common.storage.JDBCTools;
 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
 import org.collectionspace.services.config.service.ServiceBindingType;
 import org.collectionspace.services.config.tenant.TenantBindingType;
-
 import org.collectionspace.services.lifecycle.Lifecycle;
 import org.collectionspace.services.lifecycle.TransitionDef;
 import org.collectionspace.services.lifecycle.TransitionDefList;
@@ -53,7 +52,6 @@ import org.collectionspace.services.lifecycle.TransitionDefList;
 //import org.mortbay.log.Log;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import org.springframework.security.acls.model.AlreadyExistsException;
 
 
@@ -78,6 +76,10 @@ public class AuthorizationCommon {
        public class ActionGroup {
                String name;
                ActionType[] actions;
+               
+               public String getName() {
+                       return name;
+               }
        }
        
        static ActionGroup ACTIONGROUP_CRUDL;
@@ -303,14 +305,14 @@ public class AuthorizationCommon {
     
     private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
                ServiceBindingType serviceBinding,
-               TransitionDef transitionDef,
+               String transitionVerb,
                ActionGroup actionGroup)
     {
        Permission result = null;
        String workFlowServiceSuffix;
        String transitionName;
-       if (transitionDef != null) {
-               transitionName = transitionDef.getName();
+       if (transitionVerb != null) {
+               transitionName = transitionVerb;
                workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
        } else {
                transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created                 
@@ -1023,6 +1025,19 @@ public class AuthorizationCommon {
            return pa;
        }
        
+       private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
+               HashSet<String> result = new HashSet<String>();
+               
+               TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
+       for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
+               String transitionVerb = transitionDef.getName();
+               String[] tokens = transitionVerb.split("_");  // Split the verb into words.  The workflow verbs are compound words combined with the '_' character.
+               result.add(tokens[0]); // We only care about the first word.
+       }
+
+       return result;
+       }
+       
        private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
                TransitionDefList result = null;
                try {
@@ -1069,6 +1084,7 @@ public class AuthorizationCommon {
                Hashtable<String, TenantBindingType> tenantBindings =
                        tenantBindingConfigReader.getTenantBindings();
                for (String tenantId : tenantBindings.keySet()) {
+                       logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
                        TenantBindingType tenantBinding = tenantBindings.get(tenantId);
                        Role adminRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
                        Role readonlyRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_READER);
@@ -1078,25 +1094,16 @@ public class AuthorizationCommon {
                                                try {
                                                em.getTransaction().begin();
                                                TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
-                                               for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
+                                               HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
+                                               for (String transitionVerb : transitionVerbList) {
                                                        //
                                                        // Create the permission for the admin role
-                                                       Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionDef, ACTIONGROUP_CRUDL);
-                                                       persist(em, adminPerm, adminRole, true);
+                                                       Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
+                                                       persist(em, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
                                                        //
                                                        // Create the permission for the read-only role
-                                                       Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionDef, ACTIONGROUP_RL);
-                                                       
-                                                       Profiler profiler = new Profiler(AuthorizationCommon.class, 1);
-                                                       profiler.start("createDefaultPermissions started:" + readonlyPerm.getCsid());
-                                                       persist(em, readonlyPerm, readonlyRole, true); // Persist/store the permission and permrole records and related Spring Security info
-                                                       profiler.stop();
-                                                       logger.debug("Finished full perm generation for "
-                                                                       + ":" + tenantBinding.getId()
-                                                                       + ":" + serviceBinding.getName()
-                                                                       + ":" + transitionDef.getName()
-                                                                       + ":" + ACTIONGROUP_RL
-                                                                       + ":" + profiler.getCumulativeTime());                                          
+                                                       Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);                                                      
+                                                       persist(em, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
                                                }
                                                em.getTransaction().commit();
                                        } catch (IllegalStateException e) {
@@ -1145,7 +1152,7 @@ public class AuthorizationCommon {
     /*
      * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
      */
-    private static void persist(EntityManager em, Permission permission, Role role, boolean enforceTenancy) throws Exception {
+    private static void persist(EntityManager em, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
                AuthorizationStore authzStore = new AuthorizationStore();
                // First persist the Permission record
                authzStore.store(em, permission);
@@ -1168,7 +1175,7 @@ public class AuthorizationCommon {
                        logger.debug("Finished full perm generation for "
                                        + ":" + permission.getTenantId()
                                        + ":" + permission.getResourceName()
-                                       + ":" + ACTIONGROUP_RL
+                                       + ":" + actionGroup.getName()
                                        + ":" + profiler.getCumulativeTime());
                }
         
index 48904f4cfbdbadbd13bb97c78b1d8e60c6a317d6..c60f8ae4052b6e0a73f6b2f4ac4b754448d83396 100644 (file)
@@ -200,6 +200,8 @@ public class TenantBindingConfigReaderImpl extends AbstractConfigReaderImpl<List
                        if (!tenantBinding.isCreateDisabled()) {
                                enabledTenantBindings.put(tenantBinding.getId(), tenantBinding);
                        } else {
+                               logger.warn(String.format("The tenant '%s':'%s' is marked as disabled in its bindings file.",
+                                               tenantBinding.getName(), tenantBinding.getId()));
                        }
                        readDomains(tenantBinding);
                        readServiceBindings(tenantBinding);
@@ -210,6 +212,13 @@ public class TenantBindingConfigReaderImpl extends AbstractConfigReaderImpl<List
                                        logger.info("Tenant tenant id={} is marked createDisabled.", tenantBinding.getId());
                        }
                }
+               
+               //
+               // Ensure that at least one tenant is enabled, otherwise abort the startup.
+               //
+               if (enabledTenantBindings.isEmpty() == true) {
+                       throw new Exception("All of the configured tenants are marked as disabled in their tenant bindings.  At least one tenant needs to be enabled.");
+               }
        }
 
        /*
index 083ab4006de56483220640a41d0b0053551c5a22..340a375842840023c6013233a1a8faa7951a32ab 100644 (file)
@@ -33,6 +33,8 @@ import java.util.HashMap;
 import java.util.Set;
 
 
+
+
 //import org.jboss.resteasy.core.ResourceMethod;
 import org.jboss.resteasy.core.ResourceMethodInvoker;
 import org.jboss.resteasy.core.ServerResponse;
@@ -58,9 +60,11 @@ import org.collectionspace.services.client.index.IndexClient;
 import org.collectionspace.services.client.workflow.WorkflowClient;
 import org.collectionspace.services.common.CSWebApplicationException;
 import org.collectionspace.services.common.CollectionSpaceResource;
+import org.collectionspace.services.common.ServiceMain;
 import org.collectionspace.services.common.document.JaxbUtils;
 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
 import org.collectionspace.services.common.security.SecurityUtils;
+import org.collectionspace.services.config.tenant.TenantBindingType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -235,15 +239,25 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn
                String userId = AuthN.get().getUserId();
                                
                try {
-                       // Need to ensure that user is associated to a tenant
+                       //
+                       // Need to ensure that user's tenant is not disabled
                        String tenantId = AuthN.get().getCurrentTenantId();
+                       TenantBindingType tenantBindingType = ServiceMain.getInstance().getTenantBindingConfigReader().getTenantBinding(tenantId);
+                       boolean tenantDisabled = tenantBindingType.isCreateDisabled();
+                       if (tenantDisabled == true) {
+                               String errMsg = String.format("The user %s's tenant '%s' is disabled.  Contact your CollectionSpace administrator.",
+                                               userId, tenantBindingType.getDisplayName());
+                               Response response = Response.status(
+                                               Response.Status.CONFLICT).entity(errMsg).type("text/plain").build();
+                               throw new CSWebApplicationException(response);                          
+                       }
                } catch (IllegalStateException ise) {
-                       String msg = "User's account is not associated to any active tenants, userId=" + userId;
+                       String errMsg = "User's account is not associated to any active tenants, userId=" + userId;
                        // Note the RFC on return types:
                        // If the request already included Authorization credentials, then the 401 response 
                        // indicates that authorization has been refused for those credentials.
                        Response response = Response.status(
-                                       Response.Status.UNAUTHORIZED).entity(msg).type("text/plain").build();
+                                       Response.Status.UNAUTHORIZED).entity(errMsg).type("text/plain").build();
                        throw new CSWebApplicationException(ise, response);
                }