1 /**
2 * Copyright 2005-2013 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 org.apache.log4j.Logger;
19 import org.kuali.rice.core.api.exception.KualiException;
20 import org.kuali.rice.krad.service.KRADServiceLocator;
21
22 import java.io.PrintWriter;
23 import java.io.StringWriter;
24 import java.util.HashMap;
25 import java.util.Map;
26
27 /**
28 * This class 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 */
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 exceptionNames List of Kuali exception for determining the display exception
60 * message
61 * @param properties Input information when the exception is caught
62 * <p>Example:
63 * <ul>
64 * <li>DOCUMENT_ID</li>
65 * <li>USER_EMAIL</li>
66 * <li>USER_NAME</li>
67 * <li>COMPONENT_NAME</li>
68 * </ul>
69 */
70 public ExceptionIncident(Exception exception,
71 Map<String,String> properties) {
72 if (LOG.isTraceEnabled()) {
73 String message=String.format("ENTRY %s%n%s",
74 (exception==null)?"null":exception.toString(),
75 (properties==null)?"":properties.toString());
76 LOG.trace(message);
77 }
78
79 initialize(exception, properties);
80
81 if (LOG.isTraceEnabled()) {
82 String message=String.format("EXIT %s", this.properties);
83 LOG.trace(message);
84 }
85
86 }
87
88 /**
89 * This constructs an instance of this class from list of name-value pairs.
90 *
91 * @param inputs List of exception information such as
92 * <ul>
93 * <li>DOCUMENT_ID - If it's document form</li>
94 * <li>USER_EMAIL - Session user email</li>
95 * <li>USER_NAME - Session user name</li>
96 * <li>COMPONENT_NAME - Document or lookup or inquiry form</li>
97 * attribute of GlobalVariables</li>
98 * <li>EXCEPTION_REPORT_SUBJECT - Exception error message and current settings</li>
99 * <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 }