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