]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
f52eb347fc9dd85696a15360b33947b7677ba64e
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.nuxeo.listener;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8
9 import org.slf4j.Logger;
10
11 import org.collectionspace.services.client.workflow.WorkflowClient;
12 import org.collectionspace.services.common.api.Tools;
13 import org.collectionspace.services.config.tenant.EventListenerConfig;
14 import org.collectionspace.services.config.tenant.Param;
15 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
16 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
17 import org.nuxeo.common.collections.ScopeType;
18 import org.nuxeo.common.collections.ScopedMap;
19 import org.nuxeo.ecm.core.api.DocumentModel;
20 import org.nuxeo.ecm.core.api.DocumentModelList;
21 import org.nuxeo.ecm.core.event.Event;
22 import org.nuxeo.ecm.core.event.EventContext;
23 import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
24
25 public abstract class AbstractCSEventListenerImpl implements CSEventListener {
26         private static Map<String, List<String>> mapOfrepositoryNames = new HashMap<String, List<String>>(); // <className, repositoryName>
27         private static Map<String, Map<String, Map<String, String>>> eventListenerParamsMap = new HashMap<String, Map<String, Map<String, String>>>();  // <repositoryName, Map<EventListenerId, Map<key, value>>>
28         private static Map<String, String> nameMap = new HashMap<String, String>();
29
30     // SQL clauses
31     private final static String NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT =
32             "AND ecm:isCheckedInVersion = 0"
33             + " AND ecm:isProxy = 0 ";
34     protected final static String ACTIVE_DOCUMENT_WHERE_CLAUSE_FRAGMENT =
35             "AND (ecm:currentLifeCycleState <> 'deleted') "
36             + NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT;
37     static final String DOCMODEL_CONTEXT_PROPERTY_PREFIX = ScopeType.DEFAULT.getScopePrefix();
38
39         public AbstractCSEventListenerImpl() {
40                 // Intentionally left blank
41         }
42
43         /**
44          * Find out if we (the event listener) are registered (via tenant bindings config) to respond events.
45          */
46         @Override
47         public boolean isRegistered(Event event) {
48                 boolean result = false;
49
50                 if (event != null && event.getContext() != null) {
51                         result = getRepositoryNameList().contains(event.getContext().getRepositoryName());
52                 }
53
54                 return result;
55         }
56
57         /*
58          * This method is meant to be the bottleneck for handling a Nuxeo document event.
59          */
60         final public void handleEvent(Event event) {
61                 getLogger().trace(String.format("Eventlistener '%s' presenented with '%s' event.",
62                                 getClass().getName(), event.getName()));
63                 boolean isRegistered = isRegistered(event);
64
65                 try {
66                         if (isRegistered && shouldHandleEvent(event)) {
67                                 handleCSEvent(event);
68                                 getLogger().debug(String.format("Eventlistener '%s' accepted '%s' event.",
69                                                 getClass().getName(), event.getName()));
70                         } else {
71                                 if (isRegistered) {
72                                         getLogger().debug(String.format("Eventlistener '%s' declined to handle '%s' event.",
73                                                         getClass().getName(), event.getName()));
74                                 } else {
75                                         getLogger().trace(String.format("Eventlistener '%s' was not registered in the service bindings for the tenant with repo '%s'.",
76                                                         getClass().getName(), event.getContext().getRepositoryName()));
77                                 }
78                         }
79                 } catch (Exception e) {
80                         String errMsg = String.format("Eventlistener '%s' presenented with '%s' event but encountered an error: %s",
81                                         getClass().getName(), event.getName(), e.getMessage());
82                         if (getLogger().isTraceEnabled()) {
83                                 getLogger().error(errMsg, e);
84                         } else {
85                                 getLogger().error(errMsg);
86                         }
87                 }
88         }
89
90         /**
91          * An event listener can be registered by multiple tenants, so we keep track of that here.
92          *
93          * @return - the list of tenants/repositories that an event listener is registered with.
94          */
95         protected List<String> getRepositoryNameList() {
96                 String key = this.getClass().getName();
97                 List<String> result = mapOfrepositoryNames.get(key);
98
99                 if (result == null) synchronized(this) {
100                         result = new ArrayList<String>();
101                         mapOfrepositoryNames.put(key, result);
102                 }
103
104                 return result;
105         }
106
107         /**
108          * The list of parameters (specified in a tenant's bindings) for event listeners
109          * @return
110          */
111         protected Map<String, Map<String, Map<String, String>>> getEventListenerParamsMap() {
112                 return eventListenerParamsMap;
113         }
114
115         /**
116          * Returns 'true' if this collection changed as a result of the call.
117          */
118         @Override
119         public boolean register(String respositoryName, EventListenerConfig eventListenerConfig) {
120                 boolean result = false;
121
122                 // Using the repositoryName as a qualifier, register this event listener's name as specified in the tenant bindings.
123                 setName(respositoryName, eventListenerConfig.getId());
124
125                 // Register this event listener with the given repository name
126                 if (getRepositoryNameList().add(respositoryName)) {
127                         result = true;
128                 }
129
130                 if (eventListenerConfig.getParamList() != null) {
131                         // 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.
132                         List<Param> paramList = eventListenerConfig.getParamList().getParam(); // values from the tenant bindings that we need to copy into the event listener
133                         if (paramList != null) {
134                                 //
135                                 // Get the list of event listeners for a given repository
136                                 Map<String, Map<String, String>> eventListenerRepoParams = getEventListenerParamsMap().get(respositoryName); // Get the set of event listers for a given repository
137                                 if (eventListenerRepoParams == null) {
138                                         eventListenerRepoParams = new HashMap<String, Map<String, String>>();
139                                         getEventListenerParamsMap().put(respositoryName, eventListenerRepoParams); // create and put an empty map
140                                         result = true;
141                                 }
142                                 //
143                                 // Get the list of params for a given event listener for a given repository
144                                 Map<String, String> eventListenerParams = eventListenerRepoParams.get(eventListenerConfig.getId()); // Get the set of params for a given event listener for a given repository
145                                 if (eventListenerParams == null) {
146                                         eventListenerParams = new HashMap<String, String>();
147                                         eventListenerRepoParams.put(eventListenerConfig.getId(), eventListenerParams); // create and put an empty map
148                                         result = true;
149                                 }
150                                 //
151                                 // copy all the values from the tenant bindings into the event listener
152                                 for (Param params : paramList) {
153                                         String key = params.getKey();
154                                         String value = params.getValue();
155                                         if (Tools.notBlank(key)) {
156                                                 eventListenerParams.put(key, value);
157                                                 result = true;
158                                         }
159                                 }
160                         }
161                 }
162
163                 return result;
164         }
165
166         protected void setName(String repositoryName, String eventListenerName) {
167                 nameMap.put(repositoryName, eventListenerName);
168         }
169
170         @Override
171         public Map<String, String> getParams(Event event) {
172                 Map<String, String> result = null;
173                 try {
174                         String repositoryName = event.getContext().getRepositoryName();
175                         result = 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
176                 } catch (NullPointerException e) {
177                         // Do nothing.  Just means no params were configured.
178                 }
179                 return result;
180         }
181
182         @Override
183         public String getName(String repositoryName) {
184                 return nameMap.get(repositoryName);
185         }
186
187         //
188         // Return a property in the document model's transient context.
189         //
190         protected Serializable getContextPropertyValue(DocumentEventContext docEventContext, String key) {
191                 return docEventContext.getProperties().get(DOCMODEL_CONTEXT_PROPERTY_PREFIX + key);
192         }
193
194         //
195         // Set a property in the document model's transient context.
196         //
197         @Override
198     public void setDocModelContextProperty(DocumentModel collectionObjectDocModel, String key, Serializable value) {
199         ScopedMap contextData = collectionObjectDocModel.getContextData();
200         contextData.putIfAbsent(DOCMODEL_CONTEXT_PROPERTY_PREFIX + key, value);
201     }
202
203     //
204     // Clear a property from the docModel's context
205         //
206         @Override
207         public void clearDocModelContextProperty(DocumentModel docModel, String key) {
208         ScopedMap contextData = docModel.getContextData();
209         contextData.remove(DOCMODEL_CONTEXT_PROPERTY_PREFIX + key);
210         }
211
212         //
213         // Derived classes need to implement.
214         //
215         abstract protected Logger getLogger();
216
217         /**
218          * Identifies whether a supplied event concerns a document that has
219          * been transitioned to the 'deleted' workflow state.
220          *
221          * @param eventContext an event context
222          *
223          * @return true if this event concerns a document that has
224          * been transitioned to the 'deleted' workflow state.
225          */
226         protected boolean isDocumentSoftDeletedEvent(EventContext eventContext) {
227                 boolean isSoftDeletedEvent = false;
228
229                 if (eventContext instanceof DocumentEventContext) {
230                         Map<String, Serializable> properties = eventContext.getProperties();
231
232                         if (properties.containsKey(WorkflowClient.WORKFLOWTRANSITION_TO)) {
233                                 String transitionTo = (String) properties.get(WorkflowClient.WORKFLOWTRANSITION_TO);
234
235                                 if (
236                                         transitionTo.equals(WorkflowClient.WORKFLOWSTATE_DELETED)
237                                         || transitionTo.equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED)
238                                         || transitionTo.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED_DELETED)
239                                         || transitionTo.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)
240                                         || transitionTo.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)
241                                 ) {
242                                         isSoftDeletedEvent = true;
243                                 }
244                         }
245                 }
246
247                 return isSoftDeletedEvent;
248         }
249
250          /**
251           * Identifies whether a document matches a supplied document type.
252           *
253           * @param docModel a document model.
254           * @param docType a document type string.
255           * @return true if the document matches the supplied document type; false if
256           * it does not.
257           */
258      protected static boolean documentMatchesType(DocumentModel docModel, String docType) {
259          if (docModel == null || Tools.isBlank(docType)) {
260              return false;
261          }
262          if (docModel.getType().startsWith(docType)) {
263              return true;
264          } else {
265              return false;
266          }
267      }
268
269      /**
270       * Identifies whether a document is an active document; currently, whether
271       * it is not in a 'deleted' workflow state.
272       *
273       * @param docModel
274       * @return true if the document is an active document; false if it is not.
275       */
276      protected static boolean isActiveDocument(DocumentModel docModel) {
277         return isActiveDocument(docModel, false, null);
278      }
279
280      protected static boolean isActiveDocument(DocumentModel docModel, boolean isAboutToBeRemovedEvent, String aboutToBeRemovedCsid) {
281          boolean isActiveDocument = false;
282
283          if (docModel != null) {
284              if (!docModel.getCurrentLifeCycleState().contains(WorkflowClient.WORKFLOWSTATE_DELETED)) {
285                  isActiveDocument = true;
286              }
287                 //
288                 // If doc model is the target of the "aboutToBeRemoved" event, mark it as not active.
289                 //
290                 if (isAboutToBeRemovedEvent && Tools.notBlank(aboutToBeRemovedCsid)) {
291                         if (NuxeoUtils.getCsid(docModel).equalsIgnoreCase(aboutToBeRemovedCsid)) {
292                                 isActiveDocument = false;
293                         }
294                 }
295          }
296
297          return isActiveDocument;
298      }
299
300      /**
301       * Returns the current document model for a record identified by a CSID.
302       *
303       * Excludes documents which have been versioned (i.e. are a non-current
304       * version of a document), are a proxy for another document, or are
305       * un-retrievable via their CSIDs.
306       *
307       * @param session a repository session.
308       * @param csid a CollectionObject identifier (CSID)
309       * @return a document model for the document identified by the supplied
310       * CSID.
311       */
312      protected DocumentModel getCurrentDocModelFromCsid(CoreSessionInterface session, String csid) {
313          DocumentModelList docModelList = null;
314
315          if (Tools.isEmpty(csid)) {
316                 return null;
317          }
318
319          try {
320              final String query = "SELECT * FROM "
321                      + NuxeoUtils.BASE_DOCUMENT_TYPE
322                      + " WHERE "
323                      + NuxeoUtils.getByNameWhereClause(csid)
324                      + " "
325                      + NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT;
326              docModelList = session.query(query);
327          } catch (Exception e) {
328              getLogger().warn("Exception in query to get active document model for CSID: " + csid, e);
329          }
330
331          if (docModelList == null || docModelList.isEmpty()) {
332                  getLogger().warn("Could not get active document models for CSID=" + csid);
333              return null;
334          } else if (docModelList.size() != 1) {
335                  getLogger().error("Found more than 1 active document with CSID=" + csid);
336              return null;
337          }
338
339          return docModelList.get(0);
340      }
341 }