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