001/* 002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 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 * 016 * Contributors: 017 * arussel 018 */ 019package org.nuxeo.ecm.platform.ui.web.shield; 020 021import java.io.IOException; 022import java.io.PrintWriter; 023import java.io.StringWriter; 024import java.util.Collection; 025import java.util.Enumeration; 026import java.util.Map; 027 028import javax.faces.component.UIViewRoot; 029import javax.servlet.ServletContext; 030import javax.servlet.ServletException; 031import javax.servlet.http.HttpServletRequest; 032import javax.servlet.http.HttpServletResponse; 033 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.jboss.seam.Seam; 037import org.jboss.seam.contexts.Contexts; 038import org.jboss.seam.contexts.FacesLifecycle; 039import org.jboss.seam.core.ConversationPropagation; 040import org.jboss.seam.core.Manager; 041import org.jboss.seam.mock.MockApplication; 042import org.jboss.seam.mock.MockExternalContext; 043import org.jboss.seam.mock.MockFacesContext; 044import org.nuxeo.runtime.api.Framework; 045import org.nuxeo.runtime.model.RegistrationInfo; 046 047/** 048 * @author arussel 049 */ 050public class ErrorPageForwarder { 051 052 private static final Log nuxeoErrorLog = LogFactory.getLog("nuxeo-error-log"); 053 054 private static final Log log = LogFactory.getLog(ErrorPageForwarder.class); 055 056 private static final String SEAM_MESSAGES = "org.jboss.seam.international.messages"; 057 058 private ServletContext servletContext; 059 060 public void forwardToErrorPage(HttpServletRequest request, HttpServletResponse response, Throwable t, 061 String exceptionMessage, String userMessage, Boolean securityError, ServletContext servletContext) 062 throws ServletException, IOException { 063 forwardToErrorPage(request, response, getStackTraceAsString(t), exceptionMessage, userMessage, securityError, 064 servletContext); 065 } 066 067 @SuppressWarnings("rawtypes") 068 public void forwardToErrorPage(HttpServletRequest request, HttpServletResponse response, String stackTrace, 069 String exceptionMessage, String userMessage, Boolean securityError, ServletContext servletContext) 070 throws ServletException, IOException { 071 log.error(stackTrace); 072 this.servletContext = servletContext; 073 // cut/paste from seam Exception filter. 074 // we recreate the seam context to be able to use the messages. 075 MockFacesContext facesContext = createFacesContext(request, response); 076 facesContext.setCurrent(); 077 078 // if the event context was cleaned up, fish the conversation id 079 // directly out of the ServletRequest attributes, else get it from 080 // the event context 081 Manager manager = Contexts.isEventContextActive() ? (Manager) Contexts.getEventContext().get(Manager.class) 082 : (Manager) request.getAttribute(Seam.getComponentName(Manager.class)); 083 String conversationId = manager == null ? null : manager.getCurrentConversationId(); 084 FacesLifecycle.beginExceptionRecovery(facesContext.getExternalContext()); 085 // If there is an existing long-running conversation on 086 // the failed request, propagate it 087 if (conversationId == null) { 088 Manager.instance().initializeTemporaryConversation(); 089 } else { 090 ConversationPropagation.instance().setConversationId(conversationId); 091 Manager.instance().restoreConversation(); 092 } 093 // we get the message from the seam attribute as the EL won't work in 094 // xhtml 095 String user_message = request.getAttribute(SEAM_MESSAGES) == null ? "An unexpected error occurred." 096 : (String) ((Map) request.getAttribute(SEAM_MESSAGES)).get(userMessage); 097 FacesLifecycle.beginExceptionRecovery(facesContext.getExternalContext()); 098 request.setAttribute("exception_message", exceptionMessage); 099 request.setAttribute("user_message", user_message); 100 request.setAttribute("isDevModeSet", Framework.isDevModeSet()); 101 if (Framework.isDevModeSet()) { 102 request.setAttribute("stackTrace", stackTrace); 103 request.setAttribute("request_dump", getRequestDump(request)); 104 } 105 request.setAttribute("securityError", securityError); 106 request.getRequestDispatcher("/nuxeo_error.jsp").forward(request, response); 107 // FacesLifecycle.endRequest( facesContext.getExternalContext() ); 108 // facesContext.release(); 109 } 110 111 private String getRequestDump(HttpServletRequest request) { 112 StringBuilder builder = new StringBuilder(); 113 builder.append("\nParameter:\n"); 114 Map<String, String[]> m = request.getParameterMap(); 115 for (Map.Entry<String, String[]> entry : m.entrySet()) { 116 builder.append(entry.getKey()).append(":"); 117 if (entry.getValue() == null) { 118 continue; 119 } 120 for (String s : entry.getValue()) { 121 builder.append(s).append(","); 122 } 123 builder.deleteCharAt(builder.length() - 1); 124 builder.append("\n"); 125 } 126 builder.append("\n"); 127 Enumeration<String> names = request.getAttributeNames(); 128 builder.append("Attributes:\n"); 129 while (names.hasMoreElements()) { 130 String name = names.nextElement(); 131 if (name.equals(SEAM_MESSAGES)) { 132 continue; 133 } 134 Object obj = request.getAttribute(name); 135 builder.append(name).append(": ").append(obj.toString()).append("\n"); 136 } 137 builder.append("\n"); 138 Collection<RegistrationInfo> infos = Framework.getRuntime().getComponentManager().getRegistrations(); 139 builder.append("Components:\n"); 140 for (RegistrationInfo info : infos) { 141 builder.append(info.getComponent().getName()).append(",").append( 142 info.isActivated() ? "activated" : "not activated").append("\n"); 143 } 144 nuxeoErrorLog.trace("User Principal: " + request.getUserPrincipal() + "\n" + builder.toString()); 145 return builder.toString(); 146 } 147 148 private MockFacesContext createFacesContext(HttpServletRequest request, HttpServletResponse response) { 149 MockFacesContext mockFacesContext = new MockFacesContext(new MockExternalContext(servletContext, request, 150 response), new MockApplication()); 151 mockFacesContext.setViewRoot(new UIViewRoot()); 152 return mockFacesContext; 153 } 154 155 public String getStackTraceAsString(Throwable t) { 156 StringWriter swriter = new StringWriter(); 157 PrintWriter pwriter = new PrintWriter(swriter); 158 t.printStackTrace(pwriter); 159 return swriter.getBuffer().toString(); 160 } 161 162}