]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
092cdd64065501d74142ecd036b134a60260706a
[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         //FIXME: Does not include all the sync-related "delete" workflow states
218         /**
219      * Identifies whether a supplied event concerns a document that has
220      * been transitioned to the 'deleted' workflow state.
221      * 
222      * @param eventContext an event context
223      * 
224      * @return true if this event concerns a document that has
225      * been transitioned to the 'deleted' workflow state.
226      */
227     protected boolean isDocumentSoftDeletedEvent(EventContext eventContext) {
228         boolean isSoftDeletedEvent = false;
229         
230         if (eventContext instanceof DocumentEventContext) {
231             if (eventContext.getProperties().containsKey(WorkflowClient.WORKFLOWTRANSITION_TO)
232                     &&
233                 (eventContext.getProperties().get(WorkflowClient.WORKFLOWTRANSITION_TO).equals(WorkflowClient.WORKFLOWSTATE_DELETED)
234                                 ||
235                 eventContext.getProperties().get(WorkflowClient.WORKFLOWTRANSITION_TO).equals(WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED))) {
236                 isSoftDeletedEvent = true;
237             }
238         }
239         
240         return isSoftDeletedEvent;
241     }
242     
243          /**
244           * Identifies whether a document matches a supplied document type.
245           *
246           * @param docModel a document model.
247           * @param docType a document type string.
248           * @return true if the document matches the supplied document type; false if
249           * it does not.
250           */
251      protected static boolean documentMatchesType(DocumentModel docModel, String docType) {
252          if (docModel == null || Tools.isBlank(docType)) {
253              return false;
254          }
255          if (docModel.getType().startsWith(docType)) {
256              return true;
257          } else {
258              return false;
259          }
260      }
261      
262      /**
263       * Identifies whether a document is an active document; currently, whether
264       * it is not in a 'deleted' workflow state.
265       *
266       * @param docModel
267       * @return true if the document is an active document; false if it is not.
268       */
269      protected static boolean isActiveDocument(DocumentModel docModel) {
270         return isActiveDocument(docModel, false, null);
271      }
272      
273      protected static boolean isActiveDocument(DocumentModel docModel, boolean isAboutToBeRemovedEvent, String aboutToBeRemovedCsid) {
274          boolean isActiveDocument = false;
275
276          if (docModel != null) {                
277              if (!docModel.getCurrentLifeCycleState().contains(WorkflowClient.WORKFLOWSTATE_DELETED)) {
278                  isActiveDocument = true;
279              }
280                 //
281                 // If doc model is the target of the "aboutToBeRemoved" event, mark it as not active.
282                 //
283                 if (isAboutToBeRemovedEvent && Tools.notBlank(aboutToBeRemovedCsid)) {
284                         if (NuxeoUtils.getCsid(docModel).equalsIgnoreCase(aboutToBeRemovedCsid)) {
285                                 isActiveDocument = false;
286                         }
287                 }
288          }
289          
290          return isActiveDocument;
291      }
292
293      /**
294       * Returns the current document model for a record identified by a CSID.
295       *
296       * Excludes documents which have been versioned (i.e. are a non-current
297       * version of a document), are a proxy for another document, or are
298       * un-retrievable via their CSIDs.
299       *
300       * @param session a repository session.
301       * @param csid a CollectionObject identifier (CSID)
302       * @return a document model for the document identified by the supplied
303       * CSID.
304       */
305      protected DocumentModel getCurrentDocModelFromCsid(CoreSessionInterface session, String csid) {
306          DocumentModelList docModelList = null;
307          
308          if (Tools.isEmpty(csid)) {
309                 return null;
310          }
311          
312          try {
313              final String query = "SELECT * FROM "
314                      + NuxeoUtils.BASE_DOCUMENT_TYPE
315                      + " WHERE "
316                      + NuxeoUtils.getByNameWhereClause(csid)
317                      + " "
318                      + NONVERSIONED_NONPROXY_DOCUMENT_WHERE_CLAUSE_FRAGMENT;
319              docModelList = session.query(query);
320          } catch (Exception e) {
321              getLogger().warn("Exception in query to get active document model for CSID: " + csid, e);
322          }
323          
324          if (docModelList == null || docModelList.isEmpty()) {
325                  getLogger().warn("Could not get active document models for CSID=" + csid);
326              return null;
327          } else if (docModelList.size() != 1) {
328                  getLogger().error("Found more than 1 active document with CSID=" + csid);
329              return null;
330          }
331
332          return docModelList.get(0);
333      }
334 }