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 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 */
050 public 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 }