- producer links are moved to an "advanced" page, not intended for normal
[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.misc.*;
40 import mir.module.AbstractModule;
41 import mir.module.ModuleException;
42 import mir.storage.StorageObject;
43 import mir.storage.StorageObjectException;
44
45 import javax.servlet.http.HttpServletRequest;
46 import javax.servlet.http.HttpServletResponse;
47 import javax.servlet.http.HttpSession;
48 import java.io.IOException;
49 import java.io.PrintWriter;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.Locale;
53
54
55 /**
56  * Abstract class ServletModule provides the base functionality for servlets.
57  * Deriving a class from ServletModule enables class to insert/edit/update/delete
58  * and list Entity from a Database via mainModule.
59  *
60  *
61  *  Abstrakte Klasse ServletModule stellt die Basisfunktionalitaet der
62  *  abgeleiteten ServletModule zur Verfügung.
63  *
64  * @version 28.6.1999
65  * @author RK
66  */
67
68 public abstract class ServletModule {
69
70     public String defaultAction;
71     protected Logfile theLog;
72     protected AbstractModule mainModule;
73     protected String templateListString;
74     protected String templateObjektString;
75     protected String templateConfirmString;
76
77     /**
78      * Singelton - Methode muss in den abgeleiteten Klassen ueberschrieben werden.
79      * @return ServletModule
80      */
81     public static ServletModule getInstance() {
82         return null;
83     }
84
85     /**
86      * get the session binded language
87      */
88     public String getLanguage(HttpServletRequest req) {
89         HttpSession session = req.getSession(false);
90         String language = (String) session.getAttribute("Language");
91         if (language == null) {
92             language = MirConfig.getProp("StandardLanguage");
93         }
94         return language;
95     }
96
97     /**
98      * get the locale either from the session or the accept-language header ot the request
99      * this supersedes getLanguage for the new i18n
100      */
101     public Locale getLocale(HttpServletRequest req) {
102         Locale loc = null;
103         HttpSession session = req.getSession(false);
104         if (session != null) {
105             // session can be null in case of logout
106             loc = (Locale) session.getAttribute("Locale");
107         }
108         // if there is nothing in the session get it fron the accept-language
109         if (loc == null) {
110             loc = req.getLocale();
111         }
112         return loc;
113     }
114
115     // ACHTUNG DEPRECATED::::
116     public void process(HttpServletRequest req, HttpServletResponse res) throws ServletModuleException {
117     }
118
119
120     /**
121      *  list(req,res) - generische Listmethode. Wennn die Funktionalitaet
122      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
123      *  ueberschreiben werden.
124      *
125      * @param req Http-Request, das vom Dispatcher durchgereicht wird
126      * @param res Http-Response, die vom Dispatcher durchgereicht wird
127      */
128     public void list(HttpServletRequest req, HttpServletResponse res)
129             throws ServletModuleException {
130         try {
131             EntityList theList;
132             String offsetParam = req.getParameter("offset");
133             int offset = 0;
134             PrintWriter out = res.getWriter();
135
136             // hier offsetcode bearbeiten
137             if (offsetParam != null && !offsetParam.equals("")) {
138                 offset = Integer.parseInt(offsetParam);
139             }
140             if (req.getParameter("next") != null) {
141                 offset = Integer.parseInt(req.getParameter("nextoffset"));
142             }
143             else {
144                 if (req.getParameter("prev") != null) {
145                     offset = Integer.parseInt(req.getParameter("prevoffset"));
146                 }
147             }
148             theList = mainModule.getByWhereClause(null, offset);
149             //theList = mainModule.getByWhereClause((String)null, offset);
150             if (theList == null || theList.getCount() == 0 || theList.getCount() > 1) {
151                 HTMLTemplateProcessor.process(res, templateListString, theList, out, getLocale(req));
152             }
153             else {
154                 deliver(req, res, theList.elementAt(0), templateObjektString);
155             }
156         }
157         catch (Exception e) {
158             throw new ServletModuleException(e.getMessage());
159         }
160     }
161
162     /**
163      *  add(req,res) - generische Addmethode. Wennn die Funktionalitaet
164      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
165      *  ueberschreiben werden.
166      * @param req Http-Request, das vom Dispatcher durchgereicht wird
167      * @param res Http-Response, die vom Dispatcher durchgereicht wird
168      */
169     public void add(HttpServletRequest req, HttpServletResponse res)
170             throws ServletModuleException {
171
172         try {
173             SimpleHash mergeData = new SimpleHash();
174             mergeData.put("new", "1");
175             deliver(req, res, mergeData, templateObjektString);
176         }
177         catch (Exception e) {
178             throw new ServletModuleException(e.getMessage());
179         }
180     }
181
182     /**
183      *  insert(req,res) - generische Insertmethode, folgt auf add.
184      *  Wennn die Funktionalitaet
185      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
186      *  ueberschreiben werden.
187      *
188      * @param req Http-Request, das vom Dispatcher durchgereicht wird
189      * @param res Http-Response, die vom Dispatcher durchgereicht wird
190      */
191     public void insert(HttpServletRequest req, HttpServletResponse res)
192             throws ServletModuleException, ServletModuleUserException {
193         try {
194             HashMap withValues = getIntersectingValues(req, mainModule.getStorageObject());
195             theLog.printDebugInfo("--trying to add...");
196             String id = mainModule.add(withValues);
197             theLog.printDebugInfo("--trying to deliver..." + id);
198             list(req, res);
199         }
200         catch (Exception e) {
201             throw new ServletModuleException(e.getMessage());
202         }
203     }
204
205     /**
206      *  delete(req,res) - generische Deletemethode. Wennn die Funktionalitaet
207      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
208      *  ueberschreiben werden.
209      *
210      * @param req Http-Request, das vom Dispatcher durchgereicht wird
211      * @param res Http-Response, die vom Dispatcher durchgereicht wird
212      */
213
214     public void delete(HttpServletRequest req, HttpServletResponse res)
215             throws ServletModuleException {
216         try {
217             String idParam = req.getParameter("id");
218             if (idParam == null) throw new ServletModuleException("Falscher Aufruf: (id) nicht angegeben");
219             // Hier code zum Loeschen
220             String confirmParam = req.getParameter("confirm");
221             String cancelParam = req.getParameter("cancel");
222             if (confirmParam == null && cancelParam == null) {
223                 // HTML Ausgabe zum Confirmen!
224                 SimpleHash mergeData = new SimpleHash();
225                 String moduleClassName = mainModule.getClass().getName();
226                 int i = moduleClassName.indexOf(".Module");
227                 String moduleName = moduleClassName.substring(i + 7);
228                 mergeData.put("module", moduleName);
229                 mergeData.put("infoString", moduleName + ": " + idParam);
230                 mergeData.put("id", idParam);
231                 mergeData.put("where", req.getParameter("where"));
232                 mergeData.put("order", req.getParameter("order"));
233                 mergeData.put("offset", req.getParameter("offset"));
234                 deliver(req, res, mergeData, templateConfirmString);
235             }
236             else {
237                 if (confirmParam != null && !confirmParam.equals("")) {
238                     //theLog.printInfo("delete confirmed!");
239                     mainModule.deleteById(idParam);
240                     list(req, res); // back to list
241                 }
242                 else {
243                     if (req.getParameter("where") != null)
244                         list(req, res);
245                     else
246                         edit(req, res);
247                 }
248             }
249         }
250         catch (Exception e) {
251             throw new ServletModuleException(e.getMessage());
252         }
253     }
254
255     /**
256      *  edit(req,res) - generische Editmethode. Wennn die Funktionalitaet
257      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
258      *  ueberschreiben werden.
259      *
260      * @param req Http-Request, das vom Dispatcher durchgereicht wird
261      * @param res Http-Response, die vom Dispatcher durchgereicht wird
262      */
263     public void edit(HttpServletRequest req, HttpServletResponse res)
264             throws ServletModuleException {
265         try {
266             String idParam = req.getParameter("id");
267             deliver(req, res, mainModule.getById(idParam), templateObjektString);
268         }
269         catch (ModuleException e) {
270             throw new ServletModuleException(e.getMessage());
271         }
272     }
273
274     /**
275      *  update(req,res) - generische Updatemethode. Wennn die Funktionalitaet
276      *  nicht reicht, muss sie in der abgeleiteten ServletModule-Klasse
277      *  ueberschreiben werden.
278      *
279      * @param req Http-Request, das vom Dispatcher durchgereicht wird
280      * @param res Http-Response, die vom Dispatcher durchgereicht wird
281      */
282
283     public void update(HttpServletRequest req, HttpServletResponse res)
284             throws ServletModuleException {
285         try {
286             String idParam = req.getParameter("id");
287             HashMap withValues = getIntersectingValues(req, mainModule.getStorageObject());
288             String id = mainModule.set(withValues);
289             //theLog.printInfo("Showing Entity with id: " + id);
290             //edit(req,res);
291             String whereParam = req.getParameter("where");
292             String orderParam = req.getParameter("order");
293             if ((whereParam != null && !whereParam.equals("")) || (orderParam != null && !orderParam.equals(""))) {
294                 //theLog.printDebugInfo("update to list");
295                 list(req, res);
296             }
297             else {
298                 edit(req, res);
299             }
300             //list(req,res);
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 }