001/* Base class for displaying exceptions, warnings, and messages. 002 003 Copyright (c) 2003-2016 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.util; 028 029import java.io.BufferedReader; 030import java.io.IOException; 031import java.io.InputStreamReader; 032import java.lang.ref.WeakReference; 033import java.util.Locale; 034 035/////////////////////////////////////////////////////////////////// 036//// MessageHandler 037 038/** 039 This is a class that is used to report errors. It provides a 040 set of static methods that are called to report errors. However, the 041 actual reporting of the errors is deferred to an instance of this class 042 that is set using the setMessageHandler() method. Normally there 043 is only one instance, set up by the application, so the class is 044 a singleton. But this is not enforced. 045 <p> 046 This base class simply writes the errors to System.err. 047 When an applet or application starts up, it may wish to set a subclass 048 of this class as the message handler, to allow a nicer way of 049 reporting errors. For example, a Swing application will probably 050 want to report errors in a dialog box, using for example 051 the derived class GraphicalMessageHandler. 052 053 <p>See ptolemy.gui.GraphicalMessageHandler</p> 054 055 @author Edward A. Lee, Steve Neuendorffer, Elaine Cheong 056 @version $Id$ 057 @since Ptolemy II 4.0 058 @Pt.ProposedRating Green (cxh) 059 @Pt.AcceptedRating Green (cxh) 060 */ 061public class MessageHandler implements Thread.UncaughtExceptionHandler { 062 063 /** Create a MessageHandler. 064 */ 065 public MessageHandler() { 066 // Note that kepler/loader/src/org/kepler/ExecutionEngine.java 067 // invokes new MessageHandler(). 068 Thread.setDefaultUncaughtExceptionHandler(this); 069 } 070 071 /////////////////////////////////////////////////////////////////// 072 //// public methods //// 073 074 /** Defer to the set message handler to show the specified 075 * error message. 076 * 077 * <p>Note that within Ptolemy, most user code should not call 078 * this method directly. Instead, throw an exception, which will 079 * be caught by the system elsewhere and include information 080 * about what object caused the error. 081 * @param info The message. 082 */ 083 public static void error(String info) { 084 _handler._error(info); 085 } 086 087 /** Defer to the set message handler to 088 * show the specified message and throwable information. 089 * If the throwable is an instance of CancelException, then it 090 * is not shown. By default, only the message of the throwable 091 * is thrown. The stack trace information is only shown if the 092 * user clicks on the "Display Stack Trace" button. 093 * 094 * <p>Note that within Ptolemy, most user code should not call 095 * this method directly. Instead, throw an exception, which will 096 * be caught by the system elsewhere and include information 097 * about what object caused the error. 098 * @param info The message. 099 * @param throwable The throwable. 100 * @see CancelException 101 */ 102 public static void error(String info, Throwable throwable) { 103 // Sometimes you find that errors are reported multiple times. 104 // To find out who is calling this method, uncomment the following. 105 // System.out.println("------ reporting error:" + throwable); 106 // throwable.printStackTrace(); 107 // System.out.println("------ called from:"); 108 // (new Exception()).printStackTrace(); 109 try { 110 _handler._error(info, throwable); 111 } catch (Throwable throwable2) { 112 // An applet was throwing an exception while handling 113 // the error - so we print the original message if _error() fails. 114 if (_handler instanceof SimpleMessageHandler) { 115 throw new RuntimeException(throwable); 116 } else { 117 System.err.println("Internal Error, exception thrown while " 118 + "handling error: \"" + info + "\"\n"); 119 throwable.printStackTrace(); 120 System.err.println("Internal Error:\n"); 121 throwable2.printStackTrace(); 122 } 123 } 124 } 125 126 /** Return the message handler instance that is used by the static 127 * methods in this class. 128 * @return The message handler. 129 * @see #setMessageHandler(MessageHandler) 130 */ 131 public static MessageHandler getMessageHandler() { 132 return _handler; 133 } 134 135 /** Return true if the current process is a non-interactive session. 136 * If the nightly build is running, then return true. 137 * 138 * <p>This method merely checks to see if the 139 * "ptolemy.ptII.isRunningNightlyBuild" property exists and is not empty 140 * or if the "ptolemy.ptII.batchMode" property exists and is not empty 141 * and the property "ptolemyII.ptII.testingMessageHandler" is not set.</p> 142 * 143 * <p>To run the test suite in the Nightly Build mode, use</p> 144 * <pre> 145 * make nightly 146 * </pre> 147 * @return True if the nightly build is running. 148 */ 149 public static boolean isNonInteractive() { 150 // This method is necessary because Ptolemy can download models 151 // and files from websites. It is a best practice to prompt the user 152 // and ask them if they actually want to download the resource. 153 // However, code like the nightly build and the actor indexing code 154 // should run without user interaction, so we set a property 155 // when running non-interactive, batch mode code. 156 if ((StringUtilities.getProperty("ptolemy.ptII.isRunningNightlyBuild") 157 .length() > 0 158 || StringUtilities.getProperty("ptolemy.ptII.batchMode") 159 .length() > 0) 160 && StringUtilities 161 .getProperty("ptolemy.ptII.testingMessageHandler") 162 .length() == 0) { 163 return true; 164 } 165 166 return false; 167 } 168 169 /** Defer to the set message handler to show the specified 170 * message. An implementation may block, for example with a modal dialog. 171 * @param info The message. 172 * @see #status(String) 173 */ 174 public static void message(String info) { 175 _handler._message(info); 176 } 177 178 /** Set the message handler instance that is used by the static 179 * methods in this class. If the given handler is null, then 180 * do nothing. 181 * @param handler The message handler. 182 * @see #getMessageHandler() 183 */ 184 public static void setMessageHandler(MessageHandler handler) { 185 if (handler != null) { 186 _handler = handler; 187 } 188 } 189 190 /** Set the specified status handler, replacing any previously 191 * set handler. 192 * @param handler The handler, or null to set no handler. 193 * @see #status(String) 194 */ 195 public static void setStatusHandler(StatusHandler handler) { 196 _statusHandler = new WeakReference<StatusHandler>(handler); 197 } 198 199 /** Return a short description of the throwable. 200 * @param throwable The throwable 201 * @return If the throwable is an Exception, return "Exception", 202 * if it is an Error, return "Error", if it is a Throwable, return 203 * "Throwable". 204 */ 205 public static String shortDescription(Throwable throwable) { 206 String throwableType = null; 207 208 if (throwable instanceof Exception) { 209 throwableType = "Exception"; 210 } else if (throwable instanceof Error) { 211 throwableType = "Error"; 212 } else { 213 throwableType = "Throwable"; 214 } 215 216 return throwableType; 217 } 218 219 /** Display a status message to the user. 220 * This method is intended for keeping users informed of what is being done. 221 * The message may be displayed for a very short time and may be cleared after some time. 222 * This method is not intended for logging or for persistent messages, nor for messages 223 * that require some acknowledgement from the user. 224 * If a StatusHandler has been registered using #addStatusHandler(StatusHandler), 225 * then delegate displaying the message to that status handler. 226 * Otherwise, display on standard out. 227 * @param message The message to display. 228 * @see #message(String) 229 */ 230 public static void status(String message) { 231 if (_statusHandler != null && _statusHandler.get() != null) { 232 _statusHandler.get().status(message); 233 } else { 234 System.out.println(message); 235 } 236 } 237 238 /** Handle uncaught exceptions in a standard way. 239 * @param thread The thread throwing the exception. 240 * @param exception The exception. 241 */ 242 @Override 243 public void uncaughtException(Thread thread, Throwable exception) { 244 _error("UNCAUGHT EXCEPTION: " + exception.getMessage(), exception); 245 } 246 247 /** Defer to the set message handler to 248 * show the specified message in a modal dialog. If the user 249 * clicks on the "Cancel" button, then throw an exception. 250 * This gives the user the option of not continuing the 251 * execution, something that is particularly useful if continuing 252 * execution will result in repeated warnings. 253 * 254 * <p>Note that within Ptolemy, most user code should not call 255 * this method directly. Instead, throw an exception, which will 256 * be caught by the system elsewhere and include information 257 * about what object caused the warning. 258 * 259 * @param info The message. 260 * @exception CancelException If the user clicks on the "Cancel" button. 261 */ 262 public static void warning(String info) throws CancelException { 263 _handler._warning(info); 264 } 265 266 /** Show the specified message and throwable information 267 * in a modal dialog. If the user 268 * clicks on the "Cancel" button, then throw an exception. 269 * This gives the user the option of not continuing the 270 * execution, something that is particularly useful if continuing 271 * execution will result in repeated warnings. 272 * By default, only the message of the throwable 273 * is thrown. The stack trace information is only shown if the 274 * user clicks on the "Display Stack Trace" button. 275 * 276 * <p>Note that within Ptolemy, most user code should not call 277 * this method directly. Instead, throw an exception, which will 278 * be caught by the system elsewhere and include information 279 * about what object caused the warning. 280 * 281 * @param info The message. 282 * @param throwable The throwable associated with this warning. 283 * @exception CancelException If the user clicks on the "Cancel" button. 284 */ 285 public static void warning(String info, Throwable throwable) 286 throws CancelException { 287 _handler._warning(info + ": " + throwable.getMessage(), throwable); 288 } 289 290 /** Ask the user a yes/no question, and return true if the answer 291 * is yes. This method returns true without asking the user if 292 * the property "ptolemy.ptII.isRunningNightlyBuild" is set. 293 * In the regression tests, there is no user to answer the question. 294 * @param question The yes/no question. 295 * @return True if the answer is yes. 296 */ 297 public static boolean yesNoQuestion(String question) { 298 if (!isNonInteractive()) { 299 return _handler._yesNoQuestion(question); 300 } else { 301 return true; 302 } 303 } 304 305 /** Ask the user a yes/no/cancel question, and return true if the 306 * answer is yes. If the user clicks on the "Cancel" button, 307 * then throw an exception. 308 * 309 * @param question The yes/no/cancel question. 310 * @return True if the answer is yes. 311 * @exception ptolemy.util.CancelException If the user clicks on 312 * the "Cancel" button. 313 */ 314 public static boolean yesNoCancelQuestion(String question) 315 throws ptolemy.util.CancelException { 316 return yesNoCancelQuestion(question, "Yes", "No", "Cancel"); 317 } 318 319 /** Ask the user a question with three possible answers; 320 * return true if the answer is the first one and false if 321 * the answer is the second one; throw an exception if the 322 * user selects the third one. 323 * 324 * @param question The question. 325 * @param trueOption The option for which to return true. 326 * @param falseOption The option for which to return false. 327 * @param exceptionOption The option for which to throw an exception. 328 * @return True if the answer is the first option, false if it is the second. 329 * @exception ptolemy.util.CancelException If the user selects the third option. 330 */ 331 public static boolean yesNoCancelQuestion(String question, 332 String trueOption, String falseOption, String exceptionOption) 333 throws ptolemy.util.CancelException { 334 if (!isNonInteractive()) { 335 return _handler._yesNoCancelQuestion(question, trueOption, 336 falseOption, exceptionOption); 337 } else { 338 return true; 339 } 340 } 341 342 /////////////////////////////////////////////////////////////////// 343 //// protected methods //// 344 345 /** Show the specified error message. 346 * @param info The message. 347 */ 348 protected void _error(String info) { 349 System.err.println(info); 350 } 351 352 /** Show the specified message and throwable information. 353 * If the throwable is an instance of CancelException, then nothing 354 * is not shown. By default, only the message of the exception 355 * is thrown. The stack trace information is only shown if the 356 * user clicks on the "Display Stack Trace" button. 357 * 358 * @param info The message. 359 * @param throwable The throwable. 360 * @see CancelException 361 */ 362 protected void _error(String info, Throwable throwable) { 363 if (throwable instanceof CancelException) { 364 return; 365 } 366 367 System.err.println(info); 368 throwable.printStackTrace(); 369 } 370 371 /** Display the warning message. In this base class, the 372 * the default handler merely prints the warning to stderr. 373 * @param info The message. 374 */ 375 protected void _message(String info) { 376 System.err.println(info); 377 } 378 379 /** Show the specified message. In this base class, the message 380 * is printed to standard error. 381 * <p>Derived classes might show the specified message in a modal 382 * dialog. If the user clicks on the "Cancel" button, then throw 383 * an exception. This gives the user the option of not 384 * continuing the execution, something that is particularly 385 * useful if continuing execution will result in repeated 386 * warnings. 387 * @param info The message. 388 * @exception CancelException If the user clicks on the "Cancel" button. 389 */ 390 protected void _warning(String info) throws CancelException { 391 _error(info); 392 } 393 394 /** Display the warning message and throwable information. In 395 * this base class, the the default handler merely prints the 396 * warning to stderr. If the user clicks on the "Cancel" button, 397 * then throw an exception. This gives the user the option of 398 * not continuing the execution, something that is particularly 399 * useful if continuing execution will result in repeated 400 * warnings. By default, only the message of the throwable is 401 * thrown. The stack trace information is only shown if the user 402 * clicks on the "Display Stack Trace" button. 403 * @param info The message. 404 * @param throwable The Throwable. 405 * @exception CancelException If the user clicks on the "Cancel" button. 406 */ 407 protected void _warning(String info, Throwable throwable) 408 throws CancelException { 409 _error(info, throwable); 410 } 411 412 /** Ask the user a yes/no question, and return true if the answer 413 * is yes. In this base class, this prints the question on standard 414 * output and looks for the reply on standard input. 415 * @param question The yes/no question to be asked. 416 * @return True if the answer is yes. 417 */ 418 protected boolean _yesNoQuestion(String question) { 419 System.out.print(question); 420 System.out.print(" (yes or no) "); 421 422 BufferedReader stdIn = new BufferedReader( 423 new InputStreamReader(System.in)); 424 425 try { 426 String reply = stdIn.readLine(); 427 428 if (reply == null) { 429 return false; 430 } else if (reply.trim().toLowerCase(Locale.getDefault()) 431 .equals("yes")) { 432 return true; 433 } 434 } catch (IOException ex) { 435 } 436 437 return false; 438 } 439 440 /** Ask the user a question with three possible answers; 441 * return true if the answer is the first one and false if 442 * the answer is the second one; throw an exception if the 443 * user selects the third one. 444 * @param question The question. 445 * @param trueOption The option for which to return true. 446 * @param falseOption The option for which to return false. 447 * @param exceptionOption The option for which to throw an exception. 448 * @return True if the answer is the first option, false if it is the second. 449 * @exception ptolemy.util.CancelException If the user selects the third option. 450 */ 451 protected boolean _yesNoCancelQuestion(String question, String trueOption, 452 String falseOption, String exceptionOption) 453 throws ptolemy.util.CancelException { 454 System.out.print(question + " (" + trueOption + " or " + falseOption 455 + " or " + exceptionOption + ") "); 456 457 BufferedReader stdIn = new BufferedReader( 458 new InputStreamReader(System.in)); 459 460 try { 461 String reply = stdIn.readLine(); 462 463 if (reply == null) { 464 return false; 465 } else { 466 if (reply.trim().toLowerCase(Locale.getDefault()) 467 .equals(trueOption.toLowerCase(Locale.getDefault()))) { 468 return true; 469 } else if (reply.trim().toLowerCase(Locale.getDefault()).equals( 470 exceptionOption.toLowerCase(Locale.getDefault()))) { 471 throw new ptolemy.util.CancelException( 472 "Cancelled: " + question); 473 } 474 } 475 } catch (IOException ex) { 476 } 477 478 return false; 479 } 480 481 /////////////////////////////////////////////////////////////////// 482 //// private variables //// 483 484 /** The message handler. */ 485 private static MessageHandler _handler = new MessageHandler(); 486 487 /** The status handlers, if any. */ 488 private static transient WeakReference<StatusHandler> _statusHandler; 489}