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