View Javadoc

1   /**
2    * Copyright 2005-2011 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         
205         String env= KRADServiceLocator.getKualiConfigurationService().
206                 getPropertyValueAsString("environment");
207         String format="%s:%s:%s";
208         String componentName=properties.get(COMPONENT_NAME);
209         String subject=String.format(format,
210                 env,
211                 (componentName==null)?"":componentName,
212                 exception.getMessage());
213         
214         if (LOG.isTraceEnabled()) {
215             String lm=String.format("EXIT %s", subject);
216             LOG.trace(lm);
217         }
218         
219         return subject;
220     }
221     
222     /**
223      * This method compose the exception information that includes
224      * <ul>
225      * <li>documentId - If it's document form</li>
226      * <li>userEmail - Session user email</li>
227      * <li>userName - Session user name</li>
228      * <li>component - Document or lookup or inquiry form</li>
229      * <li>description - Information input by user or blank</li>
230      * <li>errorMessage - Exception error message</li>
231      * <li>stackTrace - Exception stack trace</li>
232      * </ul>
233      * <p>Example;
234      * <code>
235      * documentId: 2942084
236      * userEmail: someone@somewhere
237      * userName: some name
238      * description: Something went wrong!
239      * component: document
240      * errorMessage: Some error message
241      * stackTrace: Exception stack trace here 
242      * </code>
243      * 
244      * @return
245      */
246     private String createReportMessage() {
247         if (LOG.isTraceEnabled()) {
248             String lm=String.format("ENTRY");
249             LOG.trace(lm);
250         }
251                 
252         String documentId=properties.get(DOCUMENT_ID);
253         String userEmail=properties.get(USER_EMAIL);
254         String uuid=properties.get(UUID);
255         String description=properties.get(DESCRIPTION);
256         String component=properties.get(COMPONENT_NAME);
257         String exceptionMessage=properties.get(EXCEPTION_MESSAGE);
258         String stackTrace=properties.get(STACK_TRACE);
259         String format="Document Id: %s%n"+
260                       "User Email: %s%n"+
261                       "Person User Identifier: %s%n"+
262                       "User Input: %s%n"+
263                       "component: %s%n"+
264                       "errorMessage: %s%n"+
265                       "%s%n";
266         String message=String.format(format,
267                 (documentId==null)?"":documentId,
268                 (userEmail==null)?"":userEmail,
269                 (uuid==null)?"":uuid,
270                 (description==null)?"":description,
271                 (component==null)?"":component,
272                 (exceptionMessage==null)?"":exceptionMessage,
273                 (stackTrace==null)?"":stackTrace);
274 
275         if (LOG.isTraceEnabled()) {
276             String lm=String.format("EXIT %s", message);
277             LOG.trace(lm);
278         }
279         
280         return message;
281     }
282 
283     /**
284      * This method return the thrown exception stack trace as string.
285      * 
286      * @param thrownException
287      * @return
288      */
289     public String getExceptionStackTrace(Exception thrownException) {
290         if (LOG.isTraceEnabled()) {
291             String lm=String.format("ENTRY");
292             LOG.trace(lm);
293         }
294                 
295         StringWriter wrt=new StringWriter();
296         PrintWriter pw=new PrintWriter(wrt);
297         thrownException.printStackTrace(pw);
298         pw.flush();
299         String stackTrace=wrt.toString();
300         try {
301             wrt.close();
302             pw.close();
303         } catch (Exception e) {
304             LOG.trace(e.getMessage(), e);
305         }
306         
307         if (LOG.isTraceEnabled()) {
308             String lm=String.format("EXIT %s", stackTrace);
309             LOG.trace(lm);
310         }
311         
312         return stackTrace;
313     }
314     
315     /**
316      * This overridden method return the exception if the ixception type is in the
317      * defined list. Otherwise, it returns the GENERIC_SYSTEM_ERROR_MESSAGE.
318      * 
319      * @see org.kuali.rice.krad.exception.KualiExceptionIncident#getDisplayMessage(Exception)
320      */
321     public String getDisplayMessage(Exception exception) {
322         if (LOG.isTraceEnabled()) {
323             String message=String.format("ENTRY %s", exception.getMessage());
324             LOG.trace(message);
325         }
326 
327         // Create the display message
328         String displayMessage;
329         if (exception instanceof KualiException) {
330             displayMessage=exception.getMessage();
331         } else {
332             displayMessage=GENERIC_SYSTEM_ERROR_MESSAGE;
333         }
334 
335         if (LOG.isTraceEnabled()) {
336             String message=String.format("EXIT %s", displayMessage);
337             LOG.trace(message);
338         }
339 
340         return displayMessage;
341     }
342 
343     /**
344      * This overridden method returns value of the found property key. Except the
345      * property EXCEPTION_REPORT_MESSAGE
346      * 
347      * @see org.kuali.rice.krad.exception.KualiExceptionIncident#getProperty(java.lang.String)
348      */
349     public String getProperty(String key) {
350         if (LOG.isTraceEnabled()) {
351             String message=String.format("ENTRY %s", key);
352             LOG.trace(message);
353         }
354         
355         String value;
356         if (key.equals(EXCEPTION_REPORT_MESSAGE) && !properties.containsKey(key)) {
357             value=createReportMessage();
358             properties.put(EXCEPTION_REPORT_MESSAGE, value);
359         } else {
360             value=properties.get(key);
361         }
362         
363         if (LOG.isTraceEnabled()) {
364             String message=String.format("EXIT %s", value);
365             LOG.trace(message);
366         }
367 
368         return value;
369     }
370 
371     /**
372      * This overridden method return current internal properties.
373      * 
374      * @see org.kuali.rice.krad.exception.KualiExceptionIncident#toProperties()
375      */
376     public Map<String, String> toProperties() {
377         if (LOG.isTraceEnabled()) {
378             String message=String.format("ENTRY");
379             LOG.trace(message);
380         }
381 
382         if (LOG.isTraceEnabled()) {
383             String message=String.format("EXIT %s", properties.toString());
384             LOG.trace(message);
385         }
386 
387         return properties;
388     }
389 }