1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id$ 20 */ 21 22 /* 23 * 24 * LoggingErrorListener.java 25 * 26 */ 27 package org.apache.qetest.trax; 28 29 import javax.xml.transform.ErrorListener; 30 import javax.xml.transform.TransformerException; 31 32 import org.apache.qetest.Logger; 33 import org.apache.qetest.LoggingHandler; 34 35 /** 36 * Cheap-o ErrorListener for use by API tests. 37 * <p>Implements javax.xml.transform.ErrorListener and dumps 38 * everything to a Logger; is separately settable as 39 * to when it will throw an exception; also separately settable 40 * as to when we should validate specific events that we handle.</p> 41 * //@todo try calling getLocator() and asking it for info directly 42 * @author shane_curcuru@lotus.com 43 * @version $Id$ 44 */ 45 public class LoggingErrorListener extends LoggingHandler implements ErrorListener 46 { 47 48 /** No-op ctor seems useful. */ LoggingErrorListener()49 public LoggingErrorListener() 50 { 51 setLogger(getDefaultLogger()); 52 } 53 54 /** 55 * Ctor that calls setLogger automatically. 56 * 57 * @param l Logger we should log to 58 */ LoggingErrorListener(Logger l)59 public LoggingErrorListener(Logger l) 60 { 61 setLogger(l); 62 } 63 64 65 /** 66 * Constants determining when we should throw exceptions. 67 * <ul>Flags are combineable like a bitfield. 68 * <li>THROW_NEVER - never ever (always continue - note this 69 * may have unexpected effects when fatalErrors happen, see 70 * {@link javax.xml.transform.ErrorListener#fatalError(javax.xml.transform.TransformerException)}</li> 71 * <li>THROW_ON_WARNING - throw only on warnings</li> 72 * <li>THROW_ON_ERROR - throw only on errors</li> 73 * <li>THROW_ON_FATAL - throw only on fatalErrors - default</li> 74 * <li>THROW_ALWAYS - always throw exceptions</li> 75 * </ul> 76 */ 77 public static final int THROW_NEVER = 0; 78 79 /** THROW_ON_WARNING - throw only on warnings. */ 80 public static final int THROW_ON_WARNING = 1; 81 82 /** THROW_ON_ERROR - throw only on errors. */ 83 public static final int THROW_ON_ERROR = 2; 84 85 /** THROW_ON_FATAL - throw only on fatalErrors - default. */ 86 public static final int THROW_ON_FATAL = 4; 87 88 /** THROW_ALWAYS - always throw exceptions. */ 89 public static final int THROW_ALWAYS = THROW_ON_WARNING & THROW_ON_ERROR 90 & THROW_ON_FATAL; 91 92 /** If we should throw an exception for each message type. */ 93 protected int throwWhen = THROW_ON_FATAL; 94 95 /** 96 * Tells us when we should re-throw exceptions. 97 * 98 * @param t THROW_WHEN_* constant as to when we should re-throw 99 * an exception when we are called 100 */ setThrowWhen(int t)101 public void setThrowWhen(int t) 102 { 103 throwWhen = t; 104 } 105 106 /** 107 * Tells us when we should re-throw exceptions. 108 * 109 * @return THROW_WHEN_* constant as to when we should re-throw 110 * an exception when we are called 111 */ getThrowWhen()112 public int getThrowWhen() 113 { 114 return throwWhen; 115 } 116 117 /** Constant for items returned in getCounters: messages. */ 118 public static final int TYPE_WARNING = 0; 119 120 /** Constant for items returned in getCounters: errors. */ 121 public static final int TYPE_ERROR = 1; 122 123 /** Constant for items returned in getCounters: fatalErrors. */ 124 public static final int TYPE_FATALERROR = 2; 125 126 /** 127 * Counters for how many events we've handled. 128 * Index into array are the TYPE_* constants. 129 */ 130 protected int[] counters = 131 { 132 0, /* warning */ 133 0, /* error */ 134 0 /* fatalError */ 135 }; 136 137 138 /** 139 * Get a list of counters of all items we've logged. 140 * Returned as warnings, errors, fatalErrors 141 * Index into array are the TYPE_* constants. 142 * 143 * @return array of int counters for each item we log 144 */ getCounters()145 public int[] getCounters() 146 { 147 return counters; 148 } 149 150 /** Prefixed to all logger msg output. */ 151 public static final String prefix = "LEL:"; 152 153 154 /** 155 * Really Cheap-o string representation of our state. 156 * 157 * @return String of getCounters() rolled up in minimal space 158 */ getQuickCounters()159 public String getQuickCounters() 160 { 161 return (prefix + "(" + counters[TYPE_WARNING] + ", " 162 + counters[TYPE_ERROR] + ", " + counters[TYPE_FATALERROR] + ")"); 163 } 164 165 166 /** Cheap-o string representation of last warn/error/fatal we got. */ 167 protected String lastItem = NOTHING_HANDLED; 168 169 /** 170 * Sets a String representation of last item we handled. 171 * 172 * @param s set into lastItem for retrieval with getLast() 173 */ setLastItem(String s)174 protected void setLastItem(String s) 175 { 176 lastItem = s; 177 } 178 179 /** 180 * Get a string representation of last item we logged. 181 * 182 * @return String of the last item handled 183 */ getLast()184 public String getLast() 185 { 186 return lastItem; 187 } 188 189 190 /** Expected values for events we may handle, default=ITEM_DONT_CARE. */ 191 protected String[] expected = 192 { 193 ITEM_DONT_CARE, /* warning */ 194 ITEM_DONT_CARE, /* error */ 195 ITEM_DONT_CARE /* fatalError */ 196 }; 197 198 199 /** 200 * Ask us to report checkPass/Fail for certain events we handle. 201 * Since we may have to handle many events between when a test 202 * will be able to call us, testers can set this to have us 203 * automatically call checkPass when we see an item that matches, 204 * or to call checkFail when we get an unexpected item. 205 * Generally, we only call check* methods when: 206 * <ul> 207 * <li>containsString is not set, reset, or is ITEM_DONT_CARE, 208 * we do nothing (i.e. never call check* for this item)</li> 209 * <li>containsString is ITEM_CHECKFAIL, we will always call 210 * checkFail with the contents of any item if it occours</li> 211 * <li>containsString is anything else, we will grab a String 212 * representation of every item of that type that comes along, 213 * and if the containsString is found, case-sensitive, within 214 * the handled item's string, call checkPass, otherwise 215 * call checkFail</li> 216 * <ul> 217 * Note that any time we handle a particular event that was 218 * expected, we un-set the expected value for that item. This 219 * means that you can only ask us to validate one occourence 220 * of any particular event; all events after that one will 221 * be treated as ITEM_DONT_CARE. Callers can of course call 222 * setExpected again, of course, but this covers the case where 223 * we handle multiple events in a single block, perhaps out of 224 * the caller's direct control. 225 * Note that we first store the event via setLast(), then we 226 * validate the event as above, and then we potentially 227 * re-throw the exception as by setThrowWhen(). 228 * 229 * @param itemType which of the various types of items we might 230 * handle; should be defined as a constant by subclasses 231 * @param containsString a string to look for within whatever 232 * item we handle - usually checked for by seeing if the actual 233 * item we handle contains the containsString 234 */ setExpected(int itemType, String containsString)235 public void setExpected(int itemType, String containsString) 236 { 237 // Default to don't care on null 238 if (null == containsString) 239 containsString = ITEM_DONT_CARE; 240 241 try 242 { 243 expected[itemType] = containsString; 244 } 245 catch (ArrayIndexOutOfBoundsException aioobe) 246 { 247 // Just log it for callers reference and continue anyway 248 logger.logMsg(level, prefix + " setExpected called with illegal type:" + itemType); 249 } 250 } 251 252 253 /** 254 * Reset all items or counters we've handled. 255 */ reset()256 public void reset() 257 { 258 setLastItem(NOTHING_HANDLED); 259 for (int i = 0; i < counters.length; i++) 260 { 261 counters[i] = 0; 262 } 263 for (int j = 0; j < expected.length; j++) 264 { 265 expected[j] = ITEM_DONT_CARE; 266 } 267 } 268 269 270 /** 271 * Grab basic info out of a TransformerException. 272 * Worker method to hide implementation; currently just calls 273 * exception.getMessageAndLocation(). 274 * 275 * @param exception to get information from 276 * @return simple string describing the exception (getMessageAndLocation()) 277 */ getTransformerExceptionInfo(TransformerException exception)278 public String getTransformerExceptionInfo(TransformerException exception) 279 { 280 281 if (exception == null) 282 return ""; // Don't return null, just to make other code here simpler 283 284 return exception.getMessageAndLocation(); 285 } 286 287 288 /** 289 * Implementation of warning; calls logMsg with info contained in exception. 290 * 291 * @param exception provided by Transformer 292 * @exception TransformerException thrown only if asked to or if loggers are bad 293 */ warning(TransformerException exception)294 public void warning(TransformerException exception) throws TransformerException 295 { 296 297 // Increment counter and save the exception 298 counters[TYPE_WARNING]++; 299 300 String exInfo = getTransformerExceptionInfo(exception); 301 302 setLastItem(exInfo); 303 304 // Log or validate the exception 305 logOrCheck(TYPE_WARNING, "warning", exInfo); 306 307 // Also re-throw the exception if asked to 308 if ((throwWhen & THROW_ON_WARNING) == THROW_ON_WARNING) 309 { 310 // Note: re-throw the SAME exception, not a new one! 311 throw exception; 312 } 313 } 314 315 /** 316 * Implementation of error; calls logMsg with info contained in exception. 317 * Only ever throws an exception itself if asked to or if loggers are bad. 318 * 319 * @param exception provided by Transformer 320 * @exception TransformerException thrown only if asked to or if loggers are bad 321 */ error(TransformerException exception)322 public void error(TransformerException exception) throws TransformerException 323 { 324 325 // Increment counter, save the exception, and log what we got 326 counters[TYPE_ERROR]++; 327 328 String exInfo = getTransformerExceptionInfo(exception); 329 330 setLastItem(exInfo); 331 332 // Log or validate the exception 333 logOrCheck(TYPE_ERROR, "error", exInfo); 334 335 // Also re-throw the exception if asked to 336 if ((throwWhen & THROW_ON_ERROR) == THROW_ON_ERROR) 337 { 338 // Note: re-throw the SAME exception, not a new one! 339 throw exception; 340 } 341 } 342 343 /** 344 * Implementation of error; calls logMsg with info contained in exception. 345 * Only ever throws an exception itself if asked to or if loggers are bad. 346 * Note that this may cause unusual behavior since we may not actually 347 * re-throw the exception, even though it was 'fatal'. 348 * 349 * @param exception provided by Transformer 350 * @exception TransformerException thrown only if asked to or if loggers are bad 351 */ fatalError(TransformerException exception)352 public void fatalError(TransformerException exception) throws TransformerException 353 { 354 355 // Increment counter, save the exception, and log what we got 356 counters[TYPE_FATALERROR]++; 357 358 String exInfo = getTransformerExceptionInfo(exception); 359 360 setLastItem(exInfo); 361 362 // Log or validate the exception 363 logOrCheck(TYPE_FATALERROR, "fatalError", exInfo); 364 365 // Also re-throw the exception if asked to 366 if ((throwWhen & THROW_ON_FATAL) == THROW_ON_FATAL) 367 { 368 // Note: re-throw the SAME exception, not a new one! 369 throw exception; 370 } 371 } 372 373 374 /** 375 * Worker method to either log or call check* for this event. 376 * A simple way to validate for any kind of event. 377 * 378 * @param type of message (warning/error/fatalerror) 379 * @param desc description of this kind of message 380 * @param exInfo String representation of current exception 381 */ logOrCheck(int type, String desc, String exInfo)382 protected void logOrCheck(int type, String desc, String exInfo) 383 { 384 String tmp = getQuickCounters() + " " + desc; 385 // Either log the exception or call checkPass/checkFail 386 // as requested by setExpected for this type 387 if (ITEM_DONT_CARE == expected[type]) 388 { 389 // We don't care about this, just log it 390 logger.logMsg(level, desc + " threw: " + exInfo); 391 } 392 else if (ITEM_CHECKFAIL == expected[type]) 393 { 394 // We shouldn't have been called here, so fail 395 logger.checkFail(desc + " threw-unexpected: " + exInfo); 396 } 397 else if (exInfo.indexOf(expected[type]) > -1) 398 { 399 // We got a warning the user expected, so pass 400 logger.checkPass(desc + " threw-matching: " + exInfo); 401 // Also reset this counter 402 //@todo needswork: this is very state-dependent, and 403 // might not be what the user expects, but at least it 404 // won't give lots of extra false fails or passes 405 expected[type] = ITEM_DONT_CARE; 406 } 407 else 408 { 409 // We got a warning the user didn't expect, so fail 410 logger.checkFail(desc + " threw-notmatching: " + exInfo); 411 // Also reset this counter 412 expected[type] = ITEM_DONT_CARE; 413 } 414 } 415 } 416