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 006 package fc.web.page; 007 008 import java.io.*; 009 import java.util.*; 010 011 import javax.servlet.*; 012 import javax.servlet.http.*; 013 014 import fc.util.*; 015 import fc.io.*; 016 017 /** 018 A superclass for generated pages. All pages derive from this 019 class. 020 021 @author hursh jain 022 */ 023 public class PageImpl implements Page 024 { 025 static private final boolean internal_dbg = false; 026 027 /** 028 The default application log, retrieved via {@link 029 fc.web.servlet.WebApp#getAppLog() WebApp.getAppLog}. All molly pages can use 030 this variable directly, for example <code>log.info("test")</code>. The log 031 output level (and hence whether a logging statement at that level will be seen) 032 is set by the WebApp configuration file. (If WebApp is <i>not</i> used or 033 configured, then the log level will be set to the {@link Log#DEFAULT_LEVEL}) 034 */ 035 public Log log; 036 protected HttpServlet servlet; 037 private static ThreadLocal watchMap = new ThreadLocal(); 038 private final ThreadLocal threadLocalOut = new ThreadLocal(); 039 private String dbg_prefix = ""; 040 private String dbg_suffix = ""; 041 042 public void render(HttpServletRequest req, HttpServletResponse res) 043 throws Exception 044 { 045 throw new ServletException("Not implemented"); 046 } 047 048 public 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 055 public void destroy() { 056 log.close(); 057 if (internal_dbg) System.out.println(getClass().getName() + ClassUtil.getClassLoaderInfo(PageImpl.class)); 058 } 059 060 public String getPagePath(HttpServletRequest req) 061 { 062 return req.getContextPath() + req.getServletPath(); 063 } 064 065 public String getRealPath(HttpServletRequest req) 066 { 067 return 068 servlet.getServletConfig().getServletContext().getRealPath(getPagePath(req)); 069 } 070 071 public void clientRedirect( 072 HttpServletRequest req, HttpServletResponse res, String newLocation) 073 throws IOException 074 { 075 String redirectURL = fc.web.servlet.WebUtil.absolutePath(req, newLocation); 076 res.sendRedirect(redirectURL); 077 } 078 079 public 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 /** 092 If set to <tt>true</tt>, a timer to calculate page render time 093 is started after this method call. Page render time can then be 094 displaying the value returned by invoking the {@link #getTime} 095 method. 096 */ 097 public 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 /** 109 Returns the time elapsed after invoking {@link startTime} method (in milliseconds) 110 */ 111 public 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 119 private final Watch getWatch() 120 { 121 return (Watch) watchMap.get(); 122 } 123 124 /** 125 Controls 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 */ 128 protected volatile boolean dbg = false; 129 130 /** 131 Starts/stop debugging with no dbg_prefix/dbg_suffix. 132 133 @param val true to enable debugging, false to disable. 134 */ 135 public final void dbg(final boolean val) 136 { 137 this.dbg = val; 138 } 139 140 /** 141 Sets 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 */ 146 public final void dbgPrefix(String dbg_prefix) 147 { 148 this.dbg_prefix = dbg_prefix; 149 } 150 151 /** 152 Sets the suffix for debugging output. 153 154 @param dbg_suffix some html/text that is printed after each debug statement 155 */ 156 public final void dbgSuffix(String dbg_suffix) 157 { 158 this.dbg_suffix = dbg_suffix; 159 } 160 161 /** 162 Prints a debug statement if debugging is turned on for this page. 163 164 <p>Typically the implicit page printwriter (the <code>out</code> 165 variable) will be passed to this method and debug statements will be 166 printed at the point where they are lexically invoked at the page. 167 <p> 168 However, each page request thread can collect debugging information 169 and print the output at some arbitrary location, such as the bottom 170 of the page. The method {@link #getThreadLocalWriter()} exists for 171 this reason and can be used to collect thread-local output during 172 page execution. 173 */ 174 public 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 184 public 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 196 public 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 209 public final void bug(final Writer writer, 210 final Object str1, final Object str2, final Object str3, final Object... args) 211 throws 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 /** 230 Prints a debug statement if debugging is turned on for this page. Same 231 as calling {@link #bug(Writer, Object)}. 232 */ 233 public final void debug(final Writer writer, final Object str1) throws IOException 234 { 235 bug(writer, str1); 236 } 237 238 public final void debug(final Writer writer, 239 final Object str1, final Object str2) throws IOException 240 { 241 bug(writer, str1, str2); 242 } 243 244 public 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 250 public final void debug(final Writer writer, 251 final Object str1, final Object str2, final Object str3, final Object... args) 252 throws IOException 253 { 254 bug(writer, str1, str2, str3, args); 255 } 256 257 }