ConfigServlet.java
/*
* $Source$
* $Revision$
*
* Copyright (C) 2000 Tim Joyce
*
* Part of Melati (http://melati.org), a framework for the rapid
* development of clean, maintainable web applications.
*
* Melati is free software; Permission is granted to copy, distribute
* and/or modify this software under the terms either:
*
* a) the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version,
*
* or
*
* b) any version of the Melati Software License, as published
* at http://melati.org
*
* You should have received a copy of the GNU General Public License and
* the Melati Software License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
* GNU General Public License and visit http://melati.org to obtain the
* Melati Software License.
*
* Feel free to contact the Developers of Melati (http://melati.org),
* if you would like to work out a different arrangement than the options
* outlined here. It is our intention to allow Melati to be used by as
* wide an audience as possible.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Contact details for copyright holder:
*
* Tim Joyce <timj At paneris.org>
* http://paneris.org/
* 68 Sandbanks Rd, Poole, Dorset. BH14 8BY. UK
*/
package org.melati.servlet;
import java.io.PrintWriter;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.melati.Melati;
import org.melati.PoemContext;
import org.melati.MelatiConfig;
import org.melati.poem.AccessPoemException;
import org.melati.poem.NoSuchRowPoemException;
import org.melati.util.ConnectionPendingException;
import org.melati.util.MelatiWriter;
/**
* Config Servlet is the simplest way to use Melati.
*
* All a ConfigServlet does is to configure a melati and combine the
* doGet and doPost methods. Importantly it does not establish a poem session
* leaving you to do this for yourself.
*
* If you want a poem session established, please extend PoemServlet.
*
* ConfigServlet does set up a basic PoemContext with the Method set,
* but not the POEM logicaldatabase, table or troid.
*
* The URL is expected to take one of the following form:
*
* <BLOCKQUOTE><TT>
* http://<I>h</I>/<I>s</I>/<I>meth</I>
* </TT></BLOCKQUOTE>
*
* the method is broken out of the path info and passed to
* your application code in the <TT>Melati</TT> and
* <TT>PoemContext</TT> parameter
*
* <TABLE>
* <caption>Path context elements</caption>
* <TR>
* <TD><TT><I>h</I></TT></TD>
* <TD>host name, such as <TT>www.melati.org</TT></TD>
* </TR>
* <TR>
* <TD><TT><I>s</I></TT></TD>
* <TD>
* servlet-determining part, such as
* <TT>melati/org.melati.admin.Admin</TT>
* </TD>
* </TR>
* <TR>
* <TD><TT><I>meth</I></TT></TD>
* <TD>
* A freeform string telling your servlet what it is meant to do. This
* is automatically made available in templates as
* <TT>$melati.Method</TT>.
* </TD>
* </TR>
* </TABLE>
*
* You can change the way these things are determined by overriding
* <TT>poemContext(Melati)</TT>.
*/
public abstract class ConfigServlet extends HttpServlet {
/**
* Eclipse generated.
*/
private static final long serialVersionUID = 8995954958766276122L;
protected MelatiConfig melatiConfig;
protected String sysAdminName = "nobody";
protected String sysAdminEmail = "nobody@nobody.com";;
/**
* Inititialise Melati.
*
* @param config a <code>ServletConfig</code>
* @throws ServletException is anything goes wrong
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
melatiConfig = melatiConfig();
}
/**
* Handles GET.
*
* @param request the incoming <code>HttpServletRequest</code>
* @param response the outgoing <code>HttpServletResponse</code>
*/
public void doGet(HttpServletRequest request,
HttpServletResponse response) {
doGetPostRequest(request, response);
}
/**
* Handle a POST.
*
* @param request the incoming <code>HttpServletRequest</code>
* @param response the outgoing <code>HttpServletResponse</code>
*/
public void doPost(HttpServletRequest request,
HttpServletResponse response) {
doGetPostRequest(request, response);
}
/**
* Process the request.
*
* Exceptions are presented to the user if practicable, or written to the log.
*
* @param request the incoming <code>HttpServletRequest</code>
* @param response the outgoing <code>HttpServletResponse</code>
*/
private void doGetPostRequest(final HttpServletRequest request,
final HttpServletResponse response) {
Melati melati = new Melati(melatiConfig, request, response);
try {
melati.establishCharsets();
melati.setPoemContext(poemContext(melati));
doConfiguredRequest(melati);
// send the output to the client
melati.write();
}
catch (Exception e) {
error(melati,e);
}
}
/**
* Send an error message.
*
* @param melati the {@link Melati}
* @param e the {@link Exception} to report
*/
public void error(Melati melati, Exception e) {
melati.getResponse().setStatus(httpStatusCode(e));
// has it been trapped already, if so, we don't need to relog it here
if (! (e instanceof TrappedException)) {
try {
// log it
e.printStackTrace(System.err);
// and put it on the page
melati.setResponseContentType ("text/html");
MelatiWriter mw = melati.getWriter();
// get rid of anything that has been written so far
mw.reset();
PrintWriter out = new PrintWriter(mw.getWriter());
if (e instanceof ConnectionPendingException) {
writeConnectionPendingException(out,e);
} else {
writeError(out,e);
}
melati.write();
} catch (IOException f) {
e.printStackTrace(System.err);
throw new TrappedException("Problem logging error", f);
}
}
}
protected int httpStatusCode(Exception e) {
if (e instanceof AccessPoemException)
return 401; // Not Authorized
if (e instanceof InvalidUsageException)
return 400; // Client error
if (e instanceof NoSuchRowPoemException)
return 404; // Not found
return 500; // Server error
}
/**
* Print an error directly to the client.
*
* This is rarely called, eg when the template engine
* fails to render the default error template.
*
* @param out the <code>PrintWriter</code> to print to
* @param e the {@link Exception} to report
*/
public void writeError(PrintWriter out, Exception e) {
out.println("<html><head><title>Melati Error</title></head>");
out.println("<!-- HTML generated in " +
"org.melati.servlet.ConfigServlet.java -->");
out.println("<body><h2>Melati Error</h2>");
out.println("<h3>Reported from ConfigServlet</h3>");
out.println("<p>An error has occured in the application");
out.println("that runs this website, please contact <a href='mailto:");
out.println(getSysAdminEmail() + "'>" + getSysAdminName() + "</a>");
out.println(", with the information given below.</p>");
out.println("<h4><font color='red'><pre>");
e.printStackTrace(out);
out.println("</pre></font></h4></body></html>");
}
/**
* Print the <code>ConnectionPendingException</code> directly to the client.
*
* This is called if a request is made whilst the system is
* still being initialised.
*
* @param out the <code>PrintWriter</code> to print to
* @param e the {@link Exception} to report
*/
public void writeConnectionPendingException(PrintWriter out, Exception e) {
out.println("<html><head><title>Database Initialising</title>\n");
out.println("<META HTTP-EQUIV='Refresh' CONTENT='30'>\n</head>\n");
out.println("<!-- Generated in org.melati.servlet.ConfigServlet.java -->");
out.println("<body><center><h2>Database Initialising</h2><p> </p>");
out.println("<p><b>Sorry</b>, ");
out.println("the database that runs this website is just starting up.");
out.println("This takes a few seconds, ");
out.println("so you should be able to use the site in a moment.");
out.println("<p>This page will refresh in 30 seconds, ");
out.println("and you will be able to continue.</p>");
out.println("<!--");
e.printStackTrace(out);
out.println("--></center></body></html>");
}
/**
* This method <b>SHOULD</b> be overidden.
* @return the System Administrators name.
*/
public String getSysAdminName () {
return sysAdminName;
}
/**
* This method <b>SHOULD</b> be overidden.
* @return the System Administrators email address.
*/
public String getSysAdminEmail () {
return sysAdminEmail;
}
/**
* @param sysAdminEmail The sysAdminEmail to set.
*/
protected void setSysAdminEmail(String sysAdminEmail) {
this.sysAdminEmail = sysAdminEmail;
}
/**
* @param sysAdminName The sysAdminName to set.
*/
protected void setSysAdminName(String sysAdminName) {
this.sysAdminName = sysAdminName;
}
protected PoemContext poemContext(Melati melati)
throws PathInfoException {
PoemContext it = new PoemContext();
String[] parts = melati.getPathInfoParts();
if (parts.length > 0)
it.setMethod(parts[parts.length - 1]);
return it;
}
/**
* To override any setting from org.melati.MelatiConfig.properties,
* simply override this method and return a valid MelatiConfig.
*
* eg to use a different AccessHandler from the default:
*
* <PRE>
* protected MelatiConfig melatiConfig() throws MelatiException {
* MelatiConfig config = super.melatiConfig();
* config.setAccessHandler(new YourAccessHandler());
* return config;
* }
* </PRE>
*
* @return a new {@link MelatiConfig}
*/
protected MelatiConfig melatiConfig() {
MelatiConfig m = new MelatiConfig();
String realPath = getServletConfig().getServletContext().getRealPath("/");
if (realPath == null)
throw new NullPointerException();
m.setRealPath(realPath);
return m;
}
/**
* Instantiate this method to build up your own output.
* @param melati
* @throws Exception if anything goes wrong
*/
protected abstract void doConfiguredRequest(Melati melati)
throws Exception;
}