View Javadoc
1   /**
2    * Copyright 2005-2016 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.kns.web.struts.action;
17  
18  import org.apache.log4j.Logger;
19  import org.apache.struts.Globals;
20  import org.apache.struts.action.Action;
21  import org.apache.struts.action.ActionForm;
22  import org.apache.struts.action.ActionForward;
23  import org.apache.struts.action.ActionMapping;
24  import org.kuali.rice.core.api.config.property.ConfigContext;
25  import org.kuali.rice.core.api.util.RiceConstants;
26  import org.kuali.rice.kns.util.IncidentReportUtils;
27  import org.kuali.rice.kns.web.struts.form.KualiExceptionIncidentForm;
28  import org.kuali.rice.krad.exception.ExceptionIncident;
29  import org.kuali.rice.krad.exception.KualiExceptionIncident;
30  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
31  import org.kuali.rice.krad.service.KualiExceptionIncidentService;
32  import org.kuali.rice.krad.util.GlobalVariables;
33  import org.kuali.rice.krad.util.KRADConstants;
34  
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  import java.util.Enumeration;
38  import java.util.HashMap;
39  import java.util.Map;
40  
41  /**
42   * This is the struts action class for handling the exception for Kuali
43   * applications.
44   *
45   */
46  public class KualiExceptionHandlerAction extends Action {
47  	private static final Logger LOG = Logger
48  			.getLogger(KualiExceptionHandlerAction.class);
49  
50  	private static final String EXCEPTION_TIME_STAMP = "exception-timeStamp";
51  	private static final String EXCEPTION_DOCUMENT_ID = "exception-" + ExceptionIncident.DOCUMENT_ID;
52  	private static final String EXCEPTION_USER_EMAIL = "exception-" + ExceptionIncident.USER_EMAIL;
53  	private static final String EXCEPTION_USER_NAME = "exception-" + ExceptionIncident.USER_NAME;
54  	private static final String EXCEPTION_UUID = "exception-" + ExceptionIncident.UUID;
55  	private static final String EXCEPTION_COMPONENT_NAME = "exception-" + ExceptionIncident.COMPONENT_NAME;
56  	private static final String EXCEPTION_EXCEPTION_REPORT_SUBJECT = "exception-" + ExceptionIncident.EXCEPTION_REPORT_SUBJECT;
57  	private static final String EXCEPTION_EXCEPTION_MESSAGE = "exception-" + ExceptionIncident.EXCEPTION_MESSAGE;
58  	private static final String EXCEPTION_STACK_TRACE = "exception-" + ExceptionIncident.STACK_TRACE;
59  
60  	/**
61  	 * This overridden method dispatches action to be taken based on
62  	 * "methodToCall" parameter. The exception is processed when there is no
63  	 * "methodToCall" specified.
64  	 *
65  	 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping,
66  	 *      org.apache.struts.action.ActionForm,
67  	 *      javax.servlet.http.HttpServletRequest,
68  	 *      javax.servlet.http.HttpServletResponse)
69  	 */
70  	public ActionForward execute(ActionMapping mapping, ActionForm form,
71  			HttpServletRequest request, HttpServletResponse response)
72  			throws Exception {
73  		return executeException(mapping, form, request, response);
74  	}
75  
76  	/**
77  	 * This overridden method processes the exception and post exception (when
78  	 * user either submit/cancel the exception JSP page).
79  	 * <ul>
80  	 * <li>ProcessDefinition application Exception - Exception is stored in Http Request</li>
81  	 * <li>ProcessDefinition exception incident reporting - No exception, only form data</li>
82  	 * </ul>
83  	 *
84  	 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping,
85  	 *      org.apache.struts.action.ActionForm,
86  	 *      javax.servlet.http.HttpServletRequest,
87  	 *      javax.servlet.http.HttpServletResponse)
88  	 */
89  	public ActionForward executeException(ActionMapping mapping,
90  			ActionForm form, HttpServletRequest request,
91  			HttpServletResponse response) throws Exception {
92  
93  		if (LOG.isDebugEnabled()) {
94  			String lm = String.format("ENTRY %s%n%s", form.getClass()
95  					.getSimpleName(), request.getRequestURI());
96  			LOG.debug(lm);
97  		}
98  
99  		// Get exception thrown
100 		Exception e = (Exception) request.getAttribute(Globals.EXCEPTION_KEY);
101 
102 		// Initialize defined action mapping from struts-config
103 		ActionForward returnForward = null;
104 
105 		// In case there is no exception, either a post back after page was
106 		// filled in
107 		// or just an error from directly accessing this struts action
108 		if (e == null) {
109 			if (form instanceof KualiExceptionIncidentForm) {
110 				KualiExceptionIncidentForm formObject = (KualiExceptionIncidentForm) form;
111 				// Manage conditions: submit or cancel
112 				if (!formObject.isCancel()) {
113 					// Locate the post exception handler service. The service id
114 					// is
115 					// defined in the application properties
116 					// Only process the post exception handling when the
117 					// service
118 					// is specified
119 					KualiExceptionIncidentService reporterService = KRADServiceLocatorWeb
120 							.getKualiExceptionIncidentService();
121 					// An instance of the ExceptionIncident is created by
122 					// the
123 					// ExceptionIncidentService
124 					Map reducedMap = new HashMap();
125 					Enumeration<String> names = request.getParameterNames();
126 					while (names.hasMoreElements()) {
127 						String name = names.nextElement();
128 						reducedMap.put(name, request.getParameter(name));
129 					}
130 
131 					// Sensitive data stored in user session
132 					Map<String, Object> userSessionMap = GlobalVariables.getUserSession().getObjectMap();
133 
134 					// Only display if this is the right exception
135 					if(userSessionMap.get("EXCEPTION_TIME_STAMP").toString().equals(reducedMap.get(ExceptionIncident.STACK_TRACE))) {
136 						reducedMap.put(ExceptionIncident.DOCUMENT_ID, userSessionMap.get("EXCEPTION_DOCUMENT_ID").toString());
137 						reducedMap.put(ExceptionIncident.USER_EMAIL, userSessionMap.get("EXCEPTION_USER_EMAIL").toString());
138 						reducedMap.put(ExceptionIncident.USER_NAME, userSessionMap.get("EXCEPTION_USER_NAME").toString());
139 						reducedMap.put(ExceptionIncident.UUID, userSessionMap.get("EXCEPTION_UUID").toString());
140 						reducedMap.put(ExceptionIncident.COMPONENT_NAME, userSessionMap.get("EXCEPTION_COMPONENT_NAME").toString());
141 						reducedMap.put(ExceptionIncident.EXCEPTION_REPORT_SUBJECT, userSessionMap.get("EXCEPTION_EXCEPTION_REPORT_SUBJECT").toString());
142 						reducedMap.put(ExceptionIncident.EXCEPTION_MESSAGE, userSessionMap.get("EXCEPTION_EXCEPTION_MESSAGE").toString());
143 						reducedMap.put(ExceptionIncident.STACK_TRACE, userSessionMap.get("EXCEPTION_STACK_TRACE").toString());
144 
145 					} else {
146 						reducedMap.put(ExceptionIncident.STACK_TRACE,"Not available.");
147 					}
148 
149 					KualiExceptionIncident exceptionIncident = reporterService
150 							.getExceptionIncident(reducedMap);
151 
152 					// Report the incident
153 					reporterService.report(exceptionIncident);
154 				} else {
155 					// Set return after canceling
156 					ActionForward cancelForward = mapping
157 							.findForward(KRADConstants.MAPPING_CANCEL);
158 					if (cancelForward == null) {
159 						cancelForward = returnForward;
160 					} else {
161 						returnForward = cancelForward;
162 					}
163 				}
164 			}
165 		} else {
166 			// ProcessDefinition the received exception from HTTP request
167 			returnForward = processException(mapping, form, request, e);
168 		}
169 
170 		// Not specified, return
171 		if (returnForward == null) {
172 			returnForward = mapping.findForward(KRADConstants.MAPPING_CLOSE);
173 		}
174 
175 		if (LOG.isDebugEnabled()) {
176 			String lm = String.format("EXIT %s",
177 					(returnForward == null) ? "null" : returnForward.getPath());
178 			LOG.debug(lm);
179 		}
180 
181 		return returnForward;
182 	}
183 
184 	/**
185 	 * This method process the caught exception by creating an exception
186 	 * information properties list and forward these properties to the exception
187 	 * incident handler JSP.
188 	 *
189 	 * @param exception
190 	 * @param mapping
191 	 * @param request
192 	 * @param documentId
193 	 *            Id of the document that Struts threw exception during its
194 	 *            processing. null if not the document processing that caused
195 	 *            the exception
196 	 * @return
197 	 * @throws Exception
198 	 */
199 	@SuppressWarnings("unchecked")
200 	protected ActionForward processException(ActionMapping mapping,
201 			ActionForm form, HttpServletRequest request, Exception exception)
202 			throws Exception {
203 		// Only process the exception handling when the service
204 		// is specified
205 		KualiExceptionIncidentService reporterService = KRADServiceLocatorWeb
206 				.getKualiExceptionIncidentService();
207 		// Get exception properties from the Http Request
208 		Map<String, String> properties = (Map<String, String>) request
209 				.getAttribute(IncidentReportUtils.EXCEPTION_PROPERTIES);
210 		// Construct the exception incident object
211 		KualiExceptionIncident ei = reporterService.getExceptionIncident(
212 				exception, properties);
213 
214 		// Add sensitive data to user session
215 		String exceptionTimeStamp = String.valueOf(System.currentTimeMillis());
216 		GlobalVariables.getUserSession().addObject("EXCEPTION_TIME_STAMP", exceptionTimeStamp);
217 		GlobalVariables.getUserSession().addObject("EXCEPTION_DOCUMENT_ID", ei.getProperty(ExceptionIncident.DOCUMENT_ID));
218 		GlobalVariables.getUserSession().addObject("EXCEPTION_USER_EMAIL", ei.getProperty(ExceptionIncident.USER_EMAIL));
219 		GlobalVariables.getUserSession().addObject("EXCEPTION_USER_NAME", ei.getProperty(ExceptionIncident.USER_NAME));
220 		GlobalVariables.getUserSession().addObject("EXCEPTION_UUID", ei.getProperty(ExceptionIncident.UUID));
221 		GlobalVariables.getUserSession().addObject("EXCEPTION_COMPONENT_NAME", ei.getProperty(ExceptionIncident.COMPONENT_NAME));
222 		GlobalVariables.getUserSession().addObject("EXCEPTION_EXCEPTION_REPORT_SUBJECT", ei.getProperty(ExceptionIncident.EXCEPTION_REPORT_SUBJECT));
223 		GlobalVariables.getUserSession().addObject("EXCEPTION_EXCEPTION_MESSAGE", ei.getProperty(ExceptionIncident.EXCEPTION_MESSAGE));
224 		GlobalVariables.getUserSession().addObject("EXCEPTION_STACK_TRACE", ei.getProperty(ExceptionIncident.STACK_TRACE));
225 
226 		// Hide sensitive data from form in production only
227 		if(ConfigContext.getCurrentContextConfig().isProductionEnvironment()) {
228 			Map<String, String> prodProperties = ei.toProperties();
229 			prodProperties.put(ExceptionIncident.DOCUMENT_ID, "");
230 			prodProperties.put(ExceptionIncident.USER_EMAIL, "");
231 			prodProperties.put(ExceptionIncident.USER_NAME, "");
232 			prodProperties.put(ExceptionIncident.UUID, "");
233 			prodProperties.put(ExceptionIncident.COMPONENT_NAME, "");
234 			prodProperties.put(ExceptionIncident.EXCEPTION_REPORT_SUBJECT, "");
235 			prodProperties.put(ExceptionIncident.EXCEPTION_MESSAGE, "");
236 			prodProperties.put(ExceptionIncident.STACK_TRACE, exceptionTimeStamp);
237 			ei = reporterService.getExceptionIncident(
238 					null, prodProperties);
239 		}
240 
241 		// Set full exception properties in Http Request and forward to JSP
242 		request.setAttribute(KualiExceptionHandlerAction.class
243 				.getSimpleName(), ei.toProperties());
244 		return mapping.findForward(RiceConstants.MAPPING_BASIC);
245 	}
246 }