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