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 }