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> </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 }