View Javadoc

1   /**
2    * Copyright 2005-2012 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 }