001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011// limitations under the License. 012 013package org.apache.tapestry5.corelib.pages; 014 015import org.apache.tapestry5.EventContext; 016import org.apache.tapestry5.SymbolConstants; 017import org.apache.tapestry5.alerts.AlertManager; 018import org.apache.tapestry5.annotations.ContentType; 019import org.apache.tapestry5.annotations.Import; 020import org.apache.tapestry5.annotations.Property; 021import org.apache.tapestry5.annotations.UnknownActivationContextCheck; 022import org.apache.tapestry5.corelib.base.AbstractInternalPage; 023import org.apache.tapestry5.func.F; 024import org.apache.tapestry5.func.Mapper; 025import org.apache.tapestry5.internal.InternalConstants; 026import org.apache.tapestry5.internal.TapestryInternalUtils; 027import org.apache.tapestry5.internal.services.PageActivationContextCollector; 028import org.apache.tapestry5.internal.services.ReloadHelper; 029import org.apache.tapestry5.ioc.annotations.Inject; 030import org.apache.tapestry5.ioc.annotations.Symbol; 031import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 032import org.apache.tapestry5.ioc.internal.util.InternalUtils; 033import org.apache.tapestry5.services.*; 034 035import java.net.MalformedURLException; 036import java.net.URL; 037import java.util.List; 038import java.util.regex.Pattern; 039 040/** 041 * Responsible for reporting runtime exceptions. This page is quite verbose and is usually overridden in a production 042 * application. When {@link org.apache.tapestry5.SymbolConstants#PRODUCTION_MODE} is "true", it is very abbreviated. 043 * 044 * @see org.apache.tapestry5.corelib.components.ExceptionDisplay 045 */ 046@UnknownActivationContextCheck(false) 047@ContentType("text/html") 048@Import(stylesheet = "ExceptionReport.css") 049public class ExceptionReport extends AbstractInternalPage implements ExceptionReporter 050{ 051 private static final String PATH_SEPARATOR_PROPERTY = "path.separator"; 052 053 // Match anything ending in .(something?)path. 054 055 private static final Pattern PATH_RECOGNIZER = Pattern.compile("\\..*path$"); 056 057 @Property 058 private String attributeName; 059 060 @Inject 061 @Symbol(SymbolConstants.PRODUCTION_MODE) 062 @Property(write = false) 063 private boolean productionMode; 064 065 @Inject 066 @Symbol(SymbolConstants.TAPESTRY_VERSION) 067 @Property(write = false) 068 private String tapestryVersion; 069 070 @Inject 071 @Symbol(SymbolConstants.APPLICATION_VERSION) 072 @Property(write = false) 073 private String applicationVersion; 074 075 @Property(write = false) 076 private Throwable rootException; 077 078 @Property 079 private String propertyName; 080 081 @Property 082 private String failurePage; 083 084 @Inject 085 private RequestGlobals requestGlobals; 086 087 @Inject 088 private AlertManager alertManager; 089 090 @Inject 091 private PageActivationContextCollector pageActivationContextCollector; 092 093 @Inject 094 private PageRenderLinkSource linkSource; 095 096 @Inject 097 private BaseURLSource baseURLSource; 098 099 @Inject 100 private ReloadHelper reloadHelper; 101 102 @Inject 103 private URLEncoder urlEncoder; 104 105 @Property 106 private String rootURL; 107 108 @Property 109 private ThreadInfo thread; 110 111 public class ThreadInfo implements Comparable<ThreadInfo> 112 { 113 public final String className, name, state, flags; 114 115 public final ThreadGroup group; 116 117 public ThreadInfo(String className, String name, String state, String flags, ThreadGroup group) 118 { 119 this.className = className; 120 this.name = name; 121 this.state = state; 122 this.flags = flags; 123 this.group = group; 124 } 125 126 @Override 127 public int compareTo(ThreadInfo o) 128 { 129 return name.compareTo(o.name); 130 } 131 } 132 133 private final String pathSeparator = System.getProperty(PATH_SEPARATOR_PROPERTY); 134 135 public boolean isShowActions() 136 { 137 return failurePage != null && !request.isXHR(); 138 } 139 140 public void reportException(Throwable exception) 141 { 142 rootException = exception; 143 144 failurePage = (request.getAttribute(InternalConstants.ACTIVE_PAGE_LOADED) == null) 145 ? null 146 : requestGlobals.getActivePageName(); 147 148 rootURL = baseURLSource.getBaseURL(request.isSecure()); 149 } 150 151 public Object[] getReloadContext() 152 { 153 return pageActivationContextCollector.collectPageActivationContext(failurePage); 154 } 155 156 Object onActionFromReloadFirst(EventContext reloadContext) 157 { 158 reloadHelper.forceReload(); 159 160 return linkSource.createPageRenderLinkWithContext(urlEncoder.decode(request.getParameter("loadPage")), reloadContext); 161 } 162 163 Object onActionFromReloadRoot() throws MalformedURLException 164 { 165 reloadHelper.forceReload(); 166 167 return new URL(baseURLSource.getBaseURL(request.isSecure())); 168 } 169 170 171 public boolean getHasSession() 172 { 173 return request.getSession(false) != null; 174 } 175 176 public Session getSession() 177 { 178 return request.getSession(false); 179 } 180 181 public Object getAttributeValue() 182 { 183 return getSession().getAttribute(attributeName); 184 } 185 186 /** 187 * Returns a <em>sorted</em> list of system property names. 188 */ 189 public List<String> getSystemProperties() 190 { 191 return InternalUtils.sortedKeys(System.getProperties()); 192 } 193 194 public String getPropertyValue() 195 { 196 return System.getProperty(propertyName); 197 } 198 199 public boolean isComplexProperty() 200 { 201 return PATH_RECOGNIZER.matcher(propertyName).find() && getPropertyValue().contains(pathSeparator); 202 } 203 204 public String[] getComplexPropertyValue() 205 { 206 // Neither : nor ; is a regexp character. 207 208 return getPropertyValue().split(pathSeparator); 209 } 210 211 public List<ThreadInfo> getThreads() 212 { 213 return F.flow(TapestryInternalUtils.getAllThreads()).map(new Mapper<Thread, ThreadInfo>() 214 { 215 @Override 216 public ThreadInfo map(Thread t) 217 { 218 List<String> flags = CollectionFactory.newList(); 219 220 if (t.isDaemon()) 221 { 222 flags.add("daemon"); 223 } 224 if (!t.isAlive()) 225 { 226 flags.add("NOT alive"); 227 } 228 if (t.isInterrupted()) 229 { 230 flags.add("interrupted"); 231 } 232 233 if (t.getPriority() != Thread.NORM_PRIORITY) 234 { 235 flags.add("priority " + t.getPriority()); 236 } 237 238 return new ThreadInfo(Thread.currentThread() == t ? "active-thread" : "", 239 t.getName(), 240 t.getState().name(), 241 InternalUtils.join(flags), 242 t.getThreadGroup()); 243 } 244 }).sort().toList(); 245 } 246}