001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kns.web.struts.action; 017 018import org.apache.log4j.Logger; 019import org.apache.struts.Globals; 020import org.apache.struts.action.Action; 021import org.apache.struts.action.ActionForm; 022import org.apache.struts.action.ActionForward; 023import org.apache.struts.action.ActionMapping; 024import org.kuali.rice.core.api.config.property.ConfigContext; 025import org.kuali.rice.core.api.util.RiceConstants; 026import org.kuali.rice.kns.util.IncidentReportUtils; 027import org.kuali.rice.kns.web.struts.form.KualiExceptionIncidentForm; 028import org.kuali.rice.krad.exception.ExceptionIncident; 029import org.kuali.rice.krad.exception.KualiExceptionIncident; 030import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 031import org.kuali.rice.krad.service.KualiExceptionIncidentService; 032import org.kuali.rice.krad.util.GlobalVariables; 033import org.kuali.rice.krad.util.KRADConstants; 034 035import javax.servlet.http.HttpServletRequest; 036import javax.servlet.http.HttpServletResponse; 037import java.util.Enumeration; 038import java.util.HashMap; 039import java.util.Map; 040 041/** 042 * This is the struts action class for handling the exception for Kuali 043 * applications. 044 * 045 */ 046public class KualiExceptionHandlerAction extends Action { 047 private static final Logger LOG = Logger 048 .getLogger(KualiExceptionHandlerAction.class); 049 050 private static final String EXCEPTION_TIME_STAMP = "exception-timeStamp"; 051 private static final String EXCEPTION_DOCUMENT_ID = "exception-" + ExceptionIncident.DOCUMENT_ID; 052 private static final String EXCEPTION_USER_EMAIL = "exception-" + ExceptionIncident.USER_EMAIL; 053 private static final String EXCEPTION_USER_NAME = "exception-" + ExceptionIncident.USER_NAME; 054 private static final String EXCEPTION_UUID = "exception-" + ExceptionIncident.UUID; 055 private static final String EXCEPTION_COMPONENT_NAME = "exception-" + ExceptionIncident.COMPONENT_NAME; 056 private static final String EXCEPTION_EXCEPTION_REPORT_SUBJECT = "exception-" + ExceptionIncident.EXCEPTION_REPORT_SUBJECT; 057 private static final String EXCEPTION_EXCEPTION_MESSAGE = "exception-" + ExceptionIncident.EXCEPTION_MESSAGE; 058 private static final String EXCEPTION_STACK_TRACE = "exception-" + ExceptionIncident.STACK_TRACE; 059 060 /** 061 * This overridden method dispatches action to be taken based on 062 * "methodToCall" parameter. The exception is processed when there is no 063 * "methodToCall" specified. 064 * 065 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, 066 * org.apache.struts.action.ActionForm, 067 * javax.servlet.http.HttpServletRequest, 068 * javax.servlet.http.HttpServletResponse) 069 */ 070 public ActionForward execute(ActionMapping mapping, ActionForm form, 071 HttpServletRequest request, HttpServletResponse response) 072 throws Exception { 073 return executeException(mapping, form, request, response); 074 } 075 076 /** 077 * This overridden method processes the exception and post exception (when 078 * user either submit/cancel the exception JSP page). 079 * <ul> 080 * <li>ProcessDefinition application Exception - Exception is stored in Http Request</li> 081 * <li>ProcessDefinition exception incident reporting - No exception, only form data</li> 082 * </ul> 083 * 084 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, 085 * org.apache.struts.action.ActionForm, 086 * javax.servlet.http.HttpServletRequest, 087 * javax.servlet.http.HttpServletResponse) 088 */ 089 public ActionForward executeException(ActionMapping mapping, 090 ActionForm form, HttpServletRequest request, 091 HttpServletResponse response) throws Exception { 092 093 if (LOG.isDebugEnabled()) { 094 String lm = String.format("ENTRY %s%n%s", form.getClass() 095 .getSimpleName(), request.getRequestURI()); 096 LOG.debug(lm); 097 } 098 099 // 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}