View Javadoc

1   /*
2    * $Source: /usr/cvsroot/melati/melati/src/main/java/org/melati/servlet/ConfigServlet.java,v $
3    * $Revision: 1.47 $
4    *
5    * Copyright (C) 2000 Tim Joyce
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     Tim Joyce <timj At paneris.org>
42   *     http://paneris.org/
43   *     68 Sandbanks Rd, Poole, Dorset. BH14 8BY. UK
44   */
45  
46  package org.melati.servlet;
47  
48  import java.io.PrintWriter;
49  import java.io.IOException;
50  
51  import javax.servlet.ServletConfig;
52  import javax.servlet.ServletException;
53  import javax.servlet.http.HttpServlet;
54  import javax.servlet.http.HttpServletRequest;
55  import javax.servlet.http.HttpServletResponse;
56  
57  import org.melati.Melati;
58  import org.melati.PoemContext;
59  import org.melati.MelatiConfig;
60  import org.melati.poem.AccessPoemException;
61  import org.melati.poem.NoSuchRowPoemException;
62  import org.melati.util.ConnectionPendingException;
63  import org.melati.util.MelatiWriter;
64  
65  /**
66   * Config Servlet is the simplest way to use Melati.
67   *
68   * All a ConfigServlet does is to configure a melati and combine the
69   * doGet and doPost methods.  Importantly it does not establish a poem session
70   * leaving you to do this for yourself.
71   *
72   * If you want a poem session established, please extend PoemServlet.
73   *
74   * ConfigServlet does set up a basic PoemContext with the Method set,
75   * but not the POEM logicaldatabase, table or troid.
76   *
77   * The URL is expected to take one of the following form:
78   *
79   * <BLOCKQUOTE><TT>
80   * http://<I>h</I>/<I>s</I>/<I>meth</I>
81   * </TT></BLOCKQUOTE>
82   *
83   * the method is broken out of the path info and passed to
84   * your application code in the <TT>Melati</TT> and
85   * <TT>PoemContext</TT> parameter
86   *
87   * <TABLE>
88   *   <TR>
89   *     <TD><TT><I>h</I></TT></TD>
90   *     <TD>host name, such as <TT>www.melati.org</TT></TD>
91   *   </TR>
92   *   <TR>
93   *     <TD><TT><I>s</I></TT></TD>
94   *     <TD>
95   *       servlet-determining part, such as
96   *       <TT>melati/org.melati.admin.Admin</TT>
97   *     </TD>
98   *   </TR>
99   *   <TR>
100  *     <TD><TT><I>meth</I></TT></TD>
101  *     <TD>
102  *       A freeform string telling your servlet what it is meant to do.  This
103  *       is automatically made available in templates as
104  *       <TT>$melati.Method</TT>.
105  *     </TD>
106  *   </TR>
107  * </TABLE>
108  *
109  * You can change the way these things are determined by overriding
110  * <TT>poemContext(Melati)</TT>.
111  */
112 
113 public abstract class ConfigServlet extends HttpServlet {
114 
115   /**
116    * Eclipse generated.
117    */
118   private static final long serialVersionUID = 8995954958766276122L;
119   
120   protected MelatiConfig melatiConfig;
121   protected String sysAdminName = "nobody";
122   protected String sysAdminEmail = "nobody@nobody.com";;
123   
124   /**
125    * Inititialise Melati.
126    *
127    * @param config a <code>ServletConfig</code>
128    * @throws ServletException is anything goes wrong
129    */
130   public void init(ServletConfig config) throws ServletException {
131     super.init(config);
132     melatiConfig = melatiConfig();
133   }
134 
135   /**
136    * Handles GET.
137    *
138    * @param request the incoming <code>HttpServletRequest</code>
139    * @param response the outgoing <code>HttpServletResponse</code>
140    */
141   public void doGet(HttpServletRequest request, 
142                     HttpServletResponse response) {
143     doGetPostRequest(request, response);
144   }
145 
146   /**
147    * Handle a POST.
148    *
149    * @param request the incoming <code>HttpServletRequest</code>
150    * @param response the outgoing <code>HttpServletResponse</code>
151    */
152   public void doPost(HttpServletRequest request, 
153                      HttpServletResponse response) {
154     doGetPostRequest(request, response);
155   }
156 
157   /**
158    * Process the request.
159    *
160    * Exceptions are presented to the user if practicable, or written to the log. 
161    * 
162    * @param request the incoming <code>HttpServletRequest</code>
163    * @param response the outgoing <code>HttpServletResponse</code>
164    */
165   private void doGetPostRequest(final HttpServletRequest request, 
166                                 final HttpServletResponse response) {
167     Melati melati = new Melati(melatiConfig, request, response);
168     try {
169       melati.establishCharsets();
170       melati.setPoemContext(poemContext(melati));
171       doConfiguredRequest(melati);
172       // send the output to the client
173       melati.write();
174     }
175     catch (Exception e) {
176       error(melati,e);
177     }
178   }
179 
180   /**
181    * Send an error message.
182    *
183    * @param melati the {@link Melati}
184    * @param e      the {@link Exception} to report
185    */
186   public void error(Melati melati, Exception e) {
187     melati.getResponse().setStatus(httpStatusCode(e));
188     
189     // has it been trapped already, if so, we don't need to relog it here
190     if (! (e instanceof TrappedException)) {
191       try { 
192         // log it
193         e.printStackTrace(System.err);
194         // and put it on the page
195         melati.setResponseContentType ("text/html");
196         MelatiWriter mw =  melati.getWriter();
197         // get rid of anything that has been written so far
198         mw.reset();
199         PrintWriter out = new PrintWriter(mw.getWriter());
200         if (e instanceof ConnectionPendingException) {
201           writeConnectionPendingException(out,e);
202         } else {
203           writeError(out,e);
204         }
205         melati.write();
206       } catch (IOException f) {
207         e.printStackTrace(System.err);
208         throw new TrappedException("Problem logging error", f);
209       }
210     }
211   }
212   
213   protected int httpStatusCode(Exception e) {
214     if (e instanceof AccessPoemException)
215       return 401; // Not Authorized
216     if (e instanceof InvalidUsageException)
217       return 400; // Client error
218     if (e instanceof NoSuchRowPoemException)
219       return 404; // Not found
220     return 500; // Server error
221   }
222 
223   /**
224    * Print an error directly to the client.
225    *
226    * This is rarely called, eg when the template engine 
227    * fails to render the default error template.
228    *
229    * @param out the <code>PrintWriter</code> to print to 
230    * @param e   the {@link Exception} to report
231    */
232   public void writeError(PrintWriter out, Exception e) {
233     out.println("<html><head><title>Melati Error</title></head>");
234     out.println("<!-- HTML generated in " + 
235                 "org.melati.servlet.ConfigServlet.java -->");
236     out.println("<body><h2>Melati Error</h2>");
237     out.println("<h3>Reported from ConfigServlet</h3>");
238     out.println("<p>An error has occured in the application"); 
239     out.println("that runs this website, please contact <a href='mailto:");
240     out.println(getSysAdminEmail() + "'>" + getSysAdminName() + "</a>");
241     out.println(", with the information given below.</p>");
242     out.println("<h4><font color='red'><pre>");
243     e.printStackTrace(out);
244     out.println("</pre></font></h4></body></html>");
245   }    
246   
247   /**
248    * Print the <code>ConnectionPendingException</code>  directly to the client.
249    *
250    * This is called if a request is made whilst the system is 
251    * still being initialised.
252    *
253    * @param out the <code>PrintWriter</code> to print to 
254    * @param e   the {@link Exception} to report
255    */
256   public void writeConnectionPendingException(PrintWriter out, Exception e) {
257     out.println("<html><head><title>Database Initialising</title>\n");
258     out.println("<META HTTP-EQUIV='Refresh' CONTENT='30'>\n</head>\n");
259     out.println("<!-- Generated in org.melati.servlet.ConfigServlet.java -->");
260     out.println("<body><center><h2>Database Initialising</h2><p>&nbsp;</p>");
261     out.println("<p><b>Sorry</b>, ");
262     out.println("the database that runs this website is just starting up.");
263     out.println("This takes a few seconds, ");
264     out.println("so you should be able to use the site in a moment.");
265     out.println("<p>This page will refresh in 30 seconds, ");
266     out.println("and you will be able to continue.</p>");
267     out.println("<!--");
268     e.printStackTrace(out);
269     out.println("--></center></body></html>");
270   }    
271 
272   /** 
273    * This method <b>SHOULD</b> be overidden.
274    * @return the System Administrators name.
275    */
276   public String getSysAdminName () {
277     return sysAdminName;
278   }
279 
280   /** 
281    * This method <b>SHOULD</b> be overidden.
282    * @return the System Administrators email address.
283    */
284   public String getSysAdminEmail () {
285     return sysAdminEmail;
286   }
287 
288   /**
289    * @param sysAdminEmail The sysAdminEmail to set.
290    */
291   protected void setSysAdminEmail(String sysAdminEmail) {
292     this.sysAdminEmail = sysAdminEmail;
293   }
294 
295   
296   /**
297    * @param sysAdminName The sysAdminName to set.
298    */
299   protected void setSysAdminName(String sysAdminName) {
300     this.sysAdminName = sysAdminName;
301   }
302 
303   protected PoemContext poemContext(Melati melati) 
304       throws PathInfoException {
305     PoemContext it = new PoemContext();
306     String[] parts = melati.getPathInfoParts();
307     if (parts.length > 0)
308      it.setMethod(parts[parts.length - 1]);
309    return it;
310  }
311   
312   /** 
313    * To override any setting from org.melati.MelatiConfig.properties,
314    * simply override this method and return a valid MelatiConfig.
315    *
316    * eg to use a different AccessHandler from the default:
317    *
318    * <PRE>
319    *   protected MelatiConfig melatiConfig() throws MelatiException {
320    *     MelatiConfig config = super.melatiConfig();
321    *     config.setAccessHandler(new YourAccessHandler());
322    *     return config;
323    *   }
324    * </PRE>
325    * 
326    * @return a new {@link MelatiConfig}
327    */
328   protected MelatiConfig melatiConfig() {
329     MelatiConfig m = new MelatiConfig();
330     String realPath = getServletConfig().getServletContext().getRealPath("/");
331     if (realPath == null)
332       throw new NullPointerException();
333     m.setRealPath(realPath);
334     return m;
335   }
336   
337   /**
338    * Instantiate this method to build up your own output.
339    * @param melati
340    * @throws Exception if anything goes wrong
341    */
342   protected abstract void doConfiguredRequest(Melati melati)
343       throws Exception;
344 
345   
346 }