001    /**
002     * Copyright 2005-2012 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.kew.engine.node;
017    
018    import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
019    
020    import java.util.Iterator;
021    import 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     */
029    public 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    }