1 /**
2 * Copyright 2005-2016 The Kuali Foundation
3 *
4 * Licensed under the Educational Community License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/ecl2.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.kuali.rice.krad.exception;
17
18 import java.io.PrintWriter;
19 import java.io.StringWriter;
20 import java.net.UnknownHostException;
21 import java.util.HashMap;
22 import java.util.Map;
23
24 import org.apache.log4j.Logger;
25 import org.kuali.rice.core.api.CoreApiServiceLocator;
26 import org.kuali.rice.core.api.exception.KualiException;
27
28 /**
29 * Contains the exception incident information, exception, form and
30 * session user. It is constructed and saved into the HTTP Request for passing to the
31 * jsp when an exception occurs.
32 *
33 * @author Kuali Rice Team (rice.collab@kuali.org)
34 */
35 public class ExceptionIncident implements KualiExceptionIncident {
36 private static final Logger LOG = Logger.getLogger(ExceptionIncident.class);
37 public static final String GENERIC_SYSTEM_ERROR_MESSAGE = "The system has" +
38 " encountered an error and is unable to complete your request at this time."+
39 " Please provide more information regarding this error by completing"+
40 " this Incident Report.";
41
42 /**
43 * Basic exception information is initialized and contained in this class instance.
44 * Additional setting and other information can be added when exception is caught.
45 * Also, an JSP is displayed to collect additional user information and the returned
46 * parameters from the JSP can be used to initialize this class instance for
47 * reporting.
48 * <p>Note: The mechanism for passing data to and receiving data from the JSP uses
49 * java.util.Map. Therefore, the exception is not passed to JSP using HttpRequest.
50 * But rather a Map instance.
51 */
52 protected Map<String, String> properties=new HashMap<String, String>();
53
54 /**
55 * This constructs list of key-value pairs from the caught exception and current
56 * settings.
57 *
58 * @param exception Caught exception
59 * @param properties Input information when the exception is caught
60 * <p>Example:
61 * <ul>
62 * <li>DOCUMENT_ID</li>
63 * <li>USER_EMAIL</li>
64 * <li>USER_NAME</li>
65 * <li>COMPONENT_NAME</li>
66 * </ul>
67 */
68 public ExceptionIncident(Exception exception,
69 Map<String,String> properties) {
70 if (LOG.isTraceEnabled()) {
71 String message=String.format("ENTRY %s%n%s",
72 (exception==null)?"null":exception.toString(),
73 (properties==null)?"":properties.toString());
74 LOG.trace(message);
75 }
76
77 initialize(exception, properties);
78
79 if (LOG.isTraceEnabled()) {
80 String message=String.format("EXIT %s", this.properties);
81 LOG.trace(message);
82 }
83
84 }
85
86 /**
87 * This constructs an instance of this class from list of name-value pairs.
88 *
89 * @param inputs List of exception information such as
90 * <ul>
91 * <li>DOCUMENT_ID - If it's document form</li>
92 * <li>USER_EMAIL - Session user email</li>
93 * <li>USER_NAME - Session user name</li>
94 * <li>COMPONENT_NAME - Document or lookup or inquiry form</li>
95 * attribute of GlobalVariables</li>
96 * <li>EXCEPTION_REPORT_SUBJECT - Exception error message and current settings</li>
97 * <li>EXCEPTION_MESSAGE - Exception error message</li>
98 * <li>STACK_TRACE - Exception stack trace</li>
99 * <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 }