001// Copyright 2005 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry.portlet; 016 017import java.io.CharArrayWriter; 018import java.io.IOException; 019import java.io.PrintWriter; 020 021import javax.portlet.ActionResponse; 022 023import org.apache.hivemind.ApplicationRuntimeException; 024import org.apache.tapestry.IMarkupWriter; 025import org.apache.tapestry.IRequestCycle; 026import org.apache.tapestry.describe.RenderStrategy; 027import org.apache.tapestry.error.ErrorMessages; 028import org.apache.tapestry.error.ExceptionPresenter; 029import org.apache.tapestry.error.RequestExceptionReporter; 030import org.apache.tapestry.markup.MarkupWriterSource; 031import org.apache.tapestry.services.ServiceConstants; 032import org.apache.tapestry.util.ContentType; 033import org.apache.tapestry.util.exception.ExceptionAnalyzer; 034import org.apache.tapestry.util.exception.ExceptionDescription; 035import org.apache.tapestry.util.exception.ExceptionProperty; 036import org.apache.tapestry.web.WebRequest; 037import org.apache.tapestry.web.WebResponse; 038 039/** 040 * Service used to present a runtime exception to the user. This is very tricky in the Portlet world 041 * because of the split between the action and render requests (much more likely to get an error 042 * during the action request than during the render request, but both are possible). 043 * <p> 044 * During an action request, this code will render the HTML markup for the exception into a buffer 045 * that is stored as persistent attribute in the portal session. 046 * 047 * @author Howard M. Lewis Ship 048 * @since 4.0 049 */ 050public class PortletExceptionPresenter implements ExceptionPresenter 051{ 052 private PortletRequestGlobals _globals; 053 054 private RenderStrategy _renderStrategy; 055 056 private WebRequest _request; 057 058 private RequestExceptionReporter _requestExceptionReporter; 059 060 private WebResponse _response; 061 062 private MarkupWriterSource _markupWriterSource; 063 064 public void presentException(IRequestCycle cycle, Throwable cause) 065 { 066 try 067 { 068 if (_globals.isRenderRequest()) 069 reportRenderRequestException(cycle, cause); 070 else 071 reportActionRequestException(cycle, cause); 072 } 073 catch (Exception ex) 074 { 075 // Worst case scenario. The exception page itself is broken, leaving 076 // us with no option but to write the cause to the output. 077 078 // Also, write the exception thrown when redendering the exception 079 // page, so that can get fixed as well. 080 081 _requestExceptionReporter.reportRequestException(PortletMessages 082 .errorReportingException(ex), ex); 083 084 // And throw the exception. 085 086 throw new ApplicationRuntimeException(ex.getMessage(), ex); 087 } 088 089 _requestExceptionReporter.reportRequestException(ErrorMessages 090 .unableToProcessClientRequest(cause), cause); 091 } 092 093 private void reportActionRequestException(IRequestCycle cycle, Throwable cause) 094 { 095 CharArrayWriter caw = new CharArrayWriter(); 096 PrintWriter pw = new PrintWriter(caw); 097 098 IMarkupWriter writer = _markupWriterSource 099 .newMarkupWriter(pw, new ContentType("text/html")); 100 101 writeException(writer, cycle, cause); 102 103 writer.close(); 104 105 String markup = caw.toString(); 106 107 _request.getSession(true).setAttribute( 108 PortletConstants.PORTLET_EXCEPTION_MARKUP_ATTRIBUTE, 109 markup); 110 111 ActionResponse response = _globals.getActionResponse(); 112 113 response.setRenderParameter(ServiceConstants.SERVICE, PortletConstants.EXCEPTION_SERVICE); 114 } 115 116 private void reportRenderRequestException(IRequestCycle cycle, Throwable cause) 117 throws IOException 118 { 119 PrintWriter pw = _response.getPrintWriter(new ContentType("text/html")); 120 121 IMarkupWriter writer = _markupWriterSource 122 .newMarkupWriter(pw, new ContentType("text/html")); 123 124 writeException(writer, cycle, cause); 125 } 126 127 public void setGlobals(PortletRequestGlobals globals) 128 { 129 _globals = globals; 130 } 131 132 public void setRenderStrategy(RenderStrategy renderStrategy) 133 { 134 _renderStrategy = renderStrategy; 135 } 136 137 public void setRequest(WebRequest request) 138 { 139 _request = request; 140 } 141 142 public void setRequestExceptionReporter(RequestExceptionReporter requestExceptionReporter) 143 { 144 _requestExceptionReporter = requestExceptionReporter; 145 } 146 147 public void setResponse(WebResponse response) 148 { 149 _response = response; 150 } 151 152 public void setMarkupWriterSource(MarkupWriterSource markupWriterSource) 153 { 154 _markupWriterSource = markupWriterSource; 155 } 156 157 private void writeException(IMarkupWriter writer, IRequestCycle cycle, 158 ExceptionDescription exception, boolean showStackTrace) 159 { 160 writer.begin("div"); 161 writer.attribute("class", "portlet-section-header"); 162 writer.print(exception.getExceptionClassName()); 163 writer.end(); 164 writer.println(); 165 166 writer.begin("div"); 167 writer.attribute("class", "portlet-msg-error"); 168 writer.print(exception.getMessage()); 169 writer.end(); 170 writer.println(); 171 172 ExceptionProperty[] properties = exception.getProperties(); 173 174 if (properties.length > 0) 175 { 176 177 writer.begin("table"); 178 writer.attribute("class", "portlet-section-subheader"); 179 180 for (int i = 0; i < properties.length; i++) 181 { 182 writer.begin("tr"); 183 184 writer.attribute("class", i % 2 == 0 ? "portlet-section-body" 185 : "portlet-section-alternate"); 186 187 writer.begin("th"); 188 writer.print(properties[i].getName()); 189 writer.end(); 190 writer.println(); 191 192 writer.begin("td"); 193 194 _renderStrategy.renderObject(properties[i].getValue(), writer, cycle); 195 writer.end("tr"); 196 writer.println(); 197 } 198 199 writer.end(); 200 writer.println(); 201 } 202 203 if (!showStackTrace) 204 return; 205 206 writer.begin("ul"); 207 208 String[] trace = exception.getStackTrace(); 209 210 for (int i = 0; i < trace.length; i++) 211 { 212 writer.begin("li"); 213 writer.print(trace[i]); 214 writer.end(); 215 writer.println(); 216 } 217 218 writer.end(); 219 writer.println(); 220 221 } 222 223 private void writeException(IMarkupWriter writer, IRequestCycle cycle, Throwable cause) 224 { 225 ExceptionDescription[] exceptions = new ExceptionAnalyzer().analyze(cause); 226 227 for (int i = 0; i < exceptions.length; i++) 228 writeException(writer, cycle, exceptions[i], i + 1 == exceptions.length); 229 } 230 231}