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("stackTrace", stackTrace); 101 request.setAttribute("securityError", securityError); 102 request.setAttribute("request_dump", getRequestDump(request)); 103 request.getRequestDispatcher("/nuxeo_error.jsp").forward(request, response); 104 // FacesLifecycle.endRequest( facesContext.getExternalContext() ); 105 // facesContext.release(); 106 } 107 108 private String getRequestDump(HttpServletRequest request) { 109 StringBuilder builder = new StringBuilder(); 110 builder.append("\nParameter:\n"); 111 Map<String, String[]> m = request.getParameterMap(); 112 for (Map.Entry<String, String[]> entry : m.entrySet()) { 113 builder.append(entry.getKey()).append(":"); 114 if (entry.getValue() == null) { 115 continue; 116 } 117 for (String s : entry.getValue()) { 118 builder.append(s).append(","); 119 } 120 builder.deleteCharAt(builder.length() - 1); 121 builder.append("\n"); 122 } 123 builder.append("\n"); 124 Enumeration<String> names = request.getAttributeNames(); 125 builder.append("Attributes:\n"); 126 while (names.hasMoreElements()) { 127 String name = names.nextElement(); 128 if (name.equals(SEAM_MESSAGES)) { 129 continue; 130 } 131 Object obj = request.getAttribute(name); 132 builder.append(name).append(": ").append(obj.toString()).append("\n"); 133 } 134 builder.append("\n"); 135 Collection<RegistrationInfo> infos = Framework.getRuntime().getComponentManager().getRegistrations(); 136 builder.append("Components:\n"); 137 for (RegistrationInfo info : infos) { 138 builder.append(info.getComponent().getName()).append(",").append( 139 info.isActivated() ? "activated" : "not activated").append("\n"); 140 } 141 nuxeoErrorLog.trace("User Principal: " + request.getUserPrincipal() + "\n" + builder.toString()); 142 return builder.toString(); 143 } 144 145 private MockFacesContext createFacesContext(HttpServletRequest request, HttpServletResponse response) { 146 MockFacesContext mockFacesContext = new MockFacesContext(new MockExternalContext(servletContext, request, 147 response), new MockApplication()); 148 mockFacesContext.setViewRoot(new UIViewRoot()); 149 return mockFacesContext; 150 } 151 152 public String getStackTraceAsString(Throwable t) { 153 StringWriter swriter = new StringWriter(); 154 PrintWriter pwriter = new PrintWriter(swriter); 155 t.printStackTrace(pwriter); 156 return swriter.getBuffer().toString(); 157 } 158 159}