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