Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
Copyright 2005-2013 The Kuali Foundation Licensed under the Educational Community License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.opensource.org/licenses/ecl2.php Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 
 package org.kuali.rice.kew.actions;
 
 
 import java.util.List;
 import java.util.Map;
 import java.util.Set;

Since:
2.1
 
 public class RecallAction extends ReturnToPreviousNodeAction {
     private static final Logger LOG = Logger.getLogger(RecallAction.class);
 
     protected final boolean cancel;
     protected final Collection<RecipientnotificationRecipients;

    
Constructor required for ActionRegistry validation
 
     public RecallAction(DocumentRouteHeaderValue routeHeaderPrincipalContract principal) {
         this(routeHeaderprincipalnulltruetruetrue);
     }
 
     public RecallAction(DocumentRouteHeaderValue routeHeaderPrincipalContract principalString annotationboolean cancel) {
         this(routeHeaderprincipalannotationcanceltruetrue);
     }
 
     public RecallAction(DocumentRouteHeaderValue routeHeaderPrincipalContract principalString annotationboolean cancelboolean sendNotifications) {
         this(routeHeaderprincipalannotationcancelsendNotificationstrue);
    }
    public RecallAction(DocumentRouteHeaderValue routeHeaderPrincipalContract principalString annotationboolean cancelboolean sendNotificationsboolean runPostProcessorLogic) {
        super(..getCode(), routeHeaderprincipal,
              principal.getPrincipalName() + " recalled document" + (StringUtils.isBlank(annotation) ? "" : ": " + annotation),
              ,
              sendNotificationsrunPostProcessorLogic);
        this. = cancel;
        this. = Collections.unmodifiableCollection(parseNotificationRecipients(routeHeader));
    }

    
Parses notification recipients from the RECALL_NOTIFICATION document type policy, if present

Parameters:
routeHeader this document
Returns:
notification recipient RuleResponsibilityBos
    protected static Collection<RecipientparseNotificationRecipients(DocumentRouteHeaderValue routeHeader) {
        Collection<RecipienttoNotify = new ArrayList<Recipient>();
        org.kuali.rice.kew.doctype.DocumentTypePolicy recallNotification = routeHeader.getDocumentType().getRecallNotification();
        if (recallNotification != null) {
            String config = recallNotification.getPolicyStringValue();
            if (!StringUtils.isBlank(config)) {
                Document notificationConfig;
                try {
                    notificationConfig = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(config)));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                DOMBuilder jdom = new DOMBuilder();
                // ok, so using getElementsByTagName is less strict than it could be because it inspects all descendants
                // not just immediate children.  but the w3c dom API stinks and this is less painful than manually searching immediate children
                NodeList recipients = notificationConfig.getDocumentElement().getElementsByTagName("recipients");
                for (int i = 0; i < recipients.getLength(); i++) {
                    NodeList children = recipients.item(i).getChildNodes();
                    for (int j = 0; j < children.getLength(); j++) {
                        Node n = children.item(j);
                        if (n instanceof Element) {
                            Element e = (Elementn;
                            if ("role".equals(e.getNodeName())) {
                                String ns = e.getAttribute("namespace");
                                String name = e.getAttribute("name");
                                Role role = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName(nsname);
                                if (role == null) {
                                    .error("No role round: " + ns + ":" + name);
                                } else {
                                    toNotify.add(new KimRoleRecipient(role));
                                }
                            } else {
                                // parseResponsibilityNameAndType parses a single responsibility choice from a parent element
                                // wrap the element with a parent so we can use this method
                                org.jdom.Element parent = new org.jdom.Element("parent");
                                parent.addContent(jdom.build((Elemente.cloneNode(true)).detach());
                                toNotify.add(CommonXmlParser.parseResponsibilityNameAndType(parent).getRecipient());
                            }
                        }
                    }
                }
            }
        }
        return toNotify;
    }
    @Override
    public String validateActionRules(List<ActionRequestValueactionRequests) {
            return "Document of status '" + getRouteHeader().getDocRouteStatus() + "' cannot taken action '" + ActionType.fromCode(this.getActionTakenCode()).getLabel() + "' to node name " + this.;
        }
        // validate that recall action can be taken given prior actions taken
        String errMsg = validateActionsTaken(getRouteHeader());
        if (errMsg != null) {
            return errMsg;
        }
        // validate that the doc will actually route to anybody
        errMsg = validateRouting(getRouteHeader());
        if (errMsg != null) {
            return errMsg;
        }
        if (!KEWServiceLocator.getDocumentTypePermissionService().canRecall(getPrincipal().getPrincipalId(), getRouteHeader())) {
            return "User is not authorized to Recall document";
        }
        return "";
    }

    
Determines whether prior actions taken are compatible with recall action by checking the RECALL_VALID_ACTIONSTAKEN document type policy.

Parameters:
rh the DocumentRouteHeaderValue
Returns:
null if valid (policy not specified, no actions taken, or all actions taken are in valid actions taken list), or error message if invalid
        if (StringUtils.isNotBlank(validActionsTaken)) {
            // interpret as comma-separated list of codes OR labels
            String[] validActionsTakenStrings = validActionsTaken.split("\\s*,\\s*");
            Set<ActionTypevalidActionTypes = new HashSet<ActionType>(validActionsTakenStrings.length);
            for (String svalidActionsTakenStrings) {
                ActionType at = ActionType.fromCodeOrLabel(s);
                if (at == null) {
                    throw new IllegalArgumentException("Failed to locate the ActionType with the given code or label: " + s);
                }
                validActionTypes.add(at);
            }
            Collection<ActionTakenValueactionsTaken = KEWServiceLocator.getActionTakenService().getActionsTaken(getRouteHeader().getDocumentId());
            for (ActionTakenValue actionTakenactionsTaken) {
                ActionType at = ActionType.fromCode(actionTaken.getActionTaken());
                if (!validActionTypes.contains(at)) {
                    return "Invalid prior action taken: '" + at.getLabel() + "'. Cannot Recall.";
                }
            }
        }
        return null;
    }

    
