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 is easier, since the level of WebApp.defaultLog is set
052 //via webapp's app.cnf (and is easy to change)
053 this.log = fc.web.servlet.WebApp.getAppLog();
054 this.servlet = servlet;
055 if (internal_dbg) System.out.println(getClass().getName() + " init() called:" + ClassUtil.getClassLoaderInfo(PageImpl.class));
056 }
057
058 public void destroy() {
059 log.close();
060 if (internal_dbg) System.out.println(getClass().getName() + ClassUtil.getClassLoaderInfo(PageImpl.class));
061 }
062
063 public String getPagePath(HttpServletRequest req)
064 {
065 return req.getContextPath() + req.getServletPath();
066 }
067
068 public String getRealPath(HttpServletRequest req)
069 {
070 return
071 servlet.getServletConfig().getServletContext().getRealPath(getPagePath(req));
072 }
073
074 public void clientRedirect(
075 HttpServletRequest req, HttpServletResponse res, String newLocation)
076 throws IOException
077 {
078 String redirectURL = fc.web.servlet.WebUtil.absolutePath(req, newLocation);
079 res.sendRedirect(redirectURL);
080 }
081
082 public CharArrayWriter getThreadLocalWriter()
083 {
084 CharArrayWriter out = (CharArrayWriter) threadLocalOut.get();
085
086 if (out == null) {
087 out = new CharArrayWriter();
088 threadLocalOut.set(out);
089 }
090
091 return out;
092 }
093
094 /**
095 If set to <tt>true</tt>, a timer to calculate page render time
096 is started after this method call. Page render time can then be
097 displaying the value returned by invoking the {@link #getTime}
098 method.
099 */
100 public final void startTimer()
101 {
102 //each thread running thru the page has it's own watch.
103 Watch watch = getWatch();
104 if (watch == null) {
105 watch = new Watch();
106 watchMap.set(watch);
107 }
108 watch.start();
109 }
110
111 /**
112 Returns the time elapsed after invoking {@link startTime} method (in milliseconds)
113 */
114 public final long getTime()
115 {
116 Watch watch = getWatch();
117 if (watch == null)
118 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()..");
119 return watch.time();
120 }
121
122 private final Watch getWatch()
123 {
124 return (Watch) watchMap.get();
125 }
126
127 /**
128 Controls whether debugging is on or off. For expensive debug statements,
129 <code>if (dbg) bug(....)</code> type statements can be used in a page.
130 */
131 protected volatile boolean dbg = false;
132
133 /**
134 Starts/stop debugging with no dbg_prefix/dbg_suffix.
135
136 @param val true to enable debugging, false to disable.
137 */
138 public final void dbg(final boolean val)
139 {
140 this.dbg = val;
141 }
142
143 /**
144 Sets the prefix for debugging output.
145
146 @param dbg_prefix some html/text (such as <xmp><font color=red></xmp>) that
147 is printed before each debug statement
148 */
149 public final void dbgPrefix(String dbg_prefix)
150 {
151 this.dbg_prefix = dbg_prefix;
152 }
153
154 /**
155 Sets the suffix for debugging output.
156
157 @param dbg_suffix some html/text that is printed after each debug statement
158 */
159 public final void dbgSuffix(String dbg_suffix)
160 {
161 this.dbg_suffix = dbg_suffix;
162 }
163
164 /**
165 Prints a debug statement if debugging is turned on for this page.
166
167 <p>Typically the implicit page printwriter (the <code>out</code>
168 variable) will be passed to this method and debug statements will be
169 printed at the point where they are lexically invoked at the page.
170 <p>
171 However, each page request thread can collect debugging information
172 and print the output at some arbitrary location, such as the bottom
173 of the page. The method {@link #getThreadLocalWriter()} exists for
174 this reason and can be used to collect thread-local output during
175 page execution.
176 */
177 public final void bug(final Writer writer, final Object str1) throws IOException
178 {
179 if (! dbg)
180 return;
181
182 writer.append(dbg_prefix);
183 writer.append(str1 != null ? str1.toString() : "null");
184 writer.append(dbg_suffix);
185 }
186
187 public final void bug(final Writer writer,
188 final Object str1, final Object str2) throws IOException
189 {
190 if (! dbg)
191 return;
192
193 writer.append(dbg_prefix);
194 writer.append(str1 != null ? str1.toString() : "null");
195 writer.append(str2 != null ? str2.toString() : "null");
196 writer.append(dbg_suffix);
197 }
198
199 public final void bug(final Writer writer,
200 final Object str1, final Object str2, final Object str3) throws IOException
201 {
202 if (! dbg)
203 return;
204
205 writer.append(dbg_prefix);
206 writer.append(str1 != null ? str1.toString() : "null");
207 writer.append(str2 != null ? str2.toString() : "null");
208 writer.append(str3 != null ? str3.toString() : "null");
209 writer.append(dbg_suffix);
210 }
211
212 public final void bug(final Writer writer,
213 final Object str1, final Object str2, final Object str3, final Object... args)
214 throws IOException
215 {
216 if (! dbg)
217 return;
218
219 writer.append(dbg_prefix);
220 writer.append(str1 != null ? str1.toString() : "null");
221 writer.append(str2 != null ? str2.toString() : "null");
222 writer.append(str3 != null ? str3.toString() : "null");
223 final int len = args.length;
224 for (int i = 0; i < len; i++) {
225 writer.append(args[i] != null ? args[i].toString() : "null");
226 }
227 writer.append(dbg_suffix);
228 }
229
230 /* -------- "debug" as well as "bug" ----------- */
231
232 /**
233 Prints a debug statement if debugging is turned on for this page. Same
234 as calling {@link #bug(Writer, Object)}.
235 */
236 public final void debug(final Writer writer, final Object str1) throws IOException
237 {
238 bug(writer, str1);
239 }
240
241 public final void debug(final Writer writer,
242 final Object str1, final Object str2) throws IOException
243 {
244 bug(writer, str1, str2);
245 }
246
247 public final void debug(final Writer writer,
248 final Object str1, final Object str2, final Object str3) throws IOException
249 {
250 bug(writer, str1, str2, str3);
251 }
252
253 public final void debug(final Writer writer,
254 final Object str1, final Object str2, final Object str3, final Object... args)
255 throws IOException
256 {
257 bug(writer, str1, str2, str3, args);
258 }
259
260 }