From: remillet Date: Fri, 10 Mar 2017 20:12:02 +0000 (-0800) Subject: CSPACE-7083: Adding tenant bindings for declaring Nuxeo event listeners. X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=945b45f9eb3ba60f82f527fb100a99af5c4efd1d;p=tmp%2Fjakarta-migration.git CSPACE-7083: Adding tenant bindings for declaring Nuxeo event listeners. --- diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/AbstractUpdateObjectLocationValues.java b/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/AbstractUpdateObjectLocationValues.java index 0f4826ae9..9430b3e92 100644 --- a/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/AbstractUpdateObjectLocationValues.java +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/AbstractUpdateObjectLocationValues.java @@ -1,9 +1,8 @@ package org.collectionspace.services.listener; -import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashSet; -import java.util.Locale; +import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; @@ -17,6 +16,7 @@ import org.collectionspace.services.common.api.RefName; import org.collectionspace.services.movement.nuxeo.MovementConstants; import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface; import org.collectionspace.services.nuxeo.client.java.CoreSessionWrapper; +import org.collectionspace.services.nuxeo.listener.AbstractCSEventListenerImpl; import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.nuxeo.common.collections.ScopeType; import org.nuxeo.common.collections.ScopedMap; @@ -24,14 +24,11 @@ import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.event.DocumentEventTypes; -import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; import org.nuxeo.ecm.core.event.Event; -import org.nuxeo.ecm.core.event.EventListener; import org.nuxeo.ecm.core.event.impl.DocumentEventContext; -import org.nuxeo.ecm.core.versioning.VersioningService; -public abstract class AbstractUpdateObjectLocationValues implements EventListener { +public abstract class AbstractUpdateObjectLocationValues extends AbstractCSEventListenerImpl { // FIXME: We might experiment here with using log4j instead of Apache Commons Logging; // am using the latter to follow Ray's pattern for now @@ -75,8 +72,8 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene "AND (ecm:currentLifeCycleState <> 'deleted') " + NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT; + // Used to set/get temp values in a DocumentModel instance private static final String IGNORE_LOCATION_UPDATE_EVENT_LABEL = "IGNORE_LOCATION_UPDATE_EVENT"; - private static final String IGNORE_LOCATION_UPDATE_EVENT_OPTION = ScopeType.DEFAULT.getScopePrefix() + "IGNORE_LOCATION_UPDATE_EVENT"; public enum EventNotificationDocumentType { // Document type about which we've received a notification @@ -84,8 +81,12 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene MOVEMENT, RELATION, COLLECTIONOBJECT; } - private static void dumpEvent(Event event, String message) { - if (logger.isDebugEnabled()) { + private static void logEvent(Event event, String message) { + logEvent(event, message, false); + } + + private static void logEvent(Event event, String message, boolean forceLogging) { + if (logger.isDebugEnabled() || forceLogging) { DocumentEventContext docEventContext = (DocumentEventContext) event.getContext(); DocumentModel docModel = docEventContext.getSourceDocument(); String eventType = event.getName(); @@ -138,12 +139,17 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene @Override public void handleEvent(Event event) throws ClientException { - dumpEvent(event, "Update Location"); // Ensure we have all the event data we need to proceed. - if (event.getContext() == null || !(event.getContext() instanceof DocumentEventContext)) { + if (isRegistered(event) == false || !(event.getContext() instanceof DocumentEventContext)) { + if (logger.isTraceEnabled() == true) { + logEvent(event, "Update Location", true); + } return; } + Map params = this.getParams(event); + logEvent(event, "Update Location"); + DocumentEventContext docEventContext = (DocumentEventContext) event.getContext(); DocumentModel eventDocModel = docEventContext.getSourceDocument(); String eventType = event.getName(); @@ -155,7 +161,7 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene // exits if it is set. if (shouldIgnoreEvent(docEventContext, IGNORE_LOCATION_UPDATE_EVENT_LABEL) == true) { return; - } + } // // Ensure this event relates to a relationship record (between cataloging and movement records) or a movement record. If so, get the CSID @@ -235,23 +241,24 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene DocumentModel mostRecentMovementDocModel; for (String collectionObjectCsid : collectionObjectCsids) { collectionObjectDocModel = getCurrentDocModelFromCsid(session, collectionObjectCsid); - if (isActiveDocument(collectionObjectDocModel) == true) { + if (isActiveDocument(collectionObjectDocModel) == true) { + DocumentModel movementDocModel = getCurrentDocModelFromCsid(session, eventMovementCsid); // // Get the CollectionObject's most recent, valid related Movement to use for computing the // object's current location. // - String mostRecentLocation = getMostRecentMovement(event, session, collectionObjectCsid, + String mostRecentLocation = getMostRecentLocation(event, session, collectionObjectCsid, isAboutToBeRemovedEvent, eventMovementCsid); // // Update the CollectionObject's Computed Current Location field with the Movement record's location // - boolean didLocationChange = updateCollectionObjectLocation(collectionObjectDocModel, mostRecentLocation); + boolean didLocationChange = updateCollectionObjectLocation(collectionObjectDocModel, movementDocModel, mostRecentLocation); // // If the location changed, save/persist the change to the repository and log the change. // if (didLocationChange == true) { - persisLocationChange(session, collectionObjectDocModel); + persistLocationChange(session, collectionObjectDocModel); // // Log an INFO message if we've changed the cataloging record's location // @@ -269,13 +276,12 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene // // Disable update/documentModified events and persist the location change. // - private void persisLocationChange(CoreSessionInterface session, DocumentModel collectionObjectDocModel) { + private void persistLocationChange(CoreSessionInterface session, DocumentModel collectionObjectDocModel) { // // Set a flag in the document model indicating that we want to ignore the update event that // will be triggered by this save/persist request. - ScopedMap contextData = collectionObjectDocModel.getContextData(); - contextData.putIfAbsent(IGNORE_LOCATION_UPDATE_EVENT_OPTION, true); + setDocModelContextProperty(collectionObjectDocModel, IGNORE_LOCATION_UPDATE_EVENT_LABEL, true); // // Save/Persist the document to the DB @@ -283,7 +289,7 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene // // Clear the flag we set to ignore events triggered by our save request. - contextData.remove(IGNORE_LOCATION_UPDATE_EVENT_OPTION); + clearDocModelContextProperty(collectionObjectDocModel, IGNORE_LOCATION_UPDATE_EVENT_LABEL); } /** @@ -409,30 +415,30 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene * un-retrievable via their CSIDs. * * @param session a repository session. - * @param collectionObjectCsid a CollectionObject identifier (CSID) + * @param csid a CollectionObject identifier (CSID) * @return a document model for the document identified by the supplied * CSID. */ - protected static DocumentModel getCurrentDocModelFromCsid(CoreSessionInterface session, String collectionObjectCsid) { + protected static DocumentModel getCurrentDocModelFromCsid(CoreSessionInterface session, String csid) { DocumentModelList docModelList = null; try { final String query = "SELECT * FROM " + NuxeoUtils.BASE_DOCUMENT_TYPE + " WHERE " - + NuxeoUtils.getByNameWhereClause(collectionObjectCsid) + + NuxeoUtils.getByNameWhereClause(csid) + " " + NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT; docModelList = session.query(query); } catch (Exception e) { - logger.warn("Exception in query to get active document model for CollectionObject: ", e); + logger.warn("Exception in query to get active document model for CSID: " + csid, e); } if (docModelList == null || docModelList.isEmpty()) { - logger.warn("Could not get active document models for CollectionObject(s)."); + logger.warn("Could not get active document models for CSID=" + csid); return null; } else if (docModelList.size() != 1) { - logger.error("Found more than 1 active document with CSID=" + collectionObjectCsid); + logger.error("Found more than 1 active document with CSID=" + csid); return null; } @@ -487,7 +493,7 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene * @return the most recent Movement record related to the CollectionObject * identified by the supplied CSID. */ - protected static String getMostRecentMovement(Event event, + protected String getMostRecentLocation(Event event, CoreSessionInterface session, String collectionObjectCsid, boolean isAboutToBeRemovedEvent, String eventMovementCsid) throws ClientException { @@ -761,6 +767,7 @@ public abstract class AbstractUpdateObjectLocationValues implements EventListene * @throws ClientException */ protected abstract boolean updateCollectionObjectLocation(DocumentModel collectionObjectDocModel, + DocumentModel movmentDocModel, String movementRecordsLocation) throws ClientException; } \ No newline at end of file diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/UpdateObjectLocationAndCrateOnMove.java b/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/UpdateObjectLocationAndCrateOnMove.java new file mode 100644 index 000000000..cd593de64 --- /dev/null +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/UpdateObjectLocationAndCrateOnMove.java @@ -0,0 +1,90 @@ +package org.collectionspace.services.listener; + +import java.io.Serializable; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.collectionspace.services.common.api.RefNameUtils; +import org.collectionspace.services.common.api.Tools; +import org.nuxeo.ecm.core.api.ClientException; +import org.nuxeo.ecm.core.api.DocumentModel; + +public class UpdateObjectLocationAndCrateOnMove extends UpdateObjectLocationOnMove { + + // FIXME: We might experiment here with using log4j instead of Apache Commons Logging; + // am using the latter to follow Ray's pattern for now + private final Log logger = LogFactory.getLog(UpdateObjectLocationAndCrateOnMove.class); + // FIXME: Get values below from external constants + private final static String COLLECTIONOBJECTS_ANTHROPOLOGY_SCHEMA = "collectionobjects_anthropology"; + private final static String MOVEMENTS_ANTHROPOLOGY_SCHEMA = "movements_anthropology"; + private final static String CRATE_PROPERTY = "crate"; + private final static String COMPUTED_CRATE_PROPERTY = "computedCrate"; + + @Override + protected boolean updateCollectionObjectLocation(DocumentModel collectionObjectDocModel, + DocumentModel movementDocModel, + String mostRecentLocation) throws ClientException { + boolean flag = super.updateCollectionObjectLocation(collectionObjectDocModel, movementDocModel, mostRecentLocation); + collectionObjectDocModel = updateComputedCrateValue(collectionObjectDocModel, movementDocModel); + + return flag; + } + + private DocumentModel updateComputedCrateValue(DocumentModel collectionObjectDocModel, + DocumentModel movementDocModel) + throws ClientException { + + // Get the current crate value from the Movement (the "new" value) + String crateRefName = + (String) movementDocModel.getProperty(MOVEMENTS_ANTHROPOLOGY_SCHEMA, CRATE_PROPERTY); + + // Check that the value returned, which is expected to be a + // reference (refName) to an authority term: + // + // * If it is not blank ... + // * Is then capable of being successfully parsed by an authority item parser. + if (Tools.notBlank(crateRefName) + && RefNameUtils.parseAuthorityTermInfo(crateRefName) == null) { + logger.warn("Could not parse crate refName '" + crateRefName + "'"); + return collectionObjectDocModel; + } else { + if (logger.isTraceEnabled()) { + logger.trace("crate refName passes basic validation tests."); + logger.trace("crate refName=" + crateRefName); + } + } + // Get the computed crate value of the CollectionObject + // (the "existing" value) + String existingCrateRefName = + (String) collectionObjectDocModel.getProperty(COLLECTIONOBJECTS_ANTHROPOLOGY_SCHEMA, + COMPUTED_CRATE_PROPERTY); + if (logger.isTraceEnabled()) { + logger.trace("Existing crate refName=" + existingCrateRefName); + } + + // If the new value is blank, any non-blank existing value should always + // be overwritten ('nulled out') with a blank value. + if (Tools.isBlank(crateRefName) && Tools.notBlank(existingCrateRefName)) { + collectionObjectDocModel.setProperty(COLLECTIONOBJECTS_ANTHROPOLOGY_SCHEMA, + COMPUTED_CRATE_PROPERTY, (Serializable) null); + // Otherwise, if the new value is not blank, and + // * the existing value is blank, or + // * the new value is different than the existing value ... + } else if (Tools.notBlank(crateRefName) && + (Tools.isBlank(existingCrateRefName) + || !crateRefName.equals(existingCrateRefName))) { + if (logger.isTraceEnabled()) { + logger.trace("crate refName requires updating."); + } + // ... update the existing value in the CollectionObject with the + // new value from the Movement. + collectionObjectDocModel.setProperty(COLLECTIONOBJECTS_ANTHROPOLOGY_SCHEMA, + COMPUTED_CRATE_PROPERTY, crateRefName); + } else { + if (logger.isTraceEnabled()) { + logger.trace("crate refName does NOT require updating."); + } + } + + return collectionObjectDocModel; + } +} \ No newline at end of file diff --git a/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/UpdateObjectLocationOnMove.java b/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/UpdateObjectLocationOnMove.java index 93b98b204..819197a65 100644 --- a/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/UpdateObjectLocationOnMove.java +++ b/3rdparty/nuxeo/nuxeo-platform-listener/updateobjectlocationonmove/src/main/java/org/collectionspace/services/listener/UpdateObjectLocationOnMove.java @@ -4,7 +4,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.collectionspace.services.common.api.RefNameUtils; import org.collectionspace.services.common.api.Tools; -import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; @@ -16,6 +15,7 @@ public class UpdateObjectLocationOnMove extends AbstractUpdateObjectLocationValu @Override protected boolean updateCollectionObjectLocation(DocumentModel collectionObjectDocModel, + DocumentModel movementDocModel, String movementRecordsLocation) throws ClientException { boolean result = false; diff --git a/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml b/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml index b373c5d85..c57065a4f 100644 --- a/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml +++ b/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml @@ -2,6 +2,42 @@ + + + org.collectionspace.services.listener.UpdateObjectLocationOnMove + + + key0 + value0 + + + key1 + value1 + + + key2 + value2 + + + + + org.collectionspace.services.listener.UpdateObjectLocationAndCrateOnMove + + + crate-key0 + value0 + + + crate-key1 + value1 + + + crate-key2 + value2 + + + + - diff --git a/services/common/src/main/cspace/config/services/tenants/testsci/testsci-tenant-bindings.delta.xml b/services/common/src/main/cspace/config/services/tenants/testsci/testsci-tenant-bindings.delta.xml index 87f2760dc..2892ae366 100644 --- a/services/common/src/main/cspace/config/services/tenants/testsci/testsci-tenant-bindings.delta.xml +++ b/services/common/src/main/cspace/config/services/tenants/testsci/testsci-tenant-bindings.delta.xml @@ -8,6 +8,25 @@ + + + + + testsci-key0 + value0 + + + testsci-key1 + value1 + + + testsci-key2 + value2 + + + + + + - diff --git a/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java b/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java index 9821be5f7..b15ba97ed 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java +++ b/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java @@ -16,6 +16,7 @@ import javax.naming.NamingException; import javax.servlet.ServletContext; import javax.sql.DataSource; +import org.apache.commons.io.FileUtils; import org.apache.tomcat.dbcp.dbcp.BasicDataSource; import org.collectionspace.authentication.AuthN; import org.collectionspace.services.client.XmlTools; @@ -36,14 +37,16 @@ import org.collectionspace.services.common.storage.JDBCTools; import org.collectionspace.services.config.ClientType; import org.collectionspace.services.config.ServiceConfig; import org.collectionspace.services.config.service.ServiceBindingType; +import org.collectionspace.services.config.tenant.EventListenerConfig; +import org.collectionspace.services.config.tenant.EventListenerConfigurations; import org.collectionspace.services.config.tenant.RepositoryDomainType; import org.collectionspace.services.config.tenant.TenantBindingType; 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.collectionspace.services.nuxeo.listener.CSEventListener; +import org.collectionspace.services.nuxeo.listener.AbstractCSEventListenerImpl; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.dom4j.Document; import org.slf4j.Logger; @@ -57,7 +60,8 @@ import org.slf4j.LoggerFactory; public class ServiceMain { final static Logger logger = LoggerFactory.getLogger(ServiceMain.class); - + private static final int PRIMARY_REPOSITORY_DOMAIN = 0; + /** * 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. @@ -186,7 +190,7 @@ public class ServiceMain { String[] dataSourceNames = {JDBCTools.NUXEO_DATASOURCE_NAME, JDBCTools.NUXEO_READER_DATASOURCE_NAME}; updateInitializationScript(getNuxeoDatabasesInitScriptFilename(), dbsCheckedOrCreated, dataSourceNames); - + // // Start up and initialize our embedded Nuxeo instance. // @@ -204,6 +208,11 @@ public class ServiceMain { // throw new RuntimeException("Unknown CollectionSpace services client type: " + getClientType()); } + + // + // + // + initializeEventListeners(); // // Create all the default user accounts and permissions. Since some of our "cspace" database config files @@ -233,8 +242,93 @@ public class ServiceMain { */ showTenantStatus(); } + + /** + * Returns the primary repository name for a tenant -there's usually just one. + * @param tenantBinding + * @return + * @throws InstantiationException + */ - private void showTenantStatus() { + protected String getPrimaryRepositoryName(TenantBindingType tenantBinding) throws InstantiationException { + String result = "default"; + + List repositoryDomainList = tenantBinding.getRepositoryDomain(); + if (repositoryDomainList != null && repositoryDomainList.isEmpty() == false) { + String repositoryName = repositoryDomainList.get(PRIMARY_REPOSITORY_DOMAIN).getRepositoryName(); + if (repositoryName != null && !repositoryName.isEmpty()) { + result = repositoryName; + } + } else { + String msg = String.format("Tenant bindings for '%s' is missing a repositoryDomain element in its bindings file.", + tenantBinding.getName()); + logger.error(msg); + throw new InstantiationException(msg); + } + + return result; + } + + /** + * Initialize the event listeners. We're essentially registering listeners with tenants. This ensures that listeners ignore events + * caused by other tenants. + */ + private void initializeEventListeners() { + Hashtable tenantBindings = this.tenantBindingConfigReader.getTenantBindings(); + + for (TenantBindingType tenantBinding : tenantBindings.values()) { + EventListenerConfigurations eventListenerConfigurations = tenantBinding.getEventListenerConfigurations(); + if (eventListenerConfigurations != null) { + List eventListenerConfigList = eventListenerConfigurations.getEventListenerConfig(); + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + for (EventListenerConfig eventListenerConfig : eventListenerConfigList) { + String clazz = eventListenerConfig.getClassName().trim(); + if (clazz.isEmpty() == false) { + try { + Class c = tccl.loadClass(clazz); + if (CSEventListener.class.isAssignableFrom(c)) { + CSEventListener listener = (AbstractCSEventListenerImpl) c.newInstance(); + listener.register(getPrimaryRepositoryName(tenantBinding), eventListenerConfig); // Register the listener with a tenant using its repository name + if (logger.isInfoEnabled()) { + String msg = String.format("Event Listener - Success: Tenant '%30s'\tActivated listener %s:%s", + tenantBinding.getName(), eventListenerConfig.getId(), clazz); + logger.info(msg); + } + } + } catch (ClassNotFoundException e) { + String msg = String.format("Event Listener - FAILURE: Tenant '%30s'\tFailed to find event listener %s:%s", + tenantBinding.getName(), eventListenerConfig.getId(), clazz); + logger.warn(msg); + logger.trace(msg, e); + failIfRequired(eventListenerConfig); + } catch (InstantiationException e) { + String msg = String.format("Event Listener - FAILURE: Tenant '%30s'\tFailed to instantiate event listener %s:%s", + tenantBinding.getName(), eventListenerConfig.getId(), clazz); + logger.warn(msg); + logger.trace(msg, e); + failIfRequired(eventListenerConfig); + } catch (IllegalAccessException e) { + String msg = String.format("Event Listener - FAILURE: Tenant '%30s'\tIllegal access to event listener %s:%s", + tenantBinding.getName(), eventListenerConfig.getId(), clazz); + logger.warn(msg); + logger.trace(msg, e); + failIfRequired(eventListenerConfig); + } + } + } + } + logger.info("\n"); + } + } + + private void failIfRequired(EventListenerConfig eventListenerConfig) { + if (eventListenerConfig.isRequired() == true) { + throw new RuntimeException(String.format("Required event listener '%s' missing or could not be instantiated.", eventListenerConfig.getId())); + } + + } + + private void showTenantStatus() { Hashtable tenantBindingsList = tenantBindingConfigReader.getTenantBindings(true); mirrorToStdOut("++++++++++++++++ Summary - CollectionSpace tenant status. ++++++++++++++++++++++++"); String headerTemplate = "%10s %10s %30s %60s %10s"; diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/listener/AbstractCSEventListenerImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/listener/AbstractCSEventListenerImpl.java new file mode 100644 index 000000000..ed6579b77 --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/listener/AbstractCSEventListenerImpl.java @@ -0,0 +1,130 @@ +package org.collectionspace.services.nuxeo.listener; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.collectionspace.services.common.api.Tools; +import org.collectionspace.services.config.tenant.EventListenerConfig; +import org.collectionspace.services.config.tenant.Param; +import org.nuxeo.common.collections.ScopeType; +import org.nuxeo.common.collections.ScopedMap; +import org.nuxeo.ecm.core.api.DocumentModel; +import org.nuxeo.ecm.core.event.Event; + +public abstract class AbstractCSEventListenerImpl implements CSEventListener { + private static List repositoryNameList = new ArrayList(); + private static Map>> eventListenerParamsMap = new HashMap>>(); // >> + private static Map nameMap = new HashMap(); + + static final String DOCMODEL_CONTEXT_PROPERTY_PREFIX = ScopeType.DEFAULT.getScopePrefix(); + + public AbstractCSEventListenerImpl() { + // Intentionally left blank + } + + protected List getRepositoryNameList() { + return repositoryNameList; + } + + protected Map>> getEventListenerParamsMap() { + return eventListenerParamsMap; + } + + @Override + public boolean isRegistered(Event event) { + boolean result = false; + + if (event != null && event.getContext() != null) { + result = repositoryNameList.contains(event.getContext().getRepositoryName()); + } + + return result; + } + + + /** + * Returns 'true' if this collection changed as a result of the call. + */ + @Override + public boolean register(String respositoryName, EventListenerConfig eventListenerConfig) { + boolean result = false; + + // Using the repositoryName as a qualifier, register this event listener's name as specified in the tenant bindings. + setName(respositoryName, eventListenerConfig.getId()); + + // Register this event listener with the given repository name + if (getRepositoryNameList().add(respositoryName)) { + result = true; + } + + // Set this event listeners parameters, if any. Params are qualified with the repositoryName since multiple tenants might be registering the same event listener but with different params. + List paramList = eventListenerConfig.getParamList().getParam(); // values from the tenant bindings that we need to copy into the event listener + if (paramList != null) { + // + // Get the list of event listeners for a given repository + Map> eventListenerRepoParams = getEventListenerParamsMap().get(respositoryName); // Get the set of event listers for a given repository + if (eventListenerRepoParams == null) { + eventListenerRepoParams = new HashMap>(); + getEventListenerParamsMap().put(respositoryName, eventListenerRepoParams); // create and put an empty map + result = true; + } + // + // Get the list of params for a given event listener for a given repository + Map eventListenerParams = eventListenerRepoParams.get(eventListenerConfig.getId()); // Get the set of params for a given event listener for a given repository + if (eventListenerParams == null) { + eventListenerParams = new HashMap(); + eventListenerRepoParams.put(eventListenerConfig.getId(), eventListenerParams); // create and put an empty map + result = true; + } + // + // copy all the values from the tenant bindings into the event listener + for (Param params : paramList) { + String key = params.getKey(); + String value = params.getValue(); + if (Tools.notBlank(key)) { + eventListenerParams.put(key, value); + result = true; + } + } + } + + return result; + } + + protected void setName(String repositoryName, String eventListenerName) { + nameMap.put(repositoryName, eventListenerName); + } + + @Override + public Map getParams(Event event) { + String repositoryName = event.getContext().getRepositoryName(); + return getEventListenerParamsMap().get(repositoryName).get(getName(repositoryName)); // We need to qualify with the repositoryName since this event listener might be register by multiple tenants using different params + } + + @Override + public String getName(String repositoryName) { + return nameMap.get(repositoryName); + } + + // + // Set a property in the document model's transient context. + // + @Override + public void setDocModelContextProperty(DocumentModel collectionObjectDocModel, String key, Serializable value) { + ScopedMap contextData = collectionObjectDocModel.getContextData(); + contextData.putIfAbsent(DOCMODEL_CONTEXT_PROPERTY_PREFIX + key, value); + } + + // + // Clear a property from the docModel's context + // + @Override + public void clearDocModelContextProperty(DocumentModel docModel, String key) { + ScopedMap contextData = docModel.getContextData(); + contextData.remove(DOCMODEL_CONTEXT_PROPERTY_PREFIX + key); + } + +} diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/listener/CSEventListener.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/listener/CSEventListener.java new file mode 100644 index 000000000..22b28680d --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/listener/CSEventListener.java @@ -0,0 +1,55 @@ +package org.collectionspace.services.nuxeo.listener; + +import java.io.Serializable; +import java.util.Map; + +import org.collectionspace.services.config.tenant.EventListenerConfig; +import org.nuxeo.ecm.core.api.DocumentModel; +import org.nuxeo.ecm.core.event.Event; +import org.nuxeo.ecm.core.event.EventListener; + +public interface CSEventListener extends EventListener { + /** + * Register ourself as an event listener for the named repository -the repo name corresponds to a specific tenant. + * @param respositoryName - The name of the Nuxeo repository which links us to a CollectionSpace tenant. + * @param eventListenerConfig - Tenant bindings config for our listener. + * @return + */ + boolean register(String respositoryName, EventListenerConfig eventListenerConfig); + + /** + * Determines if we are a registered event listener for the given event. + * @param event + * @return + */ + boolean isRegistered(Event event); + + /** + * Returns event listener related params that we're supplied in the tenant bindings. + * @param event + * @return + */ + Map getParams(Event event); + + /** + * Set's a property in a DocumetModel's transient data context. + * + * @param docModel + * @param key + * @param value + */ + void setDocModelContextProperty(DocumentModel docModel, String key, Serializable value); + + /** + * Clears a property from a DocumentModel's transient data context. + * @param docModel + * @param key + */ + void clearDocModelContextProperty(DocumentModel docModel, String key); + + /** + * Returns the name of the event listener as defined during registration -see register() method. + * @return + */ + String getName(String repositoryName); +} diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/util/CSEventServiceComponent.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/util/CSEventServiceComponent.java new file mode 100644 index 000000000..c3987872a --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/util/CSEventServiceComponent.java @@ -0,0 +1,14 @@ +package org.collectionspace.services.nuxeo.util; + +import org.nuxeo.ecm.core.event.EventServiceComponent; +//import org.nuxeo.ecm.core.event.impl.EventServiceImpl; +import org.nuxeo.runtime.model.ComponentContext; + +public class CSEventServiceComponent extends EventServiceComponent { + + @Override + public void activate(ComponentContext context) { + service = new CSEventServiceImpl(); + } + +} diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/util/CSEventServiceImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/util/CSEventServiceImpl.java new file mode 100644 index 000000000..994dd7d73 --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/util/CSEventServiceImpl.java @@ -0,0 +1,18 @@ +package org.collectionspace.services.nuxeo.util; + +import java.io.Serializable; +import java.util.Map; + +import org.nuxeo.ecm.core.event.Event; +import org.nuxeo.ecm.core.event.impl.EventServiceImpl; + +public class CSEventServiceImpl extends EventServiceImpl { + + @Override + public void fireEvent(Event event) { + String repoName = event.getContext().getRepositoryName(); + Map eventProps = event.getContext().getProperties(); + super.fireEvent(event); + } + +} diff --git a/services/config/src/main/resources/tenant.xsd b/services/config/src/main/resources/tenant.xsd index f9bf0d467..177dbaed4 100644 --- a/services/config/src/main/resources/tenant.xsd +++ b/services/config/src/main/resources/tenant.xsd @@ -45,7 +45,8 @@ Tenant bindings - + + @@ -78,6 +79,7 @@ + @@ -95,5 +97,39 @@ + + + + + + + + + + Connection details for a remote CollectionSpace instance. Used for things like the Share Authority Server + + + + + + + + + + + + + + + + + + + + + + + +