working on encoding
[mir.git] / source / mir / servlet / ServletModule.java
1 /*
2  * Copyright (C) 2001, 2002  The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with the com.oreilly.servlet library, any library
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of
24  * the above that use the same license as the above), and distribute linked
25  * combinations including the two.  You must obey the GNU General Public
26  * License in all respects for all of the code used other than the above
27  * mentioned libraries.  If you modify this file, you may extend this exception
28  * to your version of the file, but you are not obligated to do so.  If you do
29  * not wish to do so, delete this exception statement from your version.
30  */
31
32 package mir.servlet;
33
34 import java.io.PrintWriter;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse;
41 import javax.servlet.http.HttpSession;
42
43 import freemarker.template.SimpleHash;
44 import freemarker.template.TemplateModelRoot;
45 import mir.config.MirPropertiesConfiguration;
46 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;
47 import mir.entity.EntityList;
48 import mir.log.LoggerWrapper;
49 import mir.misc.HTMLTemplateProcessor;
50 import mir.misc.LineFilterWriter;
51 import mir.module.AbstractModule;
52 import mir.storage.StorageObject;
53 import mir.util.HTTPRequestParser;
54
55
56
57
58 /**
59  * Abstract class ServletModule provides the base functionality for servlets.
60  * Deriving a class from ServletModule enables class to insert/edit/update/delete
61  * and list Entity from a Database via mainModule.
62  *
63  *
64  *  Abstrakte Klasse ServletModule stellt die Basisfunktionalitaet der
65  *  abgeleiteten ServletModule zur Verf?gung.
66  *
67  * @version 28.6.1999
68  * @author RK
69  */
70
71 public abstract class ServletModule {
72
73   public String defaultAction;
74   protected LoggerWrapper logger;
75   protected MirPropertiesConfiguration configuration;
76   protected AbstractModule mainModule;
77   protected Locale fallbackLocale;
78   protected String templateListString;
79   protected String templateObjektString;
80   protected String templateConfirmString;
81
82
83   public ServletModule(){
84     try {
85       configuration = MirPropertiesConfiguration.instance();
86     }
87     catch (PropertiesConfigExc e) {
88       throw new RuntimeException("Can't get configuration: " + e.getMessage());
89     }
90
91     fallbackLocale = new Locale(configuration.getString("Mir.Admin.FallbackLanguage", "en"), "");
92   }
93
94
95   /**
96    * Singelton - Methode muss in den abgeleiteten Klassen ueberschrieben werden.
97    * @return ServletModule
98    */
99   public static ServletModule getInstance() {
100     return null;
101   }
102
103   /**
104    * get the module name to be used for generic operations like delete.
105    */
106   protected String getOperationModuleName() {
107     return getClass().getName().substring((new String("mircoders.servlet.ServletModule")).length());
108   }
109
110   /**
111    * get the locale either from the session or the accept-language header ot the request
112    * this supersedes getLanguage for the new i18n
113    */
114   public Locale getLocale(HttpServletRequest req) {
115     Locale loc = null;
116     HttpSession session = req.getSession(false);
117     if (session != null) {
118       // session can be null in case of logout
119       loc = (Locale) session.getAttribute("locale");
120     }
121     // if there is nothing in the session get it fron the accept-language
122     if (loc == null) {
123       loc = req.getLocale();
124     }
125     return loc;
126   }
127
128   /**
129    * get the locale either from the session or the accept-language header ot the request
130    * this supersedes getLanguage for the new i18n
131    */
132   public Locale getFallbackLocale(HttpServletRequest req) {
133     return fallbackLocale;
134   }
135
136   public void redirect(HttpServletResponse aResponse, String aQuery) throws ServletModuleExc, ServletModuleFailure {
137     try {
138       aResponse.sendRedirect(MirPropertiesConfiguration.instance().getString("RootUri") + "/Mir?"+aQuery);
139     }
140     catch (Throwable t) {
141       throw new ServletModuleFailure("ServletModule.redirect: " +t.getMessage(), t);
142     }
143   }
144
145   /**
146    *  list(req,res) - generische Listmethode. Wennn die Funktionalitaet
147    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
148    *  ueberschreiben werden.
149    *
150    * @param req Http-Request, das vom Dispatcher durchgereicht wird
151    * @param res Http-Response, die vom Dispatcher durchgereicht wird
152    */
153   public void list(HttpServletRequest req, HttpServletResponse res)
154       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
155     try {
156       EntityList theList;
157       String offsetParam = req.getParameter("offset");
158       int offset = 0;
159       PrintWriter out = res.getWriter();
160
161       // hier offsetcode bearbeiten
162       if (offsetParam != null && !offsetParam.equals("")) {
163         offset = Integer.parseInt(offsetParam);
164       }
165       if (req.getParameter("next") != null) {
166         offset = Integer.parseInt(req.getParameter("nextoffset"));
167       }
168       else {
169         if (req.getParameter("prev") != null) {
170           offset = Integer.parseInt(req.getParameter("prevoffset"));
171         }
172       }
173       theList = mainModule.getByWhereClause(null, offset);
174
175       HTMLTemplateProcessor.process(res, templateListString, theList, null, null, out, getLocale(req), getFallbackLocale(req));
176     }
177     catch (Throwable e) {
178       throw new ServletModuleFailure(e);
179     }
180   }
181
182   /**
183    *  add(req,res) - generische Addmethode. Wennn die Funktionalitaet
184    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
185    *  ueberschreiben werden.
186    * @param req Http-Request, das vom Dispatcher durchgereicht wird
187    * @param res Http-Response, die vom Dispatcher durchgereicht wird
188    */
189   public void add(HttpServletRequest req, HttpServletResponse res)
190       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
191
192     try {
193       SimpleHash mergeData = new SimpleHash();
194       mergeData.put("new", "1");
195       deliver(req, res, mergeData, templateObjektString);
196     }
197     catch (Throwable e) {
198       throw new ServletModuleFailure(e);
199     }
200   }
201
202   /**
203    *  insert(req,res) - generische Insertmethode, folgt auf add.
204    *  Wennn die Funktionalitaet
205    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
206    *  ueberschreiben werden.
207    *
208    * @param req Http-Request, das vom Dispatcher durchgereicht wird
209    * @param res Http-Response, die vom Dispatcher durchgereicht wird
210    */
211   public void insert(HttpServletRequest req, HttpServletResponse res)
212       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
213     try {
214       Map withValues = getIntersectingValues(req, mainModule.getStorageObject());
215       logger.debug("--trying to add...");
216       String id = mainModule.add(withValues);
217       logger.debug("--trying to deliver..." + id);
218       list(req, res);
219     }
220     catch (Throwable e) {
221       throw new ServletModuleFailure(e);
222     }
223   }
224
225   /**
226    *  delete(req,res) - generic delete method. Can be overridden in subclasses.
227    *
228    */
229
230   public void delete(HttpServletRequest req, HttpServletResponse res)
231       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
232     try {
233       String idParam = req.getParameter("id");
234
235       if (idParam == null)
236         throw new ServletModuleExc("Invalid call to delete: no id supplied");
237
238       String confirmParam = req.getParameter("confirm");
239       String cancelParam = req.getParameter("cancel");
240       if (confirmParam == null && cancelParam == null) {
241         SimpleHash mergeData = new SimpleHash();
242
243         mergeData.put("module", getOperationModuleName());
244         mergeData.put("infoString", getOperationModuleName() + ": " + idParam);
245         mergeData.put("id", idParam);
246         mergeData.put("where", req.getParameter("where"));
247         mergeData.put("order", req.getParameter("order"));
248         mergeData.put("offset", req.getParameter("offset"));
249         // this stuff is to be compatible with the other more advanced
250         // search method used for media and comments
251         mergeData.put("query_media_folder", req.getParameter("query_media_folder"));
252         mergeData.put("query_is_published", req.getParameter("query_is_published"));
253         mergeData.put("query_text", req.getParameter("query_text"));
254         mergeData.put("query_field", req.getParameter("query_field"));
255
256         deliver(req, res, mergeData, templateConfirmString);
257       }
258       else {
259         if (confirmParam != null && !confirmParam.equals("")) {
260           //theLog.printInfo("delete confirmed!");
261           mainModule.deleteById(idParam);
262           list(req, res); // back to list
263         }
264         else {
265           if (req.getParameter("where") != null)
266             list(req, res);
267           else
268             edit(req, res);
269         }
270       }
271     }
272     catch (Throwable e) {
273       throw new ServletModuleFailure(e);
274     }
275   }
276
277   /**
278    *  edit(req,res) - generische Editmethode. Wennn die Funktionalitaet
279    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
280    *  ueberschreiben werden.
281    *
282    * @param req Http-Request, das vom Dispatcher durchgereicht wird
283    * @param res Http-Response, die vom Dispatcher durchgereicht wird
284    */
285   public void edit(HttpServletRequest req, HttpServletResponse res)
286       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
287     edit(req, res, req.getParameter("id"));
288   }
289
290   /**
291    *  edit(req,res) - generische Editmethode. Wennn die Funktionalitaet
292    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
293    *  ueberschreiben werden.
294    *
295    * @param req Http-Request, das vom Dispatcher durchgereicht wird
296    * @param res Http-Response, die vom Dispatcher durchgereicht wird
297    */
298   public void edit(HttpServletRequest aRequest, HttpServletResponse aResponse, String anIdentifier)
299       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
300     try {
301       deliver(aRequest, aResponse, mainModule.getById(anIdentifier), templateObjektString);
302     }
303     catch (Throwable e) {
304       throw new ServletModuleFailure(e);
305     }
306   }
307
308   /**
309    *  update(req,res) - generische Updatemethode. Wennn die Funktionalitaet
310    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
311    *  ueberschreiben werden.
312    *
313    * @param req Http-Request, das vom Dispatcher durchgereicht wird
314    * @param res Http-Response, die vom Dispatcher durchgereicht wird
315    */
316
317   public void update(HttpServletRequest req, HttpServletResponse res)
318       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
319     try {
320       String idParam = req.getParameter("id");
321       Map withValues = getIntersectingValues(req, mainModule.getStorageObject());
322
323       String id = mainModule.set(withValues);
324       String whereParam = req.getParameter("where");
325       String orderParam = req.getParameter("order");
326
327       if ((whereParam != null && !whereParam.equals("")) || (orderParam != null && !orderParam.equals(""))) {
328         list(req, res);
329       }
330       else {
331         edit(req, res);
332       }
333     }
334     catch (Throwable e) {
335       throw new ServletModuleFailure(e);
336     }
337   }
338
339   /**
340    * deliver liefert das Template mit dem Filenamen templateFilename
341    * an den HttpServletResponse res aus, nachdem es mit den Daten aus
342    * TemplateModelRoot rtm gemischt wurde
343    *
344    * @param res Http-Response, die vom Dispatcher durchgereicht wird
345    * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
346    *   Daten, die ins Template gemerged werden sollen.
347    * @param tmpl Name des Templates
348    * @exception ServletModuleException
349    */
350   public void deliver(HttpServletRequest req, HttpServletResponse res, TemplateModelRoot rtm,
351          TemplateModelRoot popups, String templateFilename) throws ServletModuleFailure  {
352     if (rtm == null)
353       rtm = new SimpleHash();
354
355     try {
356       PrintWriter out = res.getWriter();
357       HTMLTemplateProcessor.process(res, templateFilename, rtm, popups, out, getLocale(req), getFallbackLocale(req));
358
359       // we default to admin bundles here, which is not exactly beautiful...
360       // but this whole producer stuff is going to be rewritten soon.
361       // ServletModuleOpenIndy overwrites deliver() to use open bundles
362       // (br1)
363       out.close();
364     }
365     catch (Throwable e) {
366       throw new ServletModuleFailure(e);
367     }
368   }
369
370
371   /**
372    * deliver liefert das Template mit dem Filenamen templateFilename
373    * an den HttpServletResponse res aus, nachdem es mit den Daten aus
374    * TemplateModelRoot rtm gemischt wurde
375    *
376    * @param res Http-Response, die vom Dispatcher durchgereicht wird
377    * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
378    *   Daten, die ins Template gemerged werden sollen.
379    * @param tmpl Name des Templates
380    * @exception ServletModuleException
381    */
382   public void deliver(HttpServletRequest req, HttpServletResponse res,
383         TemplateModelRoot rtm, String templateFilename) throws ServletModuleFailure {
384     deliver(req, res, rtm, null, templateFilename);
385   }
386
387   /**
388    * deliver liefert das Template mit dem Filenamen templateFilename
389    * an den HttpServletResponse res aus, nachdem es mit den Daten aus
390    * TemplateModelRoot rtm gemischt wurde
391    *
392    * @param res Http-Response, die vom Dispatcher durchgereicht wird
393    * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
394    *   Daten, die ins Template gemerged werden sollen.
395    * @param tmpl Name des Templates
396    * @exception ServletModuleException
397    */
398   public void deliver_compressed(HttpServletRequest req, HttpServletResponse res,
399                                  TemplateModelRoot rtm, String templateFilename)
400       throws ServletModuleFailure {
401     if (rtm == null) rtm = new SimpleHash();
402     try {
403       PrintWriter out = new LineFilterWriter(res.getWriter());
404       //PrintWriter out =  res.getWriter();
405       HTMLTemplateProcessor.process(res, templateFilename, rtm, null, out, getLocale(req), getFallbackLocale(req));
406       out.close();
407     }
408     catch (Throwable e) {
409       throw new ServletModuleFailure(e);
410     }
411   }
412
413   /**
414    * deliver liefert das Template mit dem Filenamen templateFilename
415    * an den HttpServletResponse res aus, nachdem es mit den Daten aus
416    * TemplateModelRoot rtm gemischt wurde
417    *
418    * @param out ist der OutputStream, in den die gergten Daten geschickt werden sollen.
419    * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
420    *   Daten, die ins Template gemerged werden sollen.
421    * @param tmpl Name des Templates
422    * @exception ServletModuleException
423    */
424   private void deliver(HttpServletResponse res, HttpServletRequest req, PrintWriter out,
425                        TemplateModelRoot rtm, String templateFilename)
426       throws ServletModuleFailure {
427     try {
428       HTMLTemplateProcessor.process(res, templateFilename, rtm, null, out, getLocale(req), getFallbackLocale(req));
429     }
430     catch (Throwable e) {
431       throw new ServletModuleFailure(e);
432     }
433   }
434
435   /**
436    *  Wenn die abgeleitete Klasse diese Methode ueberschreibt und einen String mit einem
437    *  Methodennamen zurueckliefert, dann wird diese Methode bei fehlender Angabe des
438    *  doParameters ausgefuehrt.
439    *
440    * @return Name der Default-Action
441    */
442   public String defaultAction() {
443     return defaultAction;
444   }
445
446   /**
447    * Gets the fields from a httprequest and matches them with the metadata from
448    * the storage object. Returns the keys that match, with their values.
449    *
450    * @return Map with the values
451    */
452   public Map getIntersectingValues(HttpServletRequest req, StorageObject theStorage)
453       throws ServletModuleExc, ServletModuleFailure {
454
455     try {
456       HTTPRequestParser parser;
457       List theFieldList;
458
459       logger.debug("using charset: " + req.getParameter("charset"));
460       logger.debug("using method: " + req.getParameter("do"));
461       if (req.getParameter("charset") != null) {
462         parser = new HTTPRequestParser(req, req.getParameter("charset"));
463         logger.debug("using charset: " + req.getParameter("charset"));
464         logger.debug("original charset: " + req.getCharacterEncoding());
465       }
466       else {
467         parser = new HTTPRequestParser(req);
468       }
469
470       theFieldList = theStorage.getFields();
471
472       Map withValues = new HashMap();
473       String aField, aValue;
474
475       for (int i = 0; i < theFieldList.size(); i++) {
476         aField = (String) theFieldList.get(i);
477
478         aValue = parser.getParameter(aField);
479         if (aValue != null)
480           withValues.put(aField, aValue);
481       }
482       return withValues;
483     }
484     catch (Throwable e) {
485       e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
486
487       throw new ServletModuleFailure( "ServletModule.getIntersectingValues: " + e.getMessage(), e);
488     }
489   }
490
491 }