Determines whether the doc's type appears to statically define any routing. If not, then Recall action doesn't make much sense, and should not be available. Checks whether any document type processes are defined, and if so, whether are are any non-"adhoc" nodes (based on literal node name check).

Parameters:
rh the DocumentRouteHeaderValue
Returns:
error message if it looks like it's this doc will not route to a person based on static route definition, null (valid) otherwise
        List<ProcessDefinitionBoprocesses = rh.getDocumentType().getProcesses();
        String errMsg = null;
        if (processes.size() == 0) {
            // if no processes are present then this doc isn't going to route to anyone
            errMsg = "No processes are defined for document type. Recall is not applicable.";
        } else {
            // if there is not at least one route node not named "ADHOC", then assume this doc will not route to anybody
            RouteNode initialRouteNode = rh.getDocumentType().getPrimaryProcess().getInitialRouteNode();
            if (initialRouteNode.getName().equalsIgnoreCase("ADHOC") && initialRouteNode.getNextNodeIds().isEmpty()) {
                errMsg = "No non-adhoc route nodes defined for document type. Recall is not applicable";
            }
        }
        return errMsg;
    }
    @Override // overridden to simply pass through all actionrequests
        return actionRequests;
    }
    @Override // overridden to implement Recall action validation
    public boolean isActionCompatibleRequest(List<ActionRequestValuerequests) {
        return true;
    }
    @Override // When invoked, RECALL TO ACTION LIST action will return the document back to their action list with the route status of SAVED and requested action of COMPLETE.
        return .;
    }

    
Override the default return to previous behavior so that the document is returned to the recaller, not initiator
    @Override
        return getPrincipal();
    }
    @Override
    protected void sendAdditionalNotifications() {
        super.sendAdditionalNotifications();
        // NOTE: do we need construct w/ nodeInstance here?
        ActionRequestFactory arFactory = new ActionRequestFactory();
        for (Recipient recipientthis.) {
            if (!(recipient instanceof KimRoleRecipient)) {
                arFactory.addRootActionRequest(..getCode(), 0, recipient"Document was recalled".nullnullnull);
            } else {
                KimRoleRecipient kimRoleRecipient = (KimRoleRecipientrecipient;
                // no qualifications
                Map<StringStringqualifications = Collections.emptyMap();
                .info("Adding KIM Role Request for " + kimRoleRecipient.getRole());
                arFactory.addKimRoleRequest(..getCode(), 0, kimRoleRecipient.getRole(),
                                            KimApiServiceLocator.getRoleService().getRoleMembers(Collections.singletonList(kimRoleRecipient.getRole().getId()), qualifications),
                                            "Document was recalled".,
                                            // force action, so the member is notified even if they have already participated in the workflow
                                            // since this is a notification about an exceptional case they should receive
                                            true..getCode(), "Document was recalled");
            }
        }
    }
    @Override
    public void recordAction() throws InvalidActionTakenException {
        if (this.) {
            // perform action validity check here as well as WorkflowDocumentServiceImpl calls recordAction immediate after action construction
            if (!org.apache.commons.lang.StringUtils.isEmpty(errorMessage)) {
                throw new InvalidActionTakenException(errorMessage);
            }
            // we are in cancel mode, execute a cancel action with RECALL code
            // NOTE: this performs CancelAction validation, including getDocumentTypePermissionService().canCancel
            // stub out isActionCompatibleRequest since we are programmatically forcing this action
            new CancelAction(.this.this.getPrincipal(), this.) {
                @Override
                public boolean isActionCompatibleRequest(List<ActionRequestValuerequests) {
                    return true;
                }
                @Override
                protected void markDocumentStatus() throws InvalidActionTakenException {
                    getRouteHeader().markDocumentRecalled();
                }
            }.recordAction();
        } else {
            super.recordAction();
            // When invoked, RECALL TO ACTION LIST action will return the document back to their action list with the route status of SAVED and requested action of COMPLETE.
            String oldStatus = getRouteHeader().getDocRouteStatus();
            getRouteHeader().markDocumentSaved();
            String newStatus = getRouteHeader().getDocRouteStatus();
            notifyStatusChange(newStatusoldStatus);
            KEWServiceLocator.getRouteHeaderService().saveRouteHeader();
        }
        // we don't have an established mechanism for exposing the action taken that is saved as the result of a (nested) action
        // so use the last action take saved
        if (last != null) {
            notifyAfterActionTaken(last);
        }
    }

    
Returns the last action taken on a document

Parameters:
docId the doc id
Returns:
last action taken on a document
    protected static ActionTakenValue getLastActionTaken(String docId) {
        ActionTakenValue last = null;
        Collection<ActionTakenValueactionsTaken = (Collection<ActionTakenValue>) KEWServiceLocator.getActionTakenService().getActionsTaken(docId);
        for (ActionTakenValue atactionsTaken) {
            if (last == null || at.getActionDate().after(last.getActionDate())) {
                last = at;
            }
        }
        return last;
    }
New to GrepCode? Check out our FAQ X