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    }