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