001// Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002// The Molly framework is freely distributable under the terms of an 003// MIT-style license. For details, see the molly pages web site at: 004// http://www.mollypages.org/. Use, modify, have fun ! 005 006package fc.web.page; 007 008import java.io.*; 009import java.util.*; 010 011import javax.servlet.*; 012import javax.servlet.http.*; 013 014import fc.util.*; 015import fc.io.*; 016 017/** 018A superclass for generated pages. All pages derive from this 019class. 020 021@author hursh jain 022*/ 023public class PageImpl implements Page 024{ 025static private final boolean internal_dbg = false; 026 027/** 028The default application log, retrieved via {@link 029fc.web.servlet.WebApp#getAppLog() WebApp.getAppLog}. All molly pages can use 030this variable directly, for example <code>log.info("test")</code>. The log 031output level (and hence whether a logging statement at that level will be seen) 032is set by the WebApp configuration file. (If WebApp is <i>not</i> used or 033configured, then the log level will be set to the {@link Log#DEFAULT_LEVEL}) 034*/ 035public Log log; 036protected HttpServlet servlet; 037private static ThreadLocal watchMap = new ThreadLocal(); 038private final ThreadLocal threadLocalOut = new ThreadLocal(); 039private String dbg_prefix = ""; 040private String dbg_suffix = ""; 041 042public void render(HttpServletRequest req, HttpServletResponse res) 043throws Exception 044 { 045 throw new ServletException("Not implemented"); 046 } 047 048public void init(PageServlet servlet, String contextRelativePagePath) throws ServletException 049 { 050 this.log = Log.get(contextRelativePagePath); 051 this.servlet = servlet; 052 if (internal_dbg) System.out.println(getClass().getName() + " init() called:" + ClassUtil.getClassLoaderInfo(PageImpl.class)); 053 } 054 055public void destroy() { 056 log.close(); 057 if (internal_dbg) System.out.println(getClass().getName() + ClassUtil.getClassLoaderInfo(PageImpl.class)); 058 } 059 060public String getPagePath(HttpServletRequest req) 061 { 062 return req.getContextPath() + req.getServletPath(); 063 } 064 065public String getRealPath(HttpServletRequest req) 066 { 067 return 068 servlet.getServletConfig().getServletContext().getRealPath(getPagePath(req)); 069 } 070 071public void clientRedirect( 072 HttpServletRequest req, HttpServletResponse res, String newLocation) 073throws IOException 074 { 075 String redirectURL = fc.web.servlet.WebUtil.absolutePath(req, newLocation); 076 res.sendRedirect(redirectURL); 077 } 078 079public CharArrayWriter getThreadLocalWriter() 080 { 081 CharArrayWriter out = (CharArrayWriter) threadLocalOut.get(); 082 083 if (out == null) { 084 out = new CharArrayWriter(); 085 threadLocalOut.set(out); 086 } 087 088 return out; 089 } 090 091/** 092If set to <tt>true</tt>, a timer to calculate page render time 093is started after this method call. Page render time can then be 094displaying the value returned by invoking the {@link #getTime} 095method. 096*/ 097public final void startTimer() 098 { 099 //each thread running thru the page has it's own watch. 100 Watch watch = getWatch(); 101 if (watch == null) { 102 watch = new Watch(); 103 watchMap.set(watch); 104 } 105 watch.start(); 106 } 107 108/** 109Returns the time elapsed after invoking {@link startTime} method (in milliseconds) 110*/ 111public final long getTime() 112 { 113 Watch watch = getWatch(); 114 if (watch == null) 115 throw new RuntimeException("The startTimer() method must be invoked prior to invoking this method. Timers are thread local different processing threads get their own private timers. You must startTimer() and getTime() inside the render method, don't override the init() method to startTimer().."); 116 return watch.time(); 117 } 118 119private final Watch getWatch() 120 { 121 return (Watch) watchMap.get(); 122 } 123 124/** 125Controls whether debugging is on or off. For expensive debug statements, 126<code>if (dbg) bug(....)</code> type statements can be used in a page. 127*/ 128protected volatile boolean dbg = false; 129 130/** 131Starts/stop debugging with no dbg_prefix/dbg_suffix. 132 133@param val true to enable debugging, false to disable. 134*/ 135public final void dbg(final boolean val) 136 { 137 this.dbg = val; 138 } 139 140/** 141Sets the prefix for debugging output. 142 143@param dbg_prefix some html/text (such as <xmp><font color=red></xmp>) that 144 is printed before each debug statement 145*/ 146public final void dbgPrefix(String dbg_prefix) 147 { 148 this.dbg_prefix = dbg_prefix; 149 } 150 151/** 152Sets the suffix for debugging output. 153 154@param dbg_suffix some html/text that is printed after each debug statement 155*/ 156public final void dbgSuffix(String dbg_suffix) 157 { 158 this.dbg_suffix = dbg_suffix; 159 } 160 161/** 162Prints a debug statement if debugging is turned on for this page. 163 164<p>Typically the implicit page printwriter (the <code>out</code> 165variable) will be passed to this method and debug statements will be 166printed at the point where they are lexically invoked at the page. 167<p> 168However, each page request thread can collect debugging information 169and print the output at some arbitrary location, such as the bottom 170of the page. The method {@link #getThreadLocalWriter()} exists for 171this reason and can be used to collect thread-local output during 172page execution. 173*/ 174public final void bug(final Writer writer, final Object str1) throws IOException 175 { 176 if (! dbg) 177 return; 178 179 writer.append(dbg_prefix); 180 writer.append(str1 != null ? str1.toString() : "null"); 181 writer.append(dbg_suffix); 182 } 183 184public final void bug(final Writer writer, 185 final Object str1, final Object str2) throws IOException 186 { 187 if (! dbg) 188 return; 189 190 writer.append(dbg_prefix); 191 writer.append(str1 != null ? str1.toString() : "null"); 192 writer.append(str2 != null ? str2.toString() : "null"); 193 writer.append(dbg_suffix); 194 } 195 196public final void bug(final Writer writer, 197 final Object str1, final Object str2, final Object str3) throws IOException 198 { 199 if (! dbg) 200 return; 201 202 writer.append(dbg_prefix); 203 writer.append(str1 != null ? str1.toString() : "null"); 204 writer.append(str2 != null ? str2.toString() : "null"); 205 writer.append(str3 != null ? str3.toString() : "null"); 206 writer.append(dbg_suffix); 207 } 208 209public final void bug(final Writer writer, 210 final Object str1, final Object str2, final Object str3, final Object... args) 211throws IOException 212 { 213 if (! dbg) 214 return; 215 216 writer.append(dbg_prefix); 217 writer.append(str1 != null ? str1.toString() : "null"); 218 writer.append(str2 != null ? str2.toString() : "null"); 219 writer.append(str3 != null ? str3.toString() : "null"); 220 final int len = args.length; 221 for (int i = 0; i < len; i++) { 222 writer.append(args[i] != null ? args[i].toString() : "null"); 223 } 224 writer.append(dbg_suffix); 225 } 226 227/* -------- "debug" as well as "bug" ----------- */ 228 229/** 230Prints a debug statement if debugging is turned on for this page. Same 231as calling {@link #bug(Writer, Object)}. 232*/ 233public final void debug(final Writer writer, final Object str1) throws IOException 234 { 235 bug(writer, str1); 236 } 237 238public final void debug(final Writer writer, 239 final Object str1, final Object str2) throws IOException 240 { 241 bug(writer, str1, str2); 242 } 243 244public final void debug(final Writer writer, 245 final Object str1, final Object str2, final Object str3) throws IOException 246 { 247 bug(writer, str1, str2, str3); 248 } 249 250public final void debug(final Writer writer, 251 final Object str1, final Object str2, final Object str3, final Object... args) 252throws IOException 253 { 254 bug(writer, str1, str2, str3, args); 255 } 256 257}