001/**
002 * Copyright 2005-2016 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.kew.engine.node;
017
018import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
019
020import java.util.Iterator;
021import java.util.List;
022
023
024/**
025 * Logs {@link RouteNodeInstance} graphs in a format which is indented and easy to read. 
026 *
027 * @author Kuali Rice Team (rice.collab@kuali.org)
028 */
029public class NodeJotter {
030
031    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(NodeJotter.class);
032    private static final String INDENT = "   ";
033
034    public static void jotNodeInstance(DocumentRouteHeaderValue document, RouteNodeInstance nodeInstance) {
035        try {
036            if (LOG.isDebugEnabled()) {
037                List initialNodeInstances = document.getInitialRouteNodeInstances();
038                for (Iterator iterator = initialNodeInstances.iterator(); iterator.hasNext();) {
039                    RouteNodeInstance initialNodeInstance = (RouteNodeInstance) iterator.next();
040                    NodeType nodeType = NodeType.fromNodeInstance(initialNodeInstance);
041                    LOG.debug(orchestrateOutput(initialNodeInstance, nodeType, null, 0));
042                }
043            } else if (LOG.isInfoEnabled()) {
044                NodeType nodeType = NodeType.fromNodeInstance(nodeInstance);
045                LOG.info(outputNodeInstanceToLog(nodeInstance, nodeType, 0));
046            }
047        } catch (Throwable t) {
048            LOG.warn("Caught error attempting to jot node instance" + nodeInstance);
049        }
050    }
051
052    private static String orchestrateOutput(RouteNodeInstance nodeInstance, NodeType nodeType, SplitJoinContext sjCtx, int indentDepth) throws Exception {
053        String output = "";
054        boolean isSplit = NodeType.SPLIT.equals(nodeType);
055        boolean isJoin = NodeType.JOIN.equals(nodeType);
056        if (isJoin && sjCtx != null) {
057            sjCtx.joinNodeInstance = nodeInstance;
058            return output;
059        }
060        SplitJoinContext newSplitJoinContext = null;
061        if (isSplit) {
062            newSplitJoinContext = new SplitJoinContext(nodeInstance);
063        }
064        output += outputNodeInstanceToLog(nodeInstance, nodeType, indentDepth);
065        for (Iterator iterator = nodeInstance.getNextNodeInstances().iterator(); iterator.hasNext();) {
066            RouteNodeInstance nextNodeInstance = (RouteNodeInstance) iterator.next();
067            nodeType = NodeType.fromNodeInstance(nextNodeInstance);
068            if (newSplitJoinContext != null) {
069                output += orchestrateOutput(nextNodeInstance, nodeType, newSplitJoinContext, indentDepth + 1);
070            } else {
071                output += orchestrateOutput(nextNodeInstance, nodeType, sjCtx, indentDepth + 1);
072            }
073        }
074        if (isSplit) {
075            if (newSplitJoinContext != null && newSplitJoinContext.joinNodeInstance != null) {
076                nodeType = NodeType.fromNodeInstance(newSplitJoinContext.joinNodeInstance);
077                output += orchestrateOutput(newSplitJoinContext.joinNodeInstance, nodeType, sjCtx, indentDepth);
078            }
079        }
080        return output;
081    }
082
083    private static String outputNodeInstanceToLog(RouteNodeInstance nodeInstance, NodeType nodeType, int indentDepth) throws Exception {
084        String memAddress = nodeInstance.toString().split("@")[1];
085        String dataIndent = getIndent(indentDepth + 1);
086        String output = getIndent(indentDepth) + "[NODE type=" + nodeType.getName() + "]\n" + dataIndent + "Name: " + nodeInstance.getName() + "(" + memAddress + ")\n";
087        output += dataIndent + "State: ";
088        for (Iterator iterator = nodeInstance.getState().iterator(); iterator.hasNext();) {
089            NodeState nodeState = (NodeState) iterator.next();
090            output += nodeState.getKey() + "=" + nodeState.getValue();
091            if (iterator.hasNext()) {
092                output += ",";
093            }
094        }
095        output += "\n" + dataIndent + "Status Flags: initial=" + nodeInstance.isInitial() + ", active=" + nodeInstance.isActive() + ", complete=" + nodeInstance.isComplete();
096        output += (nodeInstance.getProcess() == null ? "" : "\n" + dataIndent + "ProcessDefinition Name: " + nodeInstance.getProcess().getName());
097        output += "\n";
098        return output;
099    }
100
101    private static String getIndent(int indentDepth) {
102        StringBuffer buffer = new StringBuffer();
103        for (int depth = 0; depth < indentDepth; depth++) {
104            buffer.append(INDENT);
105        }
106        return buffer.toString();
107    }
108
109    private static class SplitJoinContext {
110        public RouteNodeInstance splitNodeInstance;
111        public RouteNodeInstance joinNodeInstance;
112
113        public SplitJoinContext(RouteNodeInstance splitNodeInstance) {
114            this.splitNodeInstance = splitNodeInstance;
115        }
116    }
117
118}