Coverage Report - org.kuali.rice.kew.actionrequest.service.impl.NotificationSuppression
 
Classes in this File Line Coverage Branch Coverage Complexity
NotificationSuppression
0%
0/60
0%
0/40
4
NotificationSuppression$1
0%
0/13
0%
0/10
4
 
 1  
 /*
 2  
  * Copyright 2007-2009 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.kew.actionrequest.service.impl;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.Collections;
 20  
 import java.util.LinkedList;
 21  
 import java.util.List;
 22  
 
 23  
 import org.apache.commons.collections.CollectionUtils;
 24  
 import org.apache.commons.collections.Predicate;
 25  
 import org.kuali.rice.kew.actionitem.ActionItem;
 26  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 27  
 import org.kuali.rice.kew.dto.ActionRequestDTO;
 28  
 import org.kuali.rice.kew.engine.RouteContext;
 29  
 import org.kuali.rice.kew.engine.node.NodeState;
 30  
 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
 31  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 32  
 import org.kuali.rice.kew.util.KEWConstants;
 33  
 
 34  
 /**
 35  
  * This utility class encapsulates functions used to provide notification suppression
 36  
  * 
 37  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 38  
  *
 39  
  */
 40  0
 public class NotificationSuppression {
 41  
 
 42  
     public static final String SUPPRESS_NOTIFY_KEY_START = "SuppressNotify";
 43  
     
 44  
         /**
 45  
          * add metadata (a NodeState) to the route node so that if this action request is regenerated 
 46  
          * verbatim,  the notification email will suppressed (since it is a duplicate!).
 47  
          * @param nodeInstance where additional NodeState will be added
 48  
          * @param actionRequestValue 
 49  
          */
 50  
     public void addNotificationSuppression(
 51  
                     RouteNodeInstance nodeInstance, ActionRequestValue actionRequestValue) {
 52  
 
 53  
             // iterative depth first traversal of the action request tree
 54  0
             LinkedList<ActionRequestValue> stack = new LinkedList<ActionRequestValue>();
 55  
             // push
 56  0
             stack.add(actionRequestValue);
 57  
 
 58  0
             while (stack.size() > 0) {
 59  
                     // pop our next action request 
 60  0
                     ActionRequestValue childActionRequest = stack.removeLast(); 
 61  
 
 62  
                     // process this action request only if it is a leaf
 63  0
                     if (childActionRequest.getChildrenRequests() == null || 
 64  
                                     childActionRequest.getChildrenRequests().size() == 0) {
 65  0
                             List<String> requestKeys = getSuppressNotifyNodeStateKeys(childActionRequest);
 66  0
                             if (requestKeys != null) for (String requestKey : requestKeys) { 
 67  0
                                     if (nodeInstance.getNodeState(requestKey) == null) { // only add once
 68  0
                                             NodeState ns = new NodeState();
 69  0
                                             ns.setKey(requestKey);
 70  0
                                             ns.setValue("notification suppression");
 71  0
                                             nodeInstance.addNodeState(ns);
 72  0
                                     }
 73  
                             }
 74  
                     }
 75  
 
 76  
                     // put child action requests on the stack
 77  0
                     if (childActionRequest.getChildrenRequests() != null) {
 78  
                             // equivalent to 'push' all
 79  0
                             stack.addAll(childActionRequest.getChildrenRequests());
 80  
                     }
 81  0
             }
 82  0
     }
 83  
         
 84  
         /**
 85  
          * This method filters any ActionItems whose related ActionRequestValueS have been flagged for notification
 86  
          * suppression.
 87  
          * 
 88  
          * @param actionItems the ActionItemS to filter
 89  
          * @param routeNodeInstance the RouteNodeInstance that the actionItems are associated with
 90  
          */
 91  
         protected void filterNotificationSuppressedActionItems(List<ActionItem> actionItems, 
 92  
                         final RouteNodeInstance routeNodeInstance) {
 93  
                 
 94  
                 // remove all actionItems from the collection whose request has a suppress notification node state element
 95  0
                 CollectionUtils.filter(actionItems, new Predicate() {
 96  
                         public boolean evaluate(Object object) {
 97  0
                                 boolean result = true;
 98  0
                                 ActionItem actionItem = (ActionItem)object;
 99  0
                                 ActionRequestValue actionRequest = 
 100  
                                         KEWServiceLocator.getActionRequestService().findByActionRequestId(actionItem.getActionRequestId());
 101  
                                 
 102  0
                                 List<String> suppressNotificationKeys = getSuppressNotifyNodeStateKeys(actionRequest);
 103  0
                                 if (suppressNotificationKeys != null && suppressNotificationKeys.size() > 0) {
 104  
                                         // if any of the keys are not present, we need to notify
 105  0
                                         boolean containsAll = true;
 106  0
                                         for (String key : suppressNotificationKeys) {
 107  0
                                                 if (routeNodeInstance.getNodeState(key) == null) {
 108  0
                                                         containsAll = false;
 109  0
                                                         break;
 110  
                                                 }
 111  
                                         }
 112  
                                         // actionItem will be filtered if this Predicate returns false
 113  0
                                         result = !containsAll; // only filters if all keys are present
 114  
                                 }
 115  0
                                 return result;
 116  
                         }
 117  
                 });
 118  0
         }
 119  
         
 120  
         /**
 121  
          * 
 122  
          * <p>This method takes care of notification for ActionItemS.  It has logic for suppressing notifications 
 123  
      * when the RouteNodeInstance has NodeState specifically hinting for notification suppression for a given 
 124  
      * ActionItem.
 125  
          * 
 126  
          * <p>A side effect is that any notification suppression NodeStateS will be removed
 127  
          * from the RouteNodeInstance after notifications are sent.
 128  
          * 
 129  
          * @param actionItems a list of ActionItemS related to the given routeNodeInstance
 130  
          * @param routeNodeInstance the RouteNodeInstance related to the given actionItems
 131  
          */
 132  
         public void notify(List<ActionItem> actionItems, RouteNodeInstance routeNodeInstance) {
 133  
                 
 134  0
                 if (actionItems != null && actionItems.size() > 0) {
 135  0
                         actionItems = new ArrayList<ActionItem>(actionItems); // defensive copy since we will filter
 136  0
                         filterNotificationSuppressedActionItems(actionItems, routeNodeInstance);
 137  
                         // notify for any actionItems that were not filtered
 138  0
                         if (actionItems.size() > 0) { KEWServiceLocator.getNotificationService().notify(actionItems); }
 139  0
                         deleteNotificationSuppression(routeNodeInstance);
 140  
                 }
 141  0
         }
 142  
 
 143  
         /**
 144  
          * This method removes all NodeStates related to notification suppression, saving the RouteNodeInstance if there
 145  
          * were any removed.
 146  
          * 
 147  
          * @param routeNodeInstance
 148  
          */
 149  
         @SuppressWarnings("unchecked")
 150  
         private void deleteNotificationSuppression(
 151  
                         final RouteNodeInstance routeNodeInstance) {
 152  
                 // remove all suppress notification node states
 153  0
                 List<NodeState> nodeStates = routeNodeInstance.getState();
 154  0
                 if (nodeStates != null && nodeStates.size() > 0) {
 155  0
                         List<String> nodeStateKeysToRemove = new ArrayList<String>(nodeStates.size());
 156  
 
 157  0
                         for (NodeState nodeState : nodeStates) {
 158  0
                                 if (nodeState.getKey().startsWith(NotificationSuppression.SUPPRESS_NOTIFY_KEY_START)) {
 159  0
                                         nodeStateKeysToRemove.add(nodeState.getKey());
 160  
                                 }
 161  
                         }
 162  0
                         if (nodeStateKeysToRemove.size() > 0) {
 163  0
                                 for (String nodeStateKeyToRemove : nodeStateKeysToRemove) {
 164  0
                                         routeNodeInstance.removeNodeState(nodeStateKeyToRemove);
 165  
                                 }
 166  0
                                 KEWServiceLocator.getRouteNodeService().save(routeNodeInstance);
 167  
                         }
 168  
                 }
 169  0
         }
 170  
 
 171  
         
 172  
     /**
 173  
      * Builds keys for action requests used for notification suppression.
 174  
      * <p>NOTE: This method needs to stay in sync with {@link #getSuppressNotifyNodeStateKeys(org.kuali.rice.kew.dto.ActionRequestDTO)}
 175  
      * Any changes here must be made there as well!
 176  
      * @param a
 177  
      * @return List
 178  
      */
 179  
         protected List<String> getSuppressNotifyNodeStateKeys(ActionRequestDTO a) {
 180  0
                 List<String> results = Collections.emptyList(); 
 181  0
                 if (a != null) {
 182  0
                         results = new ArrayList<String>(3);
 183  0
                         addSuppressNotifyNodeStateKey(results, KEWConstants.ACTION_REQUEST_USER_RECIPIENT_CD, a.getPrincipalId());
 184  0
                         addSuppressNotifyNodeStateKey(results, KEWConstants.ACTION_REQUEST_GROUP_RECIPIENT_CD, a.getGroupId());
 185  0
                         addSuppressNotifyNodeStateKey(results, KEWConstants.ACTION_REQUEST_ROLE_RECIPIENT_CD, a.getQualifiedRoleName());
 186  
                 }
 187  0
                 return results;
 188  
     }
 189  
 
 190  
     /**
 191  
      * Builds keys for action requests used for notification suppression.
 192  
      * <p>NOTE: This method needs to stay in sync with {@link #getSuppressNotifyNodeStateKeys(org.kuali.rice.kew.actionrequest.ActionRequestValue)}
 193  
      * Any changes here must be made there as well!
 194  
      * @param a
 195  
      * @return List
 196  
      */
 197  
         protected List<String> getSuppressNotifyNodeStateKeys(ActionRequestValue a) {
 198  0
                 List<String> results = Collections.emptyList(); 
 199  0
                 if (a != null) {
 200  0
                         results = new ArrayList<String>(3);
 201  0
                         addSuppressNotifyNodeStateKey(results, KEWConstants.ACTION_REQUEST_USER_RECIPIENT_CD, a.getPrincipalId());
 202  0
                         addSuppressNotifyNodeStateKey(results, KEWConstants.ACTION_REQUEST_GROUP_RECIPIENT_CD, a.getGroupId());
 203  0
                         addSuppressNotifyNodeStateKey(results, KEWConstants.ACTION_REQUEST_ROLE_RECIPIENT_CD, a.getQualifiedRoleName());
 204  
                 }
 205  0
                 return results;
 206  
         }
 207  
 
 208  
         
 209  
         /**
 210  
          * This method adds a suppress notify key to the passed in list
 211  
          * 
 212  
          * @param results the list that the key will be added to
 213  
          * @param responsiblePartyType
 214  
          * @param responsiblePartyId
 215  
          */
 216  
         private void addSuppressNotifyNodeStateKey(List<String> results, String responsiblePartyType,
 217  
                         String responsiblePartyId) {
 218  0
                 if (responsiblePartyId != null && responsiblePartyType != null) {
 219  0
                         StringBuilder sb = new StringBuilder(SUPPRESS_NOTIFY_KEY_START);
 220  0
                         sb.append("(");
 221  0
                         sb.append(responsiblePartyType);
 222  0
                         sb.append(",");
 223  0
                         sb.append(responsiblePartyId);
 224  0
                         sb.append(")");
 225  0
                         results.add(sb.toString());
 226  
                 }
 227  0
         }
 228  
         
 229  
 }