1 /**
2 * Copyright 2005-2014 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.util.HashMap;
21 import java.util.Map;
22
23 import org.apache.log4j.Logger;
24 import org.kuali.rice.core.api.CoreApiServiceLocator;
25 import org.kuali.rice.core.api.exception.KualiException;
26
27 /**
28 * Contains the exception incident information, exception, form and
29 * session user. It is constructed and saved into the HTTP Request for passing to the
30 * jsp when an exception occurs.
31 *
32 * @author Kuali Rice Team (rice.collab@kuali.org)
33 */
34 public class ExceptionIncident implements KualiExceptionIncident {
35 private static final Logger LOG = Logger.getLogger(ExceptionIncident.class);
36 public static final String GENERIC_SYSTEM_ERROR_MESSAGE = "The system has" +
37 " encountered an error and is unable to complete your request at this time."+
38 " Please provide more information regarding this error by completing"+
39 " this Incident Report.";
40
41 /**
42 * Basic exception information is initialized and contained in this class instance.
43 * Additional setting and other information can be added when exception is caught.
44 * Also, an JSP is displayed to collect additional user information and the returned
45 * parameters from the JSP can be used to initialize this class instance for
46 * reporting.
47 * <p>Note: The mechanism for passing data to and receiving data from the JSP uses
48 * java.util.Map. Therefore, the exception is not passed to JSP using HttpRequest.
49 * But rather a Map instance.
50 */
51 protected Map<String, String> properties=new HashMap<String, String>();
52
53 /**
54 * This constructs list of key-value pairs from the caught exception and current
55 * settings.
56 *
57 * @param exception Caught exception
58 * @param properties Input information when the exception is caught
59 * <p>Example:
60 * <ul>
61 * <li>DOCUMENT_ID</li>
62 * <li>USER_EMAIL</li>
63 * <li>USER_NAME</li>
64 * <li>COMPONENT_NAME</li>
65 * </ul>
66 */
67 public ExceptionIncident(Exception exception,
68 Map<String,String> properties) {
69 if (LOG.isTraceEnabled()) {
70 String message=String.format("ENTRY %s%n%s",
71 (exception==null)?"null":exception.toString(),
72 (properties==null)?"":properties.toString());
73 LOG.trace(message);
74 }
75
76 initialize(exception, properties);
77
78 if (LOG.isTraceEnabled()) {
79 String message=String.format("EXIT %s", this.properties);
80 LOG.trace(message);
81 }
82
83 }
84
85 /**
86 * This constructs an instance of this class from list of name-value pairs.
87 *
88 * @param inputs List of exception information such as
89 * <ul>
90 * <li>DOCUMENT_ID - If it's document form</li>
91 * <li>USER_EMAIL - Session user email</li>
92 * <li>USER_NAME - Session user name</li>
93 * <li>COMPONENT_NAME - Document or lookup or inquiry form</li>
94 * attribute of GlobalVariables</li>
95 * <li>EXCEPTION_REPORT_SUBJECT - Exception error message and current settings</li>
96 * <li>EXCEPTION_MESSAGE - Exception error message</li>
97 * <li>STACK_TRACE - Exception stack trace</li>
98 * <li>DESCRIPTION - Information input by user or blank</li>
99 * </ul>
100 */
101 public ExceptionIncident(Map<String, String> inputs) {
102
103 this.properties=inputs;
104
105 }
106
107 /**
108 * This method create and populate the internal properties parameter.
109 *
110 * @param thrownException The caught exception
111 * @param inputs Input information when the exception is caught
112 * <p>Example:
113 * <ul>
114 * <li>DOCUMENT_ID</li>
115 * <li>USER_EMAIL</li>
116 * <li>USER_NAME</li>
117 * <li>COMPONENT_NAME</li>
118 * </ul>
119 */
120 private void initialize(Exception thrownException, Map<String, String> inputs) {
121 if (LOG.isTraceEnabled()) {
122 String lm=String.format("ENTRY %s%n%s",
123 thrownException.getMessage(),
124 (inputs==null)?"null":inputs.toString());
125 LOG.trace(lm);
126 }
127
128 properties=new HashMap<String, String>();
129 // Add all inputs
130 if (inputs != null) {
131 properties.putAll(inputs);
132 }
133 // Add all exception information
134 properties.putAll(getExceptionInfo(thrownException));
135
136 if (LOG.isTraceEnabled()) {
137 String lm=String.format("EXIT %s", properties.toString());
138 LOG.trace(lm);
139 }
140 }
141
142 /**
143 * This method return list of required information provided by the caught exception.
144 *
145 * @return
146 * <p>Example:
147 * <code>
148 * exceptionSubject, Caught exception message and settings information
149 * exceptionMessage, Caught exception message
150 * displayMessage, Either exception error message or generic exception error message
151 * stackTrace, Exception stack trace here
152 * </code>
153 *
154 */
155 private Map<String, String> getExceptionInfo(Exception exception) {
156 if (LOG.isTraceEnabled()) {
157 String message=String.format("ENTRY");
158 LOG.trace(message);
159 }
160
161 Map<String, String> map=new HashMap<String, String>();
162 map.put(EXCEPTION_REPORT_SUBJECT, createReportSubject(exception));
163 map.put(EXCEPTION_MESSAGE, exception.getMessage());
164 map.put(DISPLAY_MESSAGE, getDisplayMessage(exception));
165 map.put(STACK_TRACE, getExceptionStackTrace(exception));
166 if(exception instanceof KualiException){
167 boolean hideIncidentReport = ((KualiException) exception).isHideIncidentReport();
168 map.put(EXCEPTION_HIDE_INCIDENT_REPORT, String.valueOf(hideIncidentReport));
169 }else{
170 map.put(EXCEPTION_HIDE_INCIDENT_REPORT, String.valueOf(false));
171 }
172
173 if (LOG.isTraceEnabled()) {
174 String message=String.format("ENTRY %s", map.toString());
175 LOG.trace(message);
176 }
177
178 return map;
179 }
180
181 /**
182 * This method compose the exception information that includes
183 * <ul>
184 * <li>environment - Application environment</li>
185 * <li>componentName- Document or lookup or inquiry form</li>
186 * <li>errorMessage - Exception error message</li>
187 * </ul>
188 * <p>Example;
189 * <code>
190 * kr-dev:SomeForm:Some error message
191 * </code>
192 *
193 * @param exception The caught exception
194 * @return report subject
195 */
196 private String createReportSubject(Exception exception) {
197 if (LOG.isTraceEnabled()) {
198 String lm=String.format("ENTRY");
199 LOG.trace(lm);
200 }
201 String app = CoreApiServiceLocator.getKualiConfigurationService().
202 getPropertyValueAsString("application.id");
203 String env= CoreApiServiceLocator.getKualiConfigurationService().
204 getPropertyValueAsString("environment");
205 String format="%s:%s:%s:%s";
206 String componentName=properties.get(COMPONENT_NAME);
207 String subject=String.format(format,
208 app,
209 env,
210 (componentName==null)?"":componentName,
211 exception.getMessage());
212
213 if (LOG.isTraceEnabled()) {
214 String lm=String.format("EXIT %s", subject);
215 LOG.trace(lm);
216 }
217
218 return subject;
219 }
220
221 /**
222 * This method compose the exception information that includes
223 * <ul>
224 * <li>documentId - If it's document form</li>
225 * <li>userEmail - Session user email</li>
226 * <li>userName - Session user name</li>
227 * <li>component - Document or lookup or inquiry form</li>
228 * <li>description - Information input by user or blank</li>
229 * <li>errorMessage - Exception error message</li>
230 * <li>stackTrace - Exception stack trace</li>
231 * </ul>
232 * <p>Example;
233 * <code>
234 * documentId: 2942084
235 * userEmail: someone@somewhere
236 * userName: some name
237 * description: Something went wrong!
238 * component: document
239 * errorMessage: Some error message
240 * stackTrace: Exception stack trace here
241 * </code>
242 *
243 * @return report message body
244 */
245 private String createReportMessage() {
246 if (LOG.isTraceEnabled()) {
247 String lm=String.format("ENTRY");
248 LOG.trace(lm);
249 }
250
251 String documentId=properties.get(DOCUMENT_ID);
252 String userEmail=properties.get(USER_EMAIL);
253 String uuid=properties.get(UUID);
254 String description=properties.get(DESCRIPTION);
255 String component=properties.get(COMPONENT_NAME);
256 String exceptionMessage=properties.get(EXCEPTION_MESSAGE);
257 String stackTrace=properties.get(STACK_TRACE);
258 String format="Document Id: %s%n"+
259 "User Email: %s%n"+
260 "Person User Identifier: %s%n"+
261 "User Input: %s%n"+
262 "component: %s%n"+
263 "errorMessage: %s%n"+
264 "%s%n";
265 String message=String.format(format,
266 (documentId==null)?"":documentId,
267 (userEmail==null)?"":userEmail,
268 (uuid==null)?"":uuid,
269 (description==null)?"":description,
270 (component==null)?"":component,
271 (exceptionMessage==null)?"":exceptionMessage,
272 (stackTrace==null)?"":stackTrace);
273
274 if (LOG.isTraceEnabled()) {
275 String lm=String.format("EXIT %s", message);
276 LOG.trace(lm);
277 }
278
279 return message;
280 }
281
282 /**
283 * This method return the thrown exception stack trace as string.
284 *
285 * @param thrownException
286 * @return stack trace
287 */
288 public String getExceptionStackTrace(Exception thrownException) {
289 if (LOG.isTraceEnabled()) {
290 String lm=String.format("ENTRY");
291 LOG.trace(lm);
292 }
293
294 StringWriter wrt=new StringWriter();
295 PrintWriter pw=new PrintWriter(wrt);
296 thrownException.printStackTrace(pw);
297 pw.flush();
298 String stackTrace=wrt.toString();
299 try {
300 wrt.close();
301 pw.close();
302 } catch (Exception e) {
303 LOG.trace(e.getMessage(), e);
304 }
305
306 if (LOG.isTraceEnabled()) {
307 String lm=String.format("EXIT %s", stackTrace);
308 LOG.trace(lm);
309 }
310
311 return stackTrace;
312 }
313
314 /**
315 * This overridden method return the exception if the ixception type is in the
316 * defined list. Otherwise, it returns the GENERIC_SYSTEM_ERROR_MESSAGE.
317 *
318 * @see org.kuali.rice.krad.exception.KualiExceptionIncident#getDisplayMessage(Exception)
319 */
320 public String getDisplayMessage(Exception exception) {
321 if (LOG.isTraceEnabled()) {
322 String message=String.format("ENTRY %s", exception.getMessage());
323 LOG.trace(message);
324 }
325
326 // Create the display message
327 String displayMessage;
328 if (exception instanceof KualiException) {
329 displayMessage=exception.getMessage();
330 } else {
331 displayMessage=GENERIC_SYSTEM_ERROR_MESSAGE;
332 }
333
334 if (LOG.isTraceEnabled()) {
335 String message=String.format("EXIT %s", displayMessage);
336 LOG.trace(message);
337 }
338
339 return displayMessage;
340 }
341
342 /**
343 * This overridden method returns value of the found property key. Except the
344 * property EXCEPTION_REPORT_MESSAGE
345 *
346 * @see org.kuali.rice.krad.exception.KualiExceptionIncident#getProperty(java.lang.String)
347 */
348 public String getProperty(String key) {
349 if (LOG.isTraceEnabled()) {
350 String message=String.format("ENTRY %s", key);
351 LOG.trace(message);
352 }
353
354 String value;
355 if (key.equals(EXCEPTION_REPORT_MESSAGE) && !properties.containsKey(key)) {
356 value=createReportMessage();
357 properties.put(EXCEPTION_REPORT_MESSAGE, value);
358 } else {
359 value=properties.get(key);
360 }
361
362 if (LOG.isTraceEnabled()) {
363 String message=String.format("EXIT %s", value);
364 LOG.trace(message);
365 }
366
367 return value;
368 }
369
370 /**
371 * This overridden method return current internal properties.
372 *
373 * @see org.kuali.rice.krad.exception.KualiExceptionIncident#toProperties()
374 */
375 public Map<String, String> toProperties() {
376 if (LOG.isTraceEnabled()) {
377 String message=String.format("ENTRY");
378 LOG.trace(message);
379 }
380
381 if (LOG.isTraceEnabled()) {
382 String message=String.format("EXIT %s", properties.toString());
383 LOG.trace(message);
384 }
385
386 return properties;
387 }
388 }