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