001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.scxml2.env; 018 019import java.io.Serializable; 020import java.util.Iterator; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.apache.commons.scxml2.ErrorReporter; 027import org.apache.commons.scxml2.model.Data; 028import org.apache.commons.scxml2.model.EnterableState; 029import org.apache.commons.scxml2.model.Executable; 030import org.apache.commons.scxml2.model.SCXML; 031import org.apache.commons.scxml2.model.State; 032import org.apache.commons.scxml2.model.TransitionTarget; 033import org.apache.commons.scxml2.semantics.ErrorConstants; 034 035/** 036 * Custom error reporter that log execution errors. 037 */ 038public class SimpleErrorReporter implements ErrorReporter, Serializable { 039 040 /** Serial version UID. */ 041 private static final long serialVersionUID = 1L; 042 /** Log. */ 043 private Log log = LogFactory.getLog(getClass()); 044 045 /** 046 * Constructor. 047 */ 048 public SimpleErrorReporter() { 049 super(); 050 } 051 052 /** 053 * @see ErrorReporter#onError(String, String, Object) 054 */ 055 @SuppressWarnings("unchecked") 056 public void onError(final String errorCode, final String errDetail, 057 final Object errCtx) { 058 //Note: the if-then-else below is based on the actual usage 059 // (codebase search), it has to be kept up-to-date as the code changes 060 String errCode = errorCode.intern(); 061 StringBuffer msg = new StringBuffer(); 062 msg.append(errCode).append(" ("); 063 msg.append(errDetail).append("): "); 064 if (errCode == ErrorConstants.NO_INITIAL) { 065 if (errCtx instanceof SCXML) { 066 //determineInitialStates 067 msg.append("<SCXML>"); 068 } else if (errCtx instanceof State) { 069 //determineInitialStates 070 //determineTargetStates 071 msg.append("State " + LogUtils.getTTPath((State) errCtx)); 072 } 073 } else if (errCode == ErrorConstants.UNKNOWN_ACTION) { 074 //executeActionList 075 msg.append("Action: " + errCtx.getClass().getName()); 076 } else if (errCode == ErrorConstants.ILLEGAL_CONFIG) { 077 //isLegalConfig 078 if (errCtx instanceof Map.Entry) { //unchecked cast below 079 Map.Entry<EnterableState, Set<EnterableState>> badConfigMap = 080 (Map.Entry<EnterableState, Set<EnterableState>>) errCtx; 081 EnterableState es = badConfigMap.getKey(); 082 Set<EnterableState> vals = badConfigMap.getValue(); 083 msg.append(LogUtils.getTTPath(es) + " : ["); 084 for (Iterator<EnterableState> i = vals.iterator(); i.hasNext();) { 085 EnterableState ex = i.next(); 086 msg.append(LogUtils.getTTPath(ex)); 087 if (i.hasNext()) { // reason for iterator usage 088 msg.append(", "); 089 } 090 } 091 msg.append(']'); 092 } else if (errCtx instanceof Set) { //unchecked cast below 093 Set<EnterableState> vals = (Set<EnterableState>) errCtx; 094 msg.append("<SCXML> : ["); 095 for (Iterator<EnterableState> i = vals.iterator(); i.hasNext();) { 096 EnterableState ex = i.next(); 097 msg.append(LogUtils.getTTPath(ex)); 098 if (i.hasNext()) { 099 msg.append(", "); 100 } 101 } 102 msg.append(']'); 103 } 104 } else if (errCode == ErrorConstants.EXPRESSION_ERROR) { 105 if (errCtx instanceof Executable) { 106 TransitionTarget parent = ((Executable) errCtx).getParent(); 107 msg.append("Expression error inside " + LogUtils.getTTPath(parent)); 108 } 109 else if (errCtx instanceof Data) { 110 // Data expression error 111 msg.append("Expression error for data element with id "+((Data)errCtx).getId()); 112 } 113 else if (errCtx instanceof SCXML) { 114 // Global Script 115 msg.append("Expression error inside the global script"); 116 } 117 } 118 handleErrorMessage(errorCode, errDetail, errCtx, msg); 119 } 120 121 /** 122 * Final handling of the resulting errorMessage build by {@link #onError(String, String, Object)} onError}. 123 * <p>The default implementation write the errorMessage as a warning to the log.</p> 124 */ 125 protected void handleErrorMessage(final String errorCode, final String errDetail, 126 final Object errCtx, final CharSequence errorMessage) { 127 128 if (log.isWarnEnabled()) { 129 log.warn(errorMessage.toString()); 130 } 131 } 132} 133