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