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    }