e2e1d2ab30ae81c53ad0f06b2a37577bb76a8090
[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 freemarker.template.SimpleHash;
35 import freemarker.template.TemplateModelRoot;
36 import freemarker.template.TemplateModel;
37
38 import mir.entity.EntityList;
39 import mir.log.*;
40 import mir.misc.*;
41 import mir.module.AbstractModule;
42 import mir.module.ModuleException;
43 import mir.storage.StorageObject;
44 import mir.storage.StorageObjectException;
45
46 import javax.servlet.http.HttpServletRequest;
47 import javax.servlet.http.HttpServletResponse;
48 import javax.servlet.http.HttpSession;
49 import java.io.IOException;
50 import java.io.PrintWriter;
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.Locale;
54
55
56 /**
57  * Abstract class ServletModule provides the base functionality for servlets.
58  * Deriving a class from ServletModule enables class to insert/edit/update/delete
59  * and list Entity from a Database via mainModule.
60  *
61  *
62  *  Abstrakte Klasse ServletModule stellt die Basisfunktionalitaet der
63  *  abgeleiteten ServletModule zur Verfügung.
64  *
65  * @version 28.6.1999
66  * @author RK
67  */
68
69 public abstract class ServletModule {
70
71   public String defaultAction;
72   protected LoggerWrapper logger;
73
74   protected AbstractModule mainModule;
75   protected String templateListString;
76   protected String templateObjektString;
77   protected String templateConfirmString;
78
79   /**
80    * Singelton - Methode muss in den abgeleiteten Klassen ueberschrieben werden.
81    * @return ServletModule
82    */
83   public static ServletModule getInstance() {
84     return null;
85   }
86
87   /**
88    * get the module name to be used for generic operations like delete.
89    */
90   protected String getOperationModuleName() {
91     return getClass().getName().substring((new String("mircoders.servlet.ServletModule")).length());
92   }
93
94   /**
95    * get the session binded language
96    */
97   public String getLanguage(HttpServletRequest req) {
98     HttpSession session = req.getSession(false);
99     String language = (String) session.getAttribute("Language");
100     if (language == null) {
101       language = MirConfig.getProp("StandardLanguage");
102     }
103     return language;
104   }
105
106   /**
107    * get the locale either from the session or the accept-language header ot the request
108    * this supersedes getLanguage for the new i18n
109    */
110   public Locale getLocale(HttpServletRequest req) {
111     Locale loc = null;
112     HttpSession session = req.getSession(false);
113     if (session != null) {
114       // session can be null in case of logout
115       loc = (Locale) session.getAttribute("Locale");
116     }
117     // if there is nothing in the session get it fron the accept-language
118     if (loc == null) {
119       loc = req.getLocale();
120     }
121     return loc;
122   }
123
124   public void redirect(HttpServletResponse aResponse, String aQuery) throws ServletModuleException {
125     try {
126       aResponse.sendRedirect(MirConfig.getProp("RootUri") + "/Mir?"+aQuery);
127     }
128     catch (Throwable t) {
129       throw new ServletModuleException(t.getMessage());
130     }
131   }
132
133   /**
134    *  list(req,res) - generische Listmethode. Wennn die Funktionalitaet
135    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
136    *  ueberschreiben werden.
137    *
138    * @param req Http-Request, das vom Dispatcher durchgereicht wird
139    * @param res Http-Response, die vom Dispatcher durchgereicht wird
140    */
141   public void list(HttpServletRequest req, HttpServletResponse res)
142       throws ServletModuleException {
143     try {
144       EntityList theList;
145       String offsetParam = req.getParameter("offset");
146       int offset = 0;
147       PrintWriter out = res.getWriter();
148
149       // hier offsetcode bearbeiten
150       if (offsetParam != null && !offsetParam.equals("")) {
151         offset = Integer.parseInt(offsetParam);
152       }
153       if (req.getParameter("next") != null) {
154         offset = Integer.parseInt(req.getParameter("nextoffset"));
155       }
156       else {
157         if (req.getParameter("prev") != null) {
158           offset = Integer.parseInt(req.getParameter("prevoffset"));
159         }
160       }
161       theList = mainModule.getByWhereClause(null, offset);
162
163       HTMLTemplateProcessor.process(res, templateListString, theList, out, getLocale(req));
164     }
165     catch (Exception e) {
166       throw new ServletModuleException(e.getMessage());
167     }
168   }
169
170   /**
171    *  add(req,res) - generische Addmethode. Wennn die Funktionalitaet
172    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
173    *  ueberschreiben werden.
174    * @param req Http-Request, das vom Dispatcher durchgereicht wird
175    * @param res Http-Response, die vom Dispatcher durchgereicht wird
176    */
177   public void add(HttpServletRequest req, HttpServletResponse res)
178       throws ServletModuleException {
179
180     try {
181       SimpleHash mergeData = new SimpleHash();
182       mergeData.put("new", "1");
183       deliver(req, res, mergeData, templateObjektString);
184     }
185     catch (Exception e) {
186       throw new ServletModuleException(e.getMessage());
187     }
188   }
189
190   /**
191    *  insert(req,res) - generische Insertmethode, folgt auf add.
192    *  Wennn die Funktionalitaet
193    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
194    *  ueberschreiben werden.
195    *
196    * @param req Http-Request, das vom Dispatcher durchgereicht wird
197    * @param res Http-Response, die vom Dispatcher durchgereicht wird
198    */
199   public void insert(HttpServletRequest req, HttpServletResponse res)
200       throws ServletModuleException, ServletModuleUserException {
201     try {
202       HashMap withValues = getIntersectingValues(req, mainModule.getStorageObject());
203       logger.debug("--trying to add...");
204       String id = mainModule.add(withValues);
205       logger.debug("--trying to deliver..." + id);
206       list(req, res);
207     }
208     catch (Exception e) {
209       throw new ServletModuleException(e.getMessage());
210     }
211   }
212
213   /**
214    *  delete(req,res) - generic delete method. Can be overridden in subclasses.
215    *
216    */
217
218   public void delete(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException {
219     try {
220       String idParam = req.getParameter("id");
221
222       if (idParam == null)
223         throw new ServletModuleException("Invalid call to delete: no id supplied");
224
225       String confirmParam = req.getParameter("confirm");
226       String cancelParam = req.getParameter("cancel");
227       if (confirmParam == null && cancelParam == null) {
228         SimpleHash mergeData = new SimpleHash();
229
230         mergeData.put("module", getOperationModuleName());
231         mergeData.put("infoString", getOperationModuleName() + ": " + idParam);
232         mergeData.put("id", idParam);
233         mergeData.put("where", req.getParameter("where"));
234         mergeData.put("order", req.getParameter("order"));
235         mergeData.put("offset", req.getParameter("offset"));
236         deliver(req, res, mergeData, templateConfirmString);
237       }
238       else {
239         if (confirmParam != null && !confirmParam.equals("")) {
240           //theLog.printInfo("delete confirmed!");
241           mainModule.deleteById(idParam);
242           list(req, res); // back to list
243         }
244         else {
245           if (req.getParameter("where") != null)
246             list(req, res);
247           else
248             edit(req, res);
249         }
250       }
251     }
252     catch (Exception e) {
253       throw new ServletModuleException(e.getMessage());
254     }
255   }
256
257   /**
258    *  edit(req,res) - generische Editmethode. Wennn die Funktionalitaet
259    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
260    *  ueberschreiben werden.
261    *
262    * @param req Http-Request, das vom Dispatcher durchgereicht wird
263    * @param res Http-Response, die vom Dispatcher durchgereicht wird
264    */
265   public void edit(HttpServletRequest req, HttpServletResponse res)
266       throws ServletModuleException {
267     try {
268       String idParam = req.getParameter("id");
269       deliver(req, res, mainModule.getById(idParam), templateObjektString);
270     }
271     catch (ModuleException e) {
272       throw new ServletModuleException(e.getMessage());
273     }
274   }
275
276   /**
277    *  update(req,res) - generische Updatemethode. Wennn die Funktionalitaet
278    *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
279    *  ueberschreiben werden.
280    *
281    * @param req Http-Request, das vom Dispatcher durchgereicht wird
282    * @param res Http-Response, die vom Dispatcher durchgereicht wird
283    */
284
285   public void update(HttpServletRequest req, HttpServletResponse res)
286       throws ServletModuleException {
287     try {
288       String idParam = req.getParameter("id");
289       HashMap withValues = getIntersectingValues(req, mainModule.getStorageObject());
290
291       String id = mainModule.set(withValues);
292       String whereParam = req.getParameter("where");
293       String orderParam = req.getParameter("order");
294
295       if ((whereParam != null && !whereParam.equals("")) || (orderParam != null && !orderParam.equals(""))) {
296         list(req, res);
297       }
298       else {
299         edit(req, res);
300       }
301     }
302     catch (Exception e) {
303       throw new ServletModuleException(e.getMessage());
304     }
305   }
306
307   /**
308    * deliver liefert das Template mit dem Filenamen templateFilename
309    * an den HttpServletResponse res aus, nachdem es mit den Daten aus
310    * TemplateModelRoot rtm gemischt wurde
311    *
312    * @param res Http-Response, die vom Dispatcher durchgereicht wird
313    * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
314    *   Daten, die ins Template gemerged werden sollen.
315    * @param tmpl Name des Templates
316    * @exception ServletModuleException
317    */
318   public void deliver(HttpServletRequest req, HttpServletResponse res,
319                       TemplateModelRoot rtm, TemplateModelRoot popups,
320                       String templateFilename)
321       throws ServletModuleException {
322     if (rtm == null) rtm = new SimpleHash();
323     try {
324       PrintWriter out = res.getWriter();
325       HTMLTemplateProcessor.process(res, templateFilename, rtm, popups, out,
326                                     getLocale(req), "bundles.admin");
327       // we default to admin bundles here, which is not exactly beautiful...
328       // but this whole producer stuff is going to be rewritten soon.
329       // ServletModuleOpenIndy overwrites deliver() to use open bundles
330       // (br1)
331       out.close();
332     }
333     catch (HTMLParseException e) {
334       throw new ServletModuleException(e.getMessage());
335     } catch (IOException e) {
336       throw new ServletModuleException(e.getMessage());
337     }
338   }
339
340
341   /**
342    * deliver liefert das Template mit dem Filenamen templateFilename
343    * an den HttpServletResponse res aus, nachdem es mit den Daten aus
344    * TemplateModelRoot rtm gemischt wurde
345    *
346    * @param res Http-Response, die vom Dispatcher durchgereicht wird
347    * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
348    *   Daten, die ins Template gemerged werden sollen.
349    * @param tmpl Name des Templates
350    * @exception ServletModuleException
351    */
352   public void deliver(HttpServletRequest req, HttpServletResponse res,
353                       TemplateModelRoot rtm, String templateFilename)
354       throws ServletModuleException {
355     deliver(req, res, rtm, null, templateFilename);
356   }
357
358   /**
359    * deliver liefert das Template mit dem Filenamen templateFilename
360    * an den HttpServletResponse res aus, nachdem es mit den Daten aus
361    * TemplateModelRoot rtm gemischt wurde
362    *
363    * @param res Http-Response, die vom Dispatcher durchgereicht wird
364    * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
365    *   Daten, die ins Template gemerged werden sollen.
366    * @param tmpl Name des Templates
367    * @exception ServletModuleException
368    */
369   public void deliver_compressed(HttpServletRequest req, HttpServletResponse res,
370                                  TemplateModelRoot rtm, String templateFilename)
371       throws ServletModuleException {
372     if (rtm == null) rtm = new SimpleHash();
373     try {
374       PrintWriter out = new LineFilterWriter(res.getWriter());
375       //PrintWriter out =  res.getWriter();
376       HTMLTemplateProcessor.process(res, templateFilename, rtm, out, getLocale(req));
377       out.close();
378     }
379     catch (HTMLParseException e) {
380       throw new ServletModuleException(e.getMessage());
381     }
382     catch (IOException e) {
383       throw new ServletModuleException(e.getMessage());
384     }
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 out ist der OutputStream, in den die gergten Daten geschickt werden sollen.
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   private void deliver(HttpServletResponse res, HttpServletRequest req, PrintWriter out,
399                        TemplateModelRoot rtm, String templateFilename)
400       throws HTMLParseException {
401     HTMLTemplateProcessor.process(res, templateFilename, rtm, out, getLocale(req));
402   }
403
404   /**
405    *  Wenn die abgeleitete Klasse diese Methode ueberschreibt und einen String mit einem
406    *  Methodennamen zurueckliefert, dann wird diese Methode bei fehlender Angabe des
407    *  doParameters ausgefuehrt.
408    *
409    * @return Name der Default-Action
410    */
411   public String defaultAction() {
412     return defaultAction;
413   }
414
415   /**
416    *  Hier kann vor der Datenaufbereitung schon mal ein response geschickt
417    *  werden (um das subjektive Antwortverhalten bei langsamen Verbindungen
418    *  zu verbessern).
419    */
420   public void predeliver(HttpServletRequest req, HttpServletResponse res) {
421     ;
422   }
423
424   /**
425    * Holt die Felder aus der Metadatenfelderliste des StorageObjects, die
426    * im HttpRequest vorkommen und liefert sie als HashMap zurueck
427    *
428    * @return HashMap mit den Werten
429    */
430   public HashMap getIntersectingValues(HttpServletRequest req, StorageObject theStorage)
431       throws ServletModuleException {
432     ArrayList theFieldList;
433     try {
434       theFieldList = theStorage.getFields();
435     }
436     catch (StorageObjectException e) {
437       throw new ServletModuleException("ServletModule.getIntersectingValues: " + e.getMessage());
438     }
439
440     HashMap withValues = new HashMap();
441     String aField, aValue;
442
443     for (int i = 0; i < theFieldList.size(); i++) {
444       aField = (String) theFieldList.get(i);
445       aValue = req.getParameter(aField);
446       if (aValue != null) withValues.put(aField, aValue);
447     }
448     return withValues;
449   }
450
451 }