001    /**
002     * Copyright 2005-2014 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.ken.postprocessor.kew;
017    
018    import org.apache.log4j.Logger;
019    import org.kuali.rice.ken.bo.NotificationMessageDelivery;
020    import org.kuali.rice.ken.core.GlobalNotificationServiceLocator;
021    import org.kuali.rice.ken.deliverer.impl.KEWActionListMessageDeliverer;
022    import org.kuali.rice.ken.service.NotificationMessageDeliveryService;
023    import org.kuali.rice.ken.service.NotificationService;
024    import org.kuali.rice.ken.util.NotificationConstants;
025    import org.kuali.rice.ken.util.Util;
026    import org.kuali.rice.kew.api.KewApiConstants;
027    import org.kuali.rice.kew.api.WorkflowDocument;
028    import org.kuali.rice.kew.api.WorkflowDocumentFactory;
029    import org.kuali.rice.kew.api.action.ActionType;
030    import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent;
031    import org.kuali.rice.kew.framework.postprocessor.AfterProcessEvent;
032    import org.kuali.rice.kew.framework.postprocessor.BeforeProcessEvent;
033    import org.kuali.rice.kew.framework.postprocessor.DeleteEvent;
034    import org.kuali.rice.kew.framework.postprocessor.DocumentLockingEvent;
035    import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
036    import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
037    import org.kuali.rice.kew.framework.postprocessor.PostProcessor;
038    import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport;
039    
040    import java.io.ByteArrayInputStream;
041    import java.io.IOException;
042    import java.util.List;
043    import java.util.Properties;
044    
045    
046    /**
047     * This class is the post processor that gets run when workflow state changes occur for the 
048     * underlying core NotificationDocumentType that all notifications go into KEW as.  This class is responsible for changing 
049     * the state of the associated notification message delivery record after someone FYIs or ACKs their notification 
050     * in the KEW Action List.
051     * @author Kuali Rice Team (rice.collab@kuali.org)
052     */
053    public class NotificationPostProcessor implements PostProcessor {
054        private static final Logger LOG = Logger.getLogger(NotificationPostProcessor.class);
055    
056        NotificationService notificationService;
057        NotificationMessageDeliveryService msgDeliverySvc;
058    
059        /**
060         * Constructs a NotificationPostProcessor instance.
061         */
062        public NotificationPostProcessor() {
063            this.msgDeliverySvc = GlobalNotificationServiceLocator.getInstance().getNotificationMessageDeliveryService();
064            this.notificationService = GlobalNotificationServiceLocator.getInstance().getNotificationService();
065        }
066    
067        /**
068         * Need to intercept ACKNOWLEDGE or FYI actions taken on notification workflow documents and set the local state of the 
069         * Notification to REMOVED as well.
070         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#doActionTaken(org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent)
071         */
072        public ProcessDocReport doActionTaken(ActionTakenEvent event) throws Exception {
073            LOG.debug("ENTERING NotificationPostProcessor.doActionTaken() for Notification action item with document ID: " + event.getDocumentId());
074    
075            // NOTE: this action could be happening because the user initiated it via KEW, OR because a dismiss or autoremove action
076            // has been invoked programmatically and the KEWActionListMessageDeliverer is taking an action...so there is a risk of being
077            // invoked recursively (which will lead to locking issues and other problems).  We therefore mark the document in the KEWActionList
078            // MessageDeliverer before performing an action, so that we can detect this scenario here, and avoid invoking KEN again.
079    
080            LOG.debug("ACTION TAKEN=" + event.getActionTaken().getActionTaken());
081    
082            String actionTakenCode = event.getActionTaken().getActionTaken().getCode();
083    
084            Properties p = new Properties();
085            WorkflowDocument doc = WorkflowDocumentFactory.loadDocument(event.getActionTaken().getPrincipalId(), event.getDocumentId());
086            try {
087                p.load(new ByteArrayInputStream(doc.getAttributeContent().getBytes()));
088            } catch (IOException ioe) {
089                throw new RuntimeException(ioe);
090            }
091            String internalCommand = p.getProperty(KEWActionListMessageDeliverer.INTERNAL_COMMAND_FLAG);
092    
093            if (Boolean.valueOf(internalCommand).booleanValue()) {
094                LOG.info("Internal command detected by NotificationPostProcessor - will not invoke KEN");
095                return new ProcessDocReport(true, "");
096            }
097            
098            LOG.info("NotificationPostProcessor detected end-user action " + event.getActionTaken().getActionTaken() + " on document " + event.getActionTaken().getDocumentId());
099    
100            if(actionTakenCode.equals(KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD) || actionTakenCode.equals(KewApiConstants.ACTION_TAKEN_FYI_CD)) {
101                LOG.debug("User has taken either acknowledge or fy action (action code=" + actionTakenCode + 
102                        ") for Notification action item with document ID: " + event.getDocumentId() + 
103                ".  We are now changing the status of the associated NotificationMessageDelivery to REMOVED.");
104    
105                try {
106                    NotificationMessageDelivery nmd = msgDeliverySvc.getNotificationMessageDeliveryByDelivererId(event.getDocumentId());
107    
108                    if (nmd == null) {
109                        throw new RuntimeException("Could not find message delivery from workflow document " + event.getDocumentId() + " to dismiss");
110                    }
111    
112                    //get the id of the associated notification message delivery record
113                    String cause;
114                    if (KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD.equals(actionTakenCode)) {
115                        cause = NotificationConstants.ACK_CAUSE;
116                    } else if (KewApiConstants.ACTION_TAKEN_FYI_CD.equals(actionTakenCode)) {
117                        cause = NotificationConstants.FYI_CAUSE;
118                    } else {
119                        cause = "unknown";
120                    }
121    
122                    LOG.info("Dismissing message id " + nmd.getId() + " due to cause: " + cause);
123                    notificationService.dismissNotificationMessageDelivery(nmd.getId(),
124                            Util.getNotificationSystemUser(),
125                            cause);
126                } catch(Exception e) {
127                    throw new RuntimeException("Error dismissing message", e);
128                }
129            }
130    
131            LOG.debug("LEAVING NotificationPostProcessor.doActionTaken() for Notification action item with document ID: " + event.getDocumentId());
132            return new ProcessDocReport(true);
133        }
134    
135        /**
136         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#afterActionTaken(org.kuali.rice.kew.api.action.ActionType, org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent) 
137         */
138        @Override
139        public ProcessDocReport afterActionTaken(ActionType performed, ActionTakenEvent event) throws Exception {
140            return new ProcessDocReport(true, "");
141        }
142    
143        /**
144         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#doDeleteRouteHeader(org.kuali.rice.kew.framework.postprocessor.DeleteEvent)
145         */
146        public ProcessDocReport doDeleteRouteHeader(DeleteEvent arg0) throws Exception {
147            return new ProcessDocReport(true, "");
148        }
149    
150        /**
151         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#doRouteLevelChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange)
152         */
153        public ProcessDocReport doRouteLevelChange(DocumentRouteLevelChange arg0) throws Exception {
154            return new ProcessDocReport(true, "");
155        }
156    
157        /**
158         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#doRouteStatusChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange)
159         */
160        public ProcessDocReport doRouteStatusChange(DocumentRouteStatusChange arg0) throws Exception {
161            return new ProcessDocReport(true, "");
162        }
163    
164        /**
165         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#beforeProcess(org.kuali.rice.kew.framework.postprocessor.BeforeProcessEvent)
166         */
167        public ProcessDocReport beforeProcess(BeforeProcessEvent beforeProcessEvent) throws Exception {
168            return new ProcessDocReport(true, "");
169        }
170    
171        /**
172         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#afterProcess(org.kuali.rice.kew.framework.postprocessor.AfterProcessEvent)
173         */
174        public ProcessDocReport afterProcess(AfterProcessEvent afterProcessEvent) throws Exception {
175            return new ProcessDocReport(true, "");
176        }
177    
178        /**
179         * @see org.kuali.rice.kew.framework.postprocessor.PostProcessor#getDocumentIdsToLock(org.kuali.rice.kew.framework.postprocessor.DocumentLockingEvent)
180         */
181            public List<String> getDocumentIdsToLock(DocumentLockingEvent documentLockingEvent) throws Exception {
182                    return null;
183            }
184        
185        
186        
187        
188    }