001/**
002 * Copyright 2005-2017 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 */
016package org.kuali.rice.ken.postprocessor.kew;
017
018import org.apache.log4j.Logger;
019import org.kuali.rice.ken.bo.NotificationMessageDelivery;
020import org.kuali.rice.ken.core.GlobalNotificationServiceLocator;
021import org.kuali.rice.ken.deliverer.impl.KEWActionListMessageDeliverer;
022import org.kuali.rice.ken.service.NotificationMessageDeliveryService;
023import org.kuali.rice.ken.service.NotificationService;
024import org.kuali.rice.ken.util.NotificationConstants;
025import org.kuali.rice.ken.util.Util;
026import org.kuali.rice.kew.api.KewApiConstants;
027import org.kuali.rice.kew.api.WorkflowDocument;
028import org.kuali.rice.kew.api.WorkflowDocumentFactory;
029import org.kuali.rice.kew.api.action.ActionType;
030import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent;
031import org.kuali.rice.kew.framework.postprocessor.AfterProcessEvent;
032import org.kuali.rice.kew.framework.postprocessor.BeforeProcessEvent;
033import org.kuali.rice.kew.framework.postprocessor.DeleteEvent;
034import org.kuali.rice.kew.framework.postprocessor.DocumentLockingEvent;
035import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
036import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
037import org.kuali.rice.kew.framework.postprocessor.PostProcessor;
038import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport;
039
040import java.io.ByteArrayInputStream;
041import java.io.IOException;
042import java.util.List;
043import 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 */
053public 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}