- all servletmodules and all modules now use log4j for logging
[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 session binded language
89      */
90     public String getLanguage(HttpServletRequest req) {
91         HttpSession session = req.getSession(false);
92         String language = (String) session.getAttribute("Language");
93         if (language == null) {
94             language = MirConfig.getProp("StandardLanguage");
95         }
96         return language;
97     }
98
99     /**
100      * get the locale either from the session or the accept-language header ot the request
101      * this supersedes getLanguage for the new i18n
102      */
103     public Locale getLocale(HttpServletRequest req) {
104         Locale loc = null;
105         HttpSession session = req.getSession(false);
106         if (session != null) {
107             // session can be null in case of logout
108             loc = (Locale) session.getAttribute("Locale");
109         }
110         // if there is nothing in the session get it fron the accept-language
111         if (loc == null) {
112             loc = req.getLocale();
113         }
114         return loc;
115     }
116
117     // ACHTUNG DEPRECATED::::
118     public void process(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException {
119     }
120
121     public void redirect(HttpServletResponse aResponse, String aQuery) throws ServletModuleException {
122       try {
123         aResponse.sendRedirect(MirConfig.getProp("RootUri") + "/Mir?"+aQuery);
124       }
125       catch (Throwable t) {
126         throw new ServletModuleException(t.getMessage());
127       }
128     }
129
130     /**
131      *  list(req,res) - generische Listmethode. Wennn die Funktionalitaet
132      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
133      *  ueberschreiben werden.
134      *
135      * @param req Http-Request, das vom Dispatcher durchgereicht wird
136      * @param res Http-Response, die vom Dispatcher durchgereicht wird
137      */
138     public void list(HttpServletRequest req, HttpServletResponse res)
139             throws ServletModuleException {
140         try {
141             EntityList theList;
142             String offsetParam = req.getParameter("offset");
143             int offset = 0;
144             PrintWriter out = res.getWriter();
145
146             // hier offsetcode bearbeiten
147             if (offsetParam != null && !offsetParam.equals("")) {
148                 offset = Integer.parseInt(offsetParam);
149             }
150             if (req.getParameter("next") != null) {
151                 offset = Integer.parseInt(req.getParameter("nextoffset"));
152             }
153             else {
154                 if (req.getParameter("prev") != null) {
155                     offset = Integer.parseInt(req.getParameter("prevoffset"));
156                 }
157             }
158             theList = mainModule.getByWhereClause(null, offset);
159             //theList = mainModule.getByWhereClause((String)null, offset);
160             if (theList == null || theList.getCount() == 0 || theList.getCount() > 1) {
161                 HTMLTemplateProcessor.process(res, templateListString, theList, out, getLocale(req));
162             }
163             else {
164                 deliver(req, res, theList.elementAt(0), templateObjektString);
165             }
166         }
167         catch (Exception e) {
168             throw new ServletModuleException(e.getMessage());
169         }
170     }
171
172     /**
173      *  add(req,res) - generische Addmethode. Wennn die Funktionalitaet
174      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
175      *  ueberschreiben werden.
176      * @param req Http-Request, das vom Dispatcher durchgereicht wird
177      * @param res Http-Response, die vom Dispatcher durchgereicht wird
178      */
179     public void add(HttpServletRequest req, HttpServletResponse res)
180             throws ServletModuleException {
181
182         try {
183             SimpleHash mergeData = new SimpleHash();
184             mergeData.put("new", "1");
185             deliver(req, res, mergeData, templateObjektString);
186         }
187         catch (Exception e) {
188             throw new ServletModuleException(e.getMessage());
189         }
190     }
191
192     /**
193      *  insert(req,res) - generische Insertmethode, folgt auf add.
194      *  Wennn die Funktionalitaet
195      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
196      *  ueberschreiben werden.
197      *
198      * @param req Http-Request, das vom Dispatcher durchgereicht wird
199      * @param res Http-Response, die vom Dispatcher durchgereicht wird
200      */
201     public void insert(HttpServletRequest req, HttpServletResponse res)
202             throws ServletModuleException, ServletModuleUserException {
203         try {
204             HashMap withValues = getIntersectingValues(req, mainModule.getStorageObject());
205             logger.debug("--trying to add...");
206             String id = mainModule.add(withValues);
207             logger.debug("--trying to deliver..." + id);
208             list(req, res);
209         }
210         catch (Exception e) {
211             throw new ServletModuleException(e.getMessage());
212         }
213     }
214
215     /**
216      *  delete(req,res) - generische Deletemethode. Wennn die Funktionalitaet
217      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
218      *  ueberschreiben werden.
219      *
220      * @param req Http-Request, das vom Dispatcher durchgereicht wird
221      * @param res Http-Response, die vom Dispatcher durchgereicht wird
222      */
223
224     public void delete(HttpServletRequest req, HttpServletResponse res)
225             throws ServletModuleException {
226         try {
227             String idParam = req.getParameter("id");
228             if (idParam == null) throw new ServletModuleException("Falscher Aufruf: (id) nicht angegeben");
229             // Hier code zum Loeschen
230             String confirmParam = req.getParameter("confirm");
231             String cancelParam = req.getParameter("cancel");
232             if (confirmParam == null && cancelParam == null) {
233                 // HTML Ausgabe zum Confirmen!
234                 SimpleHash mergeData = new SimpleHash();
235                 String moduleClassName = mainModule.getClass().getName();
236                 int i = moduleClassName.indexOf(".Module");
237                 String moduleName = moduleClassName.substring(i + 7);
238                 mergeData.put("module", moduleName);
239                 mergeData.put("infoString", moduleName + ": " + idParam);
240                 mergeData.put("id", idParam);
241                 mergeData.put("where", req.getParameter("where"));
242                 mergeData.put("order", req.getParameter("order"));
243                 mergeData.put("offset", req.getParameter("offset"));
244                 deliver(req, res, mergeData, templateConfirmString);
245             }
246             else {
247                 if (confirmParam != null && !confirmParam.equals("")) {
248                     //theLog.printInfo("delete confirmed!");
249                     mainModule.deleteById(idParam);
250                     list(req, res); // back to list
251                 }
252                 else {
253                     if (req.getParameter("where") != null)
254                         list(req, res);
255                     else
256                         edit(req, res);
257                 }
258             }
259         }
260         catch (Exception e) {
261             throw new ServletModuleException(e.getMessage());
262         }
263     }
264
265     /**
266      *  edit(req,res) - generische Editmethode. Wennn die Funktionalitaet
267      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
268      *  ueberschreiben werden.
269      *
270      * @param req Http-Request, das vom Dispatcher durchgereicht wird
271      * @param res Http-Response, die vom Dispatcher durchgereicht wird
272      */
273     public void edit(HttpServletRequest req, HttpServletResponse res)
274             throws ServletModuleException {
275         try {
276             String idParam = req.getParameter("id");
277             deliver(req, res, mainModule.getById(idParam), templateObjektString);
278         }
279         catch (ModuleException e) {
280             throw new ServletModuleException(e.getMessage());
281         }
282     }
283
284     /**
285      *  update(req,res) - generische Updatemethode. Wennn die Funktionalitaet
286      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
287      *  ueberschreiben werden.
288      *
289      * @param req Http-Request, das vom Dispatcher durchgereicht wird
290      * @param res Http-Response, die vom Dispatcher durchgereicht wird
291      */
292
293     public void update(HttpServletRequest req, HttpServletResponse res)
294             throws ServletModuleException {
295         try {
296             String idParam = req.getParameter("id");
297             HashMap withValues = getIntersectingValues(req, mainModule.getStorageObject());
298             String id = mainModule.set(withValues);
299             //theLog.printInfo("Showing Entity with id: " + id);
300             //edit(req,res);
301             String whereParam = req.getParameter("where");
302             String orderParam = req.getParameter("order");
303             if ((whereParam != null && !whereParam.equals("")) || (orderParam != null && !orderParam.equals(""))) {
304                 //theLog.printDebugInfo("update to list");
305                 list(req, res);
306             }
307             else {
308                 edit(req, res);
309             }
310             //list(req,res);
311         }
312         catch (Exception e) {
313             throw new ServletModuleException(e.getMessage());
314         }
315     }
316
317     /**
318      * deliver liefert das Template mit dem Filenamen templateFilename
319      * an den HttpServletResponse res aus, nachdem es mit den Daten aus
320      * TemplateModelRoot rtm gemischt wurde
321      *
322      * @param res Http-Response, die vom Dispatcher durchgereicht wird
323      * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
324      *   Daten, die ins Template gemerged werden sollen.
325      * @param tmpl Name des Templates
326      * @exception ServletModuleException
327      */
328         public void deliver(HttpServletRequest req, HttpServletResponse res,
329                             TemplateModelRoot rtm, TemplateModelRoot popups,
330                             String templateFilename)
331         throws ServletModuleException {
332                 if (rtm == null) rtm = new SimpleHash();
333                 try {
334                         PrintWriter out = res.getWriter();
335                         HTMLTemplateProcessor.process(res, templateFilename, rtm, popups, out,
336                                                                                                                                                 getLocale(req), "bundles.admin");
337         // we default to admin bundles here, which is not exactly beautiful...
338         // but this whole producer stuff is going to be rewritten soon.
339         // ServletModuleOpenIndy overwrites deliver() to use open bundles
340         // (br1)
341                         out.close();
342                 }
343                 catch (HTMLParseException e) {
344                         throw new ServletModuleException(e.getMessage());
345                 } catch (IOException e) {
346                         throw new ServletModuleException(e.getMessage());
347                 }
348         }
349
350
351         /**
352          * deliver liefert das Template mit dem Filenamen templateFilename
353          * an den HttpServletResponse res aus, nachdem es mit den Daten aus
354          * TemplateModelRoot rtm gemischt wurde
355          *
356          * @param res Http-Response, die vom Dispatcher durchgereicht wird
357          * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
358          *   Daten, die ins Template gemerged werden sollen.
359          * @param tmpl Name des Templates
360          * @exception ServletModuleException
361          */
362         public void deliver(HttpServletRequest req, HttpServletResponse res,
363                             TemplateModelRoot rtm, String templateFilename)
364         throws ServletModuleException {
365                 deliver(req, res, rtm, null, templateFilename);
366         }
367
368     /**
369      * deliver liefert das Template mit dem Filenamen templateFilename
370      * an den HttpServletResponse res aus, nachdem es mit den Daten aus
371      * TemplateModelRoot rtm gemischt wurde
372      *
373      * @param res Http-Response, die vom Dispatcher durchgereicht wird
374      * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
375      *   Daten, die ins Template gemerged werden sollen.
376      * @param tmpl Name des Templates
377      * @exception ServletModuleException
378      */
379     public void deliver_compressed(HttpServletRequest req, HttpServletResponse res,
380                                    TemplateModelRoot rtm, String templateFilename)
381             throws ServletModuleException {
382         if (rtm == null) rtm = new SimpleHash();
383         try {
384             PrintWriter out = new LineFilterWriter(res.getWriter());
385             //PrintWriter out =  res.getWriter();
386             HTMLTemplateProcessor.process(res, templateFilename, rtm, out, getLocale(req));
387             out.close();
388         }
389         catch (HTMLParseException e) {
390             throw new ServletModuleException(e.getMessage());
391         }
392         catch (IOException e) {
393             throw new ServletModuleException(e.getMessage());
394         }
395     }
396
397     /**
398      * deliver liefert das Template mit dem Filenamen templateFilename
399      * an den HttpServletResponse res aus, nachdem es mit den Daten aus
400      * TemplateModelRoot rtm gemischt wurde
401      *
402      * @param out ist der OutputStream, in den die gergten Daten geschickt werden sollen.
403      * @param rtm beinahalten das freemarker.template.TempalteModelRoot mit den
404      *   Daten, die ins Template gemerged werden sollen.
405      * @param tmpl Name des Templates
406      * @exception ServletModuleException
407      */
408     private void deliver(HttpServletResponse res, HttpServletRequest req, PrintWriter out,
409                          TemplateModelRoot rtm, String templateFilename)
410             throws HTMLParseException {
411         HTMLTemplateProcessor.process(res, templateFilename, rtm, out, getLocale(req));
412     }
413
414     /**
415      *  Wenn die abgeleitete Klasse diese Methode ueberschreibt und einen String mit einem
416      *  Methodennamen zurueckliefert, dann wird diese Methode bei fehlender Angabe des
417      *  doParameters ausgefuehrt.
418      *
419      * @return Name der Default-Action
420      */
421     public String defaultAction() {
422         return defaultAction;
423     }
424
425     /**
426      *  Hier kann vor der Datenaufbereitung schon mal ein response geschickt
427      *  werden (um das subjektive Antwortverhalten bei langsamen Verbindungen
428      *  zu verbessern).
429      */
430     public void predeliver(HttpServletRequest req, HttpServletResponse res) {
431         ;
432     }
433
434     /**
435      * Holt die Felder aus der Metadatenfelderliste des StorageObjects, die
436      * im HttpRequest vorkommen und liefert sie als HashMap zurueck
437      *
438      * @return HashMap mit den Werten
439      */
440     public HashMap getIntersectingValues(HttpServletRequest req, StorageObject theStorage)
441             throws ServletModuleException {
442         ArrayList theFieldList;
443         try {
444             theFieldList = theStorage.getFields();
445         }
446         catch (StorageObjectException e) {
447             throw new ServletModuleException("ServletModule.getIntersectingValues: " + e.getMessage());
448         }
449
450         HashMap withValues = new HashMap();
451         String aField, aValue;
452
453         for (int i = 0; i < theFieldList.size(); i++) {
454             aField = (String) theFieldList.get(i);
455             aValue = req.getParameter(aField);
456             if (aValue != null) withValues.put(aField, aValue);
457         }
458         return withValues;
459     }
460
461 }