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}