001/** 002 * Copyright 2005-2018 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.krad.exception; 017 018import java.io.PrintWriter; 019import java.io.StringWriter; 020import java.net.UnknownHostException; 021import java.util.HashMap; 022import java.util.Map; 023 024import org.apache.log4j.Logger; 025import org.kuali.rice.core.api.CoreApiServiceLocator; 026import org.kuali.rice.core.api.exception.KualiException; 027 028/** 029 * Contains the exception incident information, exception, form and 030 * session user. It is constructed and saved into the HTTP Request for passing to the 031 * jsp when an exception occurs. 032 * 033 * @author Kuali Rice Team (rice.collab@kuali.org) 034 */ 035public class ExceptionIncident implements KualiExceptionIncident { 036 private static final Logger LOG = Logger.getLogger(ExceptionIncident.class); 037 public static final String GENERIC_SYSTEM_ERROR_MESSAGE = "The system has" + 038 " encountered an error and is unable to complete your request at this time."+ 039 " Please provide more information regarding this error by completing"+ 040 " this Incident Report."; 041 042 /** 043 * Basic exception information is initialized and contained in this class instance. 044 * Additional setting and other information can be added when exception is caught. 045 * Also, an JSP is displayed to collect additional user information and the returned 046 * parameters from the JSP can be used to initialize this class instance for 047 * reporting. 048 * <p>Note: The mechanism for passing data to and receiving data from the JSP uses 049 * java.util.Map. Therefore, the exception is not passed to JSP using HttpRequest. 050 * But rather a Map instance. 051 */ 052 protected Map<String, String> properties=new HashMap<String, String>(); 053 054 /** 055 * This constructs list of key-value pairs from the caught exception and current 056 * settings. 057 * 058 * @param exception Caught exception 059 * @param properties Input information when the exception is caught 060 * <p>Example: 061 * <ul> 062 * <li>DOCUMENT_ID</li> 063 * <li>USER_EMAIL</li> 064 * <li>USER_NAME</li> 065 * <li>COMPONENT_NAME</li> 066 * </ul> 067 */ 068 public ExceptionIncident(Exception exception, 069 Map<String,String> properties) { 070 if (LOG.isTraceEnabled()) { 071 String message=String.format("ENTRY %s%n%s", 072 (exception==null)?"null":exception.toString(), 073 (properties==null)?"":properties.toString()); 074 LOG.trace(message); 075 } 076 077 initialize(exception, properties); 078 079 if (LOG.isTraceEnabled()) { 080 String message=String.format("EXIT %s", this.properties); 081 LOG.trace(message); 082 } 083 084 } 085 086 /** 087 * This constructs an instance of this class from list of name-value pairs. 088 * 089 * @param inputs List of exception information such as 090 * <ul> 091 * <li>DOCUMENT_ID - If it's document form</li> 092 * <li>USER_EMAIL - Session user email</li> 093 * <li>USER_NAME - Session user name</li> 094 * <li>COMPONENT_NAME - Document or lookup or inquiry form</li> 095 * attribute of GlobalVariables</li> 096 * <li>EXCEPTION_REPORT_SUBJECT - Exception error message and current settings</li> 097 * <li>EXCEPTION_MESSAGE - Exception error message</li> 098 * <li>STACK_TRACE - Exception stack trace</li> 099 * <li>DESCRIPTION - Information input by user or blank</li> 100 * </ul> 101 */ 102 public ExceptionIncident(Map<String, String> inputs) { 103 104 this.properties=inputs; 105 106 } 107 108 /** 109 * This method create and populate the internal properties parameter. 110 * 111 * @param thrownException The caught exception 112 * @param inputs Input information when the exception is caught 113 * <p>Example: 114 * <ul> 115 * <li>DOCUMENT_ID</li> 116 * <li>USER_EMAIL</li> 117 * <li>USER_NAME</li> 118 * <li>COMPONENT_NAME</li> 119 * </ul> 120 */ 121 private void initialize(Exception thrownException, Map<String, String> inputs) { 122 if (LOG.isTraceEnabled()) { 123 String lm=String.format("ENTRY %s%n%s", 124 thrownException.getMessage(), 125 (inputs==null)?"null":inputs.toString()); 126 LOG.trace(lm); 127 } 128 129 properties=new HashMap<String, String>(); 130 // Add all inputs 131 if (inputs != null) { 132 properties.putAll(inputs); 133 } 134 // Add all exception information 135 properties.putAll(getExceptionInfo(thrownException)); 136 137 if (LOG.isTraceEnabled()) { 138 String lm=String.format("EXIT %s", properties.toString()); 139 LOG.trace(lm); 140 } 141 } 142 143 /** 144 * This method return list of required information provided by the caught exception. 145 * 146 * @return 147 * <p>Example: 148 * <code> 149 * exceptionSubject, Caught exception message and settings information 150 * exceptionMessage, Caught exception message 151 * displayMessage, Either exception error message or generic exception error message 152 * stackTrace, Exception stack trace here 153 * </code> 154 * 155 */ 156 private Map<String, String> getExceptionInfo(Exception exception) { 157 if (LOG.isTraceEnabled()) { 158 String message=String.format("ENTRY"); 159 LOG.trace(message); 160 } 161 162 Map<String, String> map=new HashMap<String, String>(); 163 map.put(EXCEPTION_REPORT_SUBJECT, createReportSubject(exception)); 164 map.put(EXCEPTION_MESSAGE, exception.getMessage()); 165 map.put(DISPLAY_MESSAGE, getDisplayMessage(exception)); 166 map.put(STACK_TRACE, getExceptionStackTrace(exception)); 167 if(exception instanceof KualiException){ 168 boolean hideIncidentReport = ((KualiException) exception).isHideIncidentReport(); 169 map.put(EXCEPTION_HIDE_INCIDENT_REPORT, String.valueOf(hideIncidentReport)); 170 }else{ 171 map.put(EXCEPTION_HIDE_INCIDENT_REPORT, String.valueOf(false)); 172 } 173 174 if (LOG.isTraceEnabled()) { 175 String message=String.format("ENTRY %s", map.toString()); 176 LOG.trace(message); 177 } 178 179 return map; 180 } 181 182 /** 183 * This method compose the exception information that includes 184 * <ul> 185 * <li>environment - Application environment</li> 186 * <li>componentName- Document or lookup or inquiry form</li> 187 * <li>errorMessage - Exception error message</li> 188 * </ul> 189 * <p>Example; 190 * <code> 191 * kr-dev:SomeForm:Some error message 192 * </code> 193 * 194 * @param exception The caught exception 195 * @return report subject 196 */ 197 private String createReportSubject(Exception exception) { 198 if (LOG.isTraceEnabled()) { 199 String lm=String.format("ENTRY"); 200 LOG.trace(lm); 201 } 202 String app = CoreApiServiceLocator.getKualiConfigurationService(). 203 getPropertyValueAsString("application.id"); 204 String env= CoreApiServiceLocator.getKualiConfigurationService(). 205 getPropertyValueAsString("environment"); 206 String format="%s:%s:%s:%s"; 207 String componentName=properties.get(COMPONENT_NAME); 208 String subject=String.format(format, 209 app, 210 env, 211 (componentName==null)?"":componentName, 212 exception.getMessage()); 213 214 if (LOG.isTraceEnabled()) { 215 String lm=String.format("EXIT %s", subject); 216 LOG.trace(lm); 217 } 218 219 return subject; 220 } 221 222 /** 223 * This method compose the exception information that includes 224 * <ul> 225 * <li>documentId - If it's document form</li> 226 * <li>userEmail - Session user email</li> 227 * <li>userName - Session user name</li> 228 * <li>component - Document or lookup or inquiry form</li> 229 * <li>description - Information input by user or blank</li> 230 * <li>errorMessage - Exception error message</li> 231 * <li>stackTrace - Exception stack trace</li> 232 * </ul> 233 * <p>Example; 234 * <code> 235 * documentId: 2942084 236 * userEmail: someone@somewhere 237 * userName: some name 238 * description: Something went wrong! 239 * component: document 240 * errorMessage: Some error message 241 * stackTrace: Exception stack trace here 242 * </code> 243 * 244 * @return report message body 245 */ 246 private String createReportMessage() { 247 if (LOG.isTraceEnabled()) { 248 String lm=String.format("ENTRY"); 249 LOG.trace(lm); 250 } 251 //KULRICE-12280: Adding server info to the message of the report 252 java.net.InetAddress addr = null; 253 try { 254 addr = java.net.InetAddress.getLocalHost(); 255 } catch (UnknownHostException e) { 256 LOG.warn("Unable to get the localHost to inclue in exeception incident", e); 257 } 258 String documentId=properties.get(DOCUMENT_ID); 259 String userEmail=properties.get(USER_EMAIL); 260 String uuid=properties.get(UUID); 261 String description=properties.get(DESCRIPTION); 262 String component=properties.get(COMPONENT_NAME); 263 String serverInfo = addr.toString(); 264 String exceptionMessage=properties.get(EXCEPTION_MESSAGE); 265 String stackTrace=properties.get(STACK_TRACE); 266 String format="Document Id: %s%n"+ 267 "User Email: %s%n"+ 268 "Person User Identifier: %s%n"+ 269 "User Input: %s%n"+ 270 "component: %s%n"+ 271 "Server Info: %s%n"+ 272 "errorMessage: %s%n"+ 273 "%s%n"; 274 String message=String.format(format, 275 (documentId==null)?"":documentId, 276 (userEmail==null)?"":userEmail, 277 (uuid==null)?"":uuid, 278 (description==null)?"":description, 279 (component==null)?"":component, 280 (serverInfo==null)?"":serverInfo, 281 (exceptionMessage==null)?"":exceptionMessage, 282 (stackTrace==null)?"":stackTrace); 283 284 if (LOG.isTraceEnabled()) { 285 String lm=String.format("EXIT %s", message); 286 LOG.trace(lm); 287 } 288 289 return message; 290 } 291 292 /** 293 * This method return the thrown exception stack trace as string. 294 * 295 * @param thrownException 296 * @return stack trace 297 */ 298 public String getExceptionStackTrace(Exception thrownException) { 299 if (LOG.isTraceEnabled()) { 300 String lm=String.format("ENTRY"); 301 LOG.trace(lm); 302 } 303 304 StringWriter wrt=new StringWriter(); 305 PrintWriter pw=new PrintWriter(wrt); 306 thrownException.printStackTrace(pw); 307 pw.flush(); 308 String stackTrace=wrt.toString(); 309 try { 310 wrt.close(); 311 pw.close(); 312 } catch (Exception e) { 313 LOG.trace(e.getMessage(), e); 314 } 315 316 if (LOG.isTraceEnabled()) { 317 String lm=String.format("EXIT %s", stackTrace); 318 LOG.trace(lm); 319 } 320 321 return stackTrace; 322 } 323 324 /** 325 * This overridden method return the exception if the ixception type is in the 326 * defined list. Otherwise, it returns the GENERIC_SYSTEM_ERROR_MESSAGE. 327 * 328 * @see org.kuali.rice.krad.exception.KualiExceptionIncident#getDisplayMessage(Exception) 329 */ 330 public String getDisplayMessage(Exception exception) { 331 if (LOG.isTraceEnabled()) { 332 String message=String.format("ENTRY %s", exception.getMessage()); 333 LOG.trace(message); 334 } 335 336 // Create the display message 337 String displayMessage; 338 if (exception instanceof KualiException) { 339 displayMessage=exception.getMessage(); 340 } else { 341 //KULRICE:12361-Added a more detailed message on exception incidents 342 displayMessage=GENERIC_SYSTEM_ERROR_MESSAGE + "<br> Error Details: " + exception.getMessage(); 343 } 344 345 if (LOG.isTraceEnabled()) { 346 String message=String.format("EXIT %s", displayMessage); 347 LOG.trace(message); 348 } 349 350 return displayMessage; 351 } 352 353 /** 354 * This overridden method returns value of the found property key. Except the 355 * property EXCEPTION_REPORT_MESSAGE 356 * 357 * @see org.kuali.rice.krad.exception.KualiExceptionIncident#getProperty(java.lang.String) 358 */ 359 public String getProperty(String key) { 360 if (LOG.isTraceEnabled()) { 361 String message=String.format("ENTRY %s", key); 362 LOG.trace(message); 363 } 364 365 String value; 366 if (key.equals(EXCEPTION_REPORT_MESSAGE) && !properties.containsKey(key)) { 367 value=createReportMessage(); 368 properties.put(EXCEPTION_REPORT_MESSAGE, value); 369 } else { 370 value=properties.get(key); 371 } 372 373 if (LOG.isTraceEnabled()) { 374 String message=String.format("EXIT %s", value); 375 LOG.trace(message); 376 } 377 378 return value; 379 } 380 381 /** 382 * This overridden method return current internal properties. 383 * 384 * @see org.kuali.rice.krad.exception.KualiExceptionIncident#toProperties() 385 */ 386 public Map<String, String> toProperties() { 387 if (LOG.isTraceEnabled()) { 388 String message=String.format("ENTRY"); 389 LOG.trace(message); 390 } 391 392 if (LOG.isTraceEnabled()) { 393 String message=String.format("EXIT %s", properties.toString()); 394 LOG.trace(message); 395 } 396 397 return properties; 398 } 399}