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 }