fixes the problem Alster spotted ...
[mir.git] / source / Mir.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  any library licensed under the Apache Software License,
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23  * (or with modified versions of the above that use the same license as the above),
24  * and distribute linked combinations including the two.  You must obey the
25  * GNU General Public License in all respects for all of the code used other than
26  * the above mentioned libraries.  If you modify this file, you may extend this
27  * exception to your version of the file, but you are not obligated to do so.
28  * If you do not wish to do so, delete this exception statement from your version.
29  */
30
31 import mir.bundle.Bundle;
32 import mir.config.MirPropertiesConfiguration;
33 import mir.log.LoggerWrapper;
34 import mir.servlet.*;
35 import mir.util.StringRoutines;
36 import mir.util.ExceptionRoutines;
37 import multex.Failure;
38
39 import javax.servlet.ServletConfig;
40 import javax.servlet.ServletException;
41 import javax.servlet.UnavailableException;
42 import javax.servlet.http.*;
43 import java.io.IOException;
44 import java.io.PrintWriter;
45 import java.io.StringWriter;
46 import java.lang.reflect.Method;
47 import java.util.*;
48
49 import mircoders.module.ModuleUsers;
50 import mircoders.global.MirGlobal;
51 import mircoders.servlet.ServletHelper;
52 import mircoders.entity.EntityUsers;
53
54 public class Mir extends AbstractServlet {
55   private static ModuleUsers usersModule = null;
56   private final static Map servletModuleInstanceHash = new HashMap();
57   private static Locale fallbackLocale = null;
58
59   private static List loginLanguages = null;
60
61   private List getLoginLanguages() throws Failure {
62     synchronized (Mir.class) {
63       try {
64         if (loginLanguages == null) {
65           List languages =
66             StringRoutines.splitString(MirGlobal.config().getString("Mir.Login.Languages", "en"), ";");
67
68           loginLanguages = new ArrayList();
69
70           Iterator i = languages.iterator();
71
72           while (i.hasNext()) {
73             String code = (String) i.next();
74
75             Bundle bundle =
76                 MirGlobal.getBundleFactory().getBundle("etc/bundles/adminlocal", new String[] { code });
77             Bundle defaultBundle =
78                 MirGlobal.getBundleFactory().getBundle("bundles/admin", new String[] { code });
79
80             String name = bundle.getValue("languagename", Collections.EMPTY_LIST);
81
82             if (name == null) {
83               name = defaultBundle.getValue("languagename", Collections.EMPTY_LIST);
84             }
85
86             if (name == null) {
87               name = code;
88             }
89
90             Map record = new HashMap();
91             record.put("name", name);
92             record.put("code", code);
93             loginLanguages.add(record);
94           }
95         }
96
97         return loginLanguages;
98       }
99       catch (Throwable t) {
100         throw new Failure("Error while retrieving the available login languages", t);
101       }
102     }
103   }
104
105   public void init(ServletConfig config) throws ServletException {
106     super.init(config);
107
108     usersModule = new ModuleUsers();
109   }
110
111   protected String getDefaultLanguage(HttpServletRequest aRequest) {
112     String defaultlanguage =
113       MirGlobal.config().getString("Mir.Login.DefaultLanguage", "");
114
115     if (defaultlanguage.length() == 0) {
116       Locale locale = aRequest.getLocale();
117       defaultlanguage = locale.getLanguage();
118     }
119
120     return defaultlanguage;
121   }
122
123   protected synchronized Locale getFallbackLocale() throws ServletException {
124     try {
125       if (fallbackLocale == null) {
126         fallbackLocale = new Locale(MirPropertiesConfiguration.instance().getString("Mir.Admin.FallbackLanguage", "en"), "");
127       }
128     }
129     catch (Throwable t) {
130       throw new ServletException(t.getMessage());
131     }
132
133     return fallbackLocale;
134   }
135
136   public EntityUsers checkCredentials(HttpServletRequest aRequest) throws ServletException {
137     try {
138       EntityUsers user = ServletHelper.getUser(aRequest);
139
140       String username = aRequest.getParameter("login");
141       String password = aRequest.getParameter("password");
142
143       if (username != null && password != null) {
144         user = usersModule.getUserForLogin(username, password);
145
146         if (user!=null) {
147           ServletHelper.setUser(aRequest, user);
148           usersModule.recordLogin(user);
149           aRequest.getSession().setAttribute("sessiontracker", new SessionTracker(username, user.getId()));
150         }
151       }
152
153       return user;
154     }
155     catch (Throwable t) {
156       t.printStackTrace();
157
158       throw new ServletException(t.toString());
159     }
160   }
161
162   public void process(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletException, IOException, UnavailableException {
163     try {
164       long startTime = System.currentTimeMillis();
165       long sessionConnectTime = 0;
166
167       HttpSession session = aRequest.getSession(true);
168       setNoCaching(aResponse);
169       aResponse.setContentType("text/html; charset=" +
170                                configuration.
171                                getString("Mir.DefaultHTMLCharset", "UTF-8"));
172
173       EntityUsers userEntity = checkCredentials(aRequest);
174
175       if (userEntity == null) {
176         String queryString = aRequest.getQueryString();
177
178         if ( (queryString != null) && (queryString.length() != 0) && session.getAttribute("login.target") == null &&
179              (aRequest.getParameter("module")==null ||
180               (!aRequest.getParameter("module").equals("login") && !aRequest.getParameter("module").equals("logout")))) {
181           session.setAttribute("login.target", queryString);
182         }
183
184         _sendLoginPage(aResponse, aRequest);
185       }
186       else {
187         String moduleName = aRequest.getParameter("module");
188         checkLanguage(session, aRequest);
189
190         if ( ( (moduleName == null) || moduleName.equals(""))) {
191           moduleName="Admin";
192         }
193
194
195         if (moduleName.equals("login")) {
196           String target = (String) session.getAttribute("login.target");
197
198           if (target != null) {
199             ServletHelper.redirect(aResponse, target);
200           }
201           else {
202             ServletHelper.redirect(aResponse, "");
203           }
204         }
205         else if (moduleName.equals("logout")) {
206           logger.info(userEntity.getFieldValue("login") + " has logged out");
207           session.invalidate();
208           _sendLoginPage(aResponse, aRequest);
209           return;
210         }
211         else {
212           try {
213             ServletModule servletModule = getServletModuleForName(moduleName);
214             ServletModuleDispatch.dispatch(servletModule, aRequest, aResponse);
215
216             sessionConnectTime = System.currentTimeMillis() - startTime;
217             logger.info("EXECTIME (" + moduleName + "): " + sessionConnectTime + " ms");
218           }
219           catch (Throwable e) {
220             Throwable cause = ExceptionRoutines.traceCauseException(e);
221
222             if (cause instanceof ServletModuleUserExc)
223               handleUserError(aRequest, aResponse, (ServletModuleUserExc) cause);
224             else
225               handleError(aRequest, aResponse, cause);
226           }
227
228           if (aRequest.getParameter("killsession")!=null)
229             aRequest.getSession().invalidate();
230         }
231       }
232     }
233     catch (Throwable t) {
234       t.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
235
236       throw new ServletException(t.toString());
237     }
238   }
239
240   /**
241    * caching routine to get a module for a module name
242    *
243    * @param moduleName the module name
244    * @return the requested module
245    * @throws ServletModuleExc
246    */
247
248   private static ServletModule getServletModuleForName(String moduleName) throws ServletModuleExc {
249     // Instance in Map ?
250     if (!servletModuleInstanceHash.containsKey(moduleName)) {
251       // was not found in hash...
252       try {
253         Class theServletModuleClass = null;
254
255         try {
256           // first we try to get ServletModule from stern.che3.servlet
257           theServletModuleClass =
258             Class.forName("mircoders.servlet.ServletModule" + moduleName);
259         }
260         catch (ClassNotFoundException e) {
261           // on failure, we try to get it from lib-layer
262           theServletModuleClass =
263             Class.forName("mir.servlet.ServletModule" + moduleName);
264         }
265
266         Method m = theServletModuleClass.getMethod("getInstance", null);
267         ServletModule smod = (ServletModule) m.invoke(null, null);
268
269         // we put it into map for further reference
270         servletModuleInstanceHash.put(moduleName, smod);
271
272         return smod;
273       }
274       catch (Exception e) {
275         throw new ServletModuleExc("*** error resolving classname for " + moduleName + " -- " + e.getMessage());
276       }
277     }
278                 return (ServletModule) servletModuleInstanceHash.get(moduleName);
279   }
280
281   private void handleUserError(HttpServletRequest aRequest, HttpServletResponse aResponse, ServletModuleUserExc anException) {
282     try {
283       logger.info("user error: " + anException.getMessage());
284
285       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] {getLocale(aRequest), getFallbackLocale()});
286
287       Bundle bundle =
288           MirGlobal.getBundleFactory().getBundle("etc/bundles/adminlocal", new
289               String[] { getLocale(aRequest).getLanguage() });
290       Bundle defaultBundle =
291           MirGlobal.getBundleFactory().getBundle("bundles/admin", new
292               String[] { getLocale(aRequest).getLanguage() });
293       String message =
294         bundle.getValue(anException.getMessage(), Arrays.asList(anException.getParameters()));
295
296       if (message==null) {
297         message =
298           defaultBundle.getValue(anException.getMessage(), Arrays.asList(anException.getParameters()));
299       }
300
301       responseData.put("errorstring", message);
302       responseData.put("date", new GregorianCalendar().getTime());
303
304       ServletHelper.generateResponse(aResponse.getWriter(), responseData, MirPropertiesConfiguration.instance().getString("Mir.UserErrorTemplate"));
305     }
306     catch (Throwable e) {
307       logger.error("Error handling user error" + e.toString());
308     }
309   }
310
311   private void handleError(HttpServletRequest aRequest, HttpServletResponse aResponse, Throwable anException) {
312     try {
313       logger.error("error: " + anException);
314
315       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] {getLocale(aRequest), getFallbackLocale()});
316
317       responseData.put("errorstring", anException.toString());
318       StringWriter writer = new StringWriter();
319       anException.printStackTrace(new PrintWriter(writer));
320       responseData.put("stacktrace", writer.toString());
321       responseData.put("date", new GregorianCalendar().getTime());
322
323       ServletHelper.generateResponse(aResponse.getWriter(), responseData, MirPropertiesConfiguration.instance().getString("Mir.ErrorTemplate"));
324     }
325     catch (Throwable e) {
326       logger.error("Error handling error: " + e.toString());
327
328       try {
329         Throwable rootException = ExceptionRoutines.traceCauseException(anException);
330
331         PrintWriter writer = aResponse.getWriter();
332         writer.println("<html><head><title>FATAL Error</title><body>");
333         writer.println("<h1>" + rootException.toString()+"</h1>");
334         writer.println("<code>");
335         rootException.printStackTrace(writer);
336         writer.println("</code>");
337         writer.println("</body></html>");
338         writer.close();
339       }
340       catch (Throwable t) {
341
342       }
343     }
344   }
345
346   // Redirect-methods
347   private void _sendLoginPage(HttpServletResponse aResponse, HttpServletRequest aRequest) {
348     String loginTemplate = configuration.getString("Mir.LoginTemplate");
349
350     try {
351       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] {getLocale(aRequest), getFallbackLocale()});
352
353       responseData.put("defaultlanguage", getDefaultLanguage(aRequest));
354       responseData.put("languages", getLoginLanguages());
355
356       ServletHelper.generateResponse(aResponse.getWriter(), responseData, loginTemplate);
357     }
358     catch (Throwable e) {
359       handleError(aRequest, aResponse, e);
360     }
361   }
362
363   public String getServletInfo() {
364     return "Mir " + configuration.getString("Mir.Version");
365   }
366
367   private class SessionTracker implements HttpSessionBindingListener {
368     private String name;
369     private String id;
370
371     public SessionTracker(String aUserName, String anId) {
372       name = aUserName;
373       id = anId;
374     }
375
376     public void valueBound(HttpSessionBindingEvent anEvent) {
377       MirGlobal.registerLogin(name, id);
378     }
379
380     public void valueUnbound(HttpSessionBindingEvent anEvent) {
381       MirGlobal.registerLogout(name, id);
382     }
383   }
384 }