View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.ken.deliverer.impl;
17  
18  import java.io.ByteArrayOutputStream;
19  import java.io.IOException;
20  import java.util.HashMap;
21  import java.util.LinkedHashMap;
22  import java.util.Properties;
23  
24  import org.kuali.rice.ken.bo.NotificationMessageDelivery;
25  import org.kuali.rice.ken.core.GlobalNotificationServiceLocator;
26  import org.kuali.rice.ken.deliverer.NotificationMessageDeliverer;
27  import org.kuali.rice.ken.document.kew.NotificationWorkflowDocument;
28  import org.kuali.rice.ken.exception.ErrorList;
29  import org.kuali.rice.ken.exception.NotificationAutoRemoveException;
30  import org.kuali.rice.ken.exception.NotificationMessageDeliveryException;
31  import org.kuali.rice.ken.service.NotificationWorkflowDocumentService;
32  import org.kuali.rice.ken.util.NotificationConstants;
33  import org.kuali.rice.ken.util.Util;
34  import org.kuali.rice.kew.exception.WorkflowException;
35  import org.kuali.rice.kew.service.WorkflowDocument;
36  
37  
38  /**
39   * This class is responsible for describing the default delivery mechanism for
40   * the system - the KEW Action List.
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42   */
43  public class KEWActionListMessageDeliverer implements NotificationMessageDeliverer {
44      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KEWActionListMessageDeliverer.class);
45      
46      /**
47       * Property set in the attribute content that indicates the action received by workflow was initiated by the Notification System itself
48       * (and not an end user)
49       */
50      public static final String INTERNAL_COMMAND_FLAG = "internal_command";
51  
52      private NotificationWorkflowDocumentService notificationWorkflowDocumentService;
53  
54      /**
55       * Constructs a KEWActionListMessageDeliverer.java.
56       */
57      public KEWActionListMessageDeliverer() {
58          this.notificationWorkflowDocumentService = GlobalNotificationServiceLocator.getInstance().getNotificationWorkflowDocumentService();
59      }
60  
61      /**
62       * This implementation leverages the workflow integration services to push this notification into the KEW action list.
63       * @see org.kuali.rice.ken.deliverer.NotificationMessageDeliverer#deliverMessage(org.kuali.rice.ken.bo.NotificationMessageDelivery)
64       */
65      public void deliverMessage(NotificationMessageDelivery messageDelivery) throws NotificationMessageDeliveryException {
66          try {
67              // make the call to actually generate and ad-hoc route a workflow document
68              Long workflowDocId = notificationWorkflowDocumentService.createAndAdHocRouteNotificationWorkflowDocument(
69                      messageDelivery,
70                      Util.getNotificationSystemUser(),
71                      messageDelivery.getUserRecipientId(),
72                      NotificationConstants.KEW_CONSTANTS.GENERIC_DELIVERY_ANNOTATION);
73  
74              // now prepare and set the workflow doc id into the message delivery's delivery system id
75              String deliverySystemId = null;
76              if(workflowDocId != null) {
77                  deliverySystemId = workflowDocId.toString();
78              }
79              messageDelivery.setDeliverySystemId(deliverySystemId);
80              LOG.debug("Message Delivery: " + messageDelivery.toString());
81          } catch (WorkflowException we) {
82              LOG.error(we.getStackTrace());
83              throw new NotificationMessageDeliveryException("Workflow exception delivering message", we);
84          }
85      }
86  
87      /**
88       * This implementation does an auto-remove by "canceling" the workflow document associated with the message delivery record.  This 
89       * prevents the user from seeing the item in their list anymore.
90       * @see org.kuali.rice.ken.deliverer.NotificationMessageDeliverer#autoRemoveMessageDelivery(org.kuali.rice.ken.bo.NotificationMessageDelivery)
91       */
92      public void autoRemoveMessageDelivery(NotificationMessageDelivery messageDelivery) throws NotificationAutoRemoveException {
93          // first retrieve the appropriate notification workflow document to "auto-remove" and proxy as the recipient
94          NotificationWorkflowDocument workflowDoc = null;
95          String sysId = messageDelivery.getDeliverySystemId();
96          if (sysId == null) {
97              LOG.error("NotificationMessageDelivery " + messageDelivery.getId() + " is missing delivery system id (workflow document id");
98              // there is no possibility for recovery, so since there is no id, we'll just log an error and return successfully instead
99              // of throwing an exception
100             return;
101         }
102         
103         Long docId;
104         try {
105             docId = Long.parseLong(sysId);
106         } catch (NumberFormatException nfe) {
107             LOG.error("Invalid workflow document id for NotificationMessageDelivery " + messageDelivery.getId() + ": " + sysId);
108             // there is no possibility for recovery, so since there is no id, we'll just log an error and return successfully instead
109             // of throwing an exception
110             return;
111         }
112 
113         try {
114             workflowDoc = notificationWorkflowDocumentService.getNotificationWorkflowDocumentByDocumentId(messageDelivery.getUserRecipientId(), docId);
115         } catch(WorkflowException we) {
116             throw new NotificationAutoRemoveException(we);
117         }
118 
119         flagWorkflowDocument(workflowDoc);
120 
121         try {
122             notificationWorkflowDocumentService.clearAllFyisAndAcknowledgeNotificationWorkflowDocument(messageDelivery.getUserRecipientId(), workflowDoc, 
123                     NotificationConstants.KEW_CONSTANTS.GENERIC_AUTO_REMOVE_ANNOTATION);
124         } catch(WorkflowException we) {
125             throw new NotificationAutoRemoveException(we);
126         }
127     }
128 
129     /**
130      * @see org.kuali.rice.ken.deliverer.NotificationMessageDeliverer#dismissMessageDelivery(org.kuali.rice.ken.bo.NotificationMessageDelivery, java.lang.String, java.lang.String)
131      */
132     public void dismissMessageDelivery(NotificationMessageDelivery messageDelivery, String user, String cause) {
133         // TODO: move hardcoded web controller actions here...
134         LOG.info("Dismissing as user '" + user + "' workflow document '" + messageDelivery.getDeliverySystemId() + "' corresponding to message delivery #" + messageDelivery.getId() + " due to cause: " + cause);
135         if (NotificationConstants.AUTO_REMOVE_CAUSE.equals(cause)) {
136             // perform an auto-remove
137             // XXX: currently auto-removes are going through autoremove method
138         } else {
139             NotificationWorkflowDocument nwd;
140             try {
141                 nwd = notificationWorkflowDocumentService.getNotificationWorkflowDocumentByDocumentId(user, Long.decode(messageDelivery.getDeliverySystemId()));
142             } catch (WorkflowException we) {
143                 LOG.error("Could not get workflow document with docId");
144                 throw new RuntimeException("Could not get workflow document with docId", we);
145             }
146             
147             flagWorkflowDocument(nwd);
148 
149             try {
150                 if (NotificationConstants.ACK_CAUSE.equals(cause)) {
151                     // moved from NotificationController, ack command
152                     /*
153                      * acknowledge using workflow docId
154                      */
155                     if (nwd.isAcknowledgeRequested()) {
156                         nwd.acknowledge("This notification has been acknowledged.");
157                         LOG.debug("acknowledged "+nwd.getTitle());                      
158                         LOG.debug("status display value: "+nwd.getStatusDisplayValue());
159                     } else {
160                         LOG.debug("Acknowledgement was not needed for document " + nwd.getRouteHeaderId());
161                     }
162                 } else if (NotificationConstants.FYI_CAUSE.equals(cause)) {
163                     // moved from NotificationController, fyi command
164                     /*
165                      * FYI using workflow docId
166                      */
167                     if (nwd.isFYIRequested()) {
168                         nwd.fyi();
169                         LOG.debug("fyi "+nwd.getTitle());                      
170                         LOG.debug("status display value: "+nwd.getStatusDisplayValue());
171                     } else {
172                         LOG.debug("FYI was not needed for document " + nwd.getRouteHeaderId());
173                     }
174                 }
175             } catch (WorkflowException we) {
176                 LOG.error("Error performing action on workflow document");
177                 throw new RuntimeException("Error performing action on workflow document", we);
178             }
179         }
180     }
181 
182     /**
183      * Marks the workflow document as originating from the Notification System, so that the Notification
184      * post-processor does not route the action back through the Notification System.
185      * @param doc the doc to monogram
186      */
187     protected void flagWorkflowDocument(WorkflowDocument doc) {
188         Properties p = new Properties();
189         p.setProperty(INTERNAL_COMMAND_FLAG, "true");
190         ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
191         try {
192             p.store(baos, null);
193         } catch (IOException ioe) {
194             throw new RuntimeException("Could not store properties", ioe);
195         }
196         doc.getDocumentContent().setAttributeContent("<whatever>" + new String(baos.toByteArray()) + "</whatever>");
197     }
198 }