View Javadoc
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 }