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 {
// 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();
}
} 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();
+ }
}
}
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;
*/
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)
*/
//
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();
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
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>
// 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;
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);
}
}
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;
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;
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;
//import org.mortbay.log.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import org.springframework.security.acls.model.AlreadyExistsException;
public class ActionGroup {
String name;
ActionType[] actions;
+
+ public String getName() {
+ return name;
+ }
}
static ActionGroup ACTIONGROUP_CRUDL;
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
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 {
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);
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) {
/*
* 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);
logger.debug("Finished full perm generation for "
+ ":" + permission.getTenantId()
+ ":" + permission.getResourceName()
- + ":" + ACTIONGROUP_RL
+ + ":" + actionGroup.getName()
+ ":" + profiler.getCumulativeTime());
}
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);
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.");
+ }
}
/*
import java.util.Set;
+
+
//import org.jboss.resteasy.core.ResourceMethod;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ServerResponse;
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;
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);
}