001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.markup.html.pages; 018 019import java.util.List; 020 021import javax.servlet.http.HttpServletResponse; 022 023import org.apache.wicket.Page; 024import org.apache.wicket.WicketRuntimeException; 025import org.apache.wicket.markup.MarkupException; 026import org.apache.wicket.markup.MarkupStream; 027import org.apache.wicket.markup.html.WebMarkupContainer; 028import org.apache.wicket.markup.html.basic.Label; 029import org.apache.wicket.markup.html.basic.MultiLineLabel; 030import org.apache.wicket.markup.html.debug.PageView; 031import org.apache.wicket.markup.html.link.Link; 032import org.apache.wicket.request.http.WebResponse; 033import org.apache.wicket.util.lang.Generics; 034 035 036/** 037 * Shows a runtime exception on a nice HTML page. 038 * 039 * @author Jonathan Locke 040 */ 041public class ExceptionErrorPage extends AbstractErrorPage 042{ 043 private static final long serialVersionUID = 1L; 044 045 /** Keep a reference to the root cause. WicketTester will use it */ 046 private final transient Throwable throwable; 047 048 /** 049 * Constructor. 050 * 051 * @param throwable 052 * The exception to show 053 * @param page 054 * The page being rendered when the exception was thrown 055 */ 056 public ExceptionErrorPage(final Throwable throwable, final Page page) 057 { 058 this.throwable = throwable; 059 060 // Add exception label 061 add(new MultiLineLabel("exception", getErrorMessage(throwable))); 062 063 add(new MultiLineLabel("stacktrace", getStackTrace(throwable))); 064 065 // Get values 066 String resource = ""; 067 String markup = ""; 068 MarkupStream markupStream = null; 069 070 if (throwable instanceof MarkupException) 071 { 072 markupStream = ((MarkupException)throwable).getMarkupStream(); 073 074 if (markupStream != null) 075 { 076 markup = markupStream.toHtmlDebugString(); 077 resource = markupStream.getResource().toString(); 078 } 079 } 080 081 // Create markup label 082 final MultiLineLabel markupLabel = new MultiLineLabel("markup", markup); 083 084 markupLabel.setEscapeModelStrings(false); 085 086 // Add container with markup highlighted 087 final WebMarkupContainer markupHighlight = new WebMarkupContainer("markupHighlight"); 088 089 markupHighlight.add(markupLabel); 090 markupHighlight.add(new Label("resource", resource)); 091 add(markupHighlight); 092 093 // Show container if markup stream is available 094 markupHighlight.setVisible(markupStream != null); 095 096 add(new Link<Void>("displayPageViewLink") 097 { 098 private static final long serialVersionUID = 1L; 099 100 @Override 101 public void onClick() 102 { 103 ExceptionErrorPage.this.replace(new PageView("componentTree", page)); 104 setVisible(false); 105 } 106 }); 107 108 add(new Label("componentTree", "")); 109 } 110 111 /** 112 * Converts a Throwable to a string. 113 * 114 * @param throwable 115 * The throwable 116 * @return The string 117 */ 118 public String getErrorMessage(final Throwable throwable) 119 { 120 if (throwable != null) 121 { 122 StringBuilder sb = new StringBuilder(256); 123 124 // first print the last cause 125 List<Throwable> al = convertToList(throwable); 126 int length = al.size() - 1; 127 Throwable cause = al.get(length); 128 sb.append("Last cause: ").append(cause.getMessage()).append('\n'); 129 if (throwable instanceof WicketRuntimeException) 130 { 131 String msg = throwable.getMessage(); 132 if ((msg != null) && (msg.equals(cause.getMessage()) == false)) 133 { 134 if (throwable instanceof MarkupException) 135 { 136 MarkupStream stream = ((MarkupException)throwable).getMarkupStream(); 137 if (stream != null) 138 { 139 String text = "\n" + stream.toString(); 140 if (msg.endsWith(text)) 141 { 142 msg = msg.substring(0, msg.length() - text.length()); 143 } 144 } 145 } 146 147 sb.append("WicketMessage: "); 148 sb.append(msg); 149 sb.append("\n\n"); 150 } 151 } 152 return sb.toString(); 153 } 154 else 155 { 156 return "[Unknown]"; 157 } 158 } 159 160 /** 161 * Converts a Throwable to a string. 162 * 163 * @param throwable 164 * The throwable 165 * @return The string 166 */ 167 public String getStackTrace(final Throwable throwable) 168 { 169 if (throwable != null) 170 { 171 List<Throwable> al = convertToList(throwable); 172 173 StringBuilder sb = new StringBuilder(256); 174 175 // first print the last cause 176 int length = al.size() - 1; 177 Throwable cause = al.get(length); 178 179 sb.append("Root cause:\n\n"); 180 outputThrowable(cause, sb, false); 181 182 if (length > 0) 183 { 184 sb.append("\n\nComplete stack:\n\n"); 185 for (int i = 0; i < length; i++) 186 { 187 outputThrowable(al.get(i), sb, true); 188 sb.append("\n"); 189 } 190 } 191 return sb.toString(); 192 } 193 else 194 { 195 return "<Null Throwable>"; 196 } 197 } 198 199 /** 200 * @param throwable 201 * @return xxx 202 */ 203 private List<Throwable> convertToList(final Throwable throwable) 204 { 205 List<Throwable> al = Generics.newArrayList(); 206 Throwable cause = throwable; 207 al.add(cause); 208 while ((cause.getCause() != null) && (cause != cause.getCause())) 209 { 210 cause = cause.getCause(); 211 al.add(cause); 212 } 213 return al; 214 } 215 216 /** 217 * Outputs the throwable and its stacktrace to the stringbuffer. If stopAtWicketSerlvet is true 218 * then the output will stop when the org.apache.wicket servlet is reached. sun.reflect. 219 * packages are filtered out. 220 * 221 * @param cause 222 * @param sb 223 * @param stopAtWicketServlet 224 */ 225 private void outputThrowable(Throwable cause, StringBuilder sb, boolean stopAtWicketServlet) 226 { 227 sb.append(cause); 228 sb.append("\n"); 229 StackTraceElement[] trace = cause.getStackTrace(); 230 for (int i = 0; i < trace.length; i++) 231 { 232 String traceString = trace[i].toString(); 233 if (!(traceString.startsWith("sun.reflect.") && i > 1)) 234 { 235 sb.append(" at "); 236 sb.append(traceString); 237 sb.append("\n"); 238 if (stopAtWicketServlet && 239 (traceString.startsWith("org.apache.wicket.protocol.http.WicketServlet") || traceString.startsWith("org.apache.wicket.protocol.http.WicketFilter"))) 240 { 241 return; 242 } 243 } 244 } 245 } 246 247 @Override 248 protected void setHeaders(final WebResponse response) 249 { 250 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 251 } 252 253 /** 254 * Get access to the exception 255 * 256 * @return The exception 257 */ 258 public Throwable getThrowable() 259 { 260 return throwable; 261 } 262}