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    }