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