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