package org.openswing.swing.server; import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import org.openswing.swing.internationalization.server.*; import org.openswing.swing.logger.server.*; import org.openswing.swing.message.receive.java.*; import org.openswing.swing.message.send.java.*; import org.openswing.swing.util.server.*; import java.lang.reflect.*; /** * <p>Title: OpenSwing Framework</p> * <p>Description: Server Controller (HTTP Servlet). * It receives all client requests and dispatch them to the correct action class. * This controller requires a user authentication before processing other requests: that request have method name: "login" * The web.xml file must have the following parameters: * </p> * <ul> * <li>"actionClasses" - i.e. the class name that collects all action classes; it must derive from "org.openswing.swing.server.ActionCollection" * <li>"connectionSource" - i.e. the class name that manages the database connections; it must implements "org.openswing.swing.server.ConnectionSource" class; possible values: "org.openswing.swing.server.DataSourceConnection", "org.openswing.swing.server.NoConnectionSource", "org.openswing.swing.server.PoolerConnectionSource" * <li>"resourceFactory" - i.e. the class name that defines the internationalization settings; it must implements "org.openswing.swing.internationalization.ServerResourcesFactory" class * <li>"sessionIdGenerator" - i.e. the class name that generated session identifiers; it must implements "org.openswing.swing.server.SessionIdGenerator" class; possible values: "org.openswing.swing.server.DefaultSessionIdGenerator" * <li>"logger" - i.e. the class name that manages the server log; it must implements "org.openswing.swing.logger.server.LoggerMethods" class; possible values: "org.openswing.swing.logger.server.ConsoleLogger", "org.openswing.swing.logger.server.Log4JLogger" * <li>"controllerCallbacks" - i.e. the class name that derives from ControllerCallbacks class; it must derive from "org.openswing.swing.server.ControllerCallbacks" class * </ul> * * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p> * * <p> This file is part of OpenSwing Framework. * This library is free software; you can redistribute it and/or * modify it under the terms of the (LGPL) Lesser General Public * License as published by the Free Software Foundation; * * GNU LESSER GENERAL PUBLIC LICENSE * Version 2.1, February 1999 * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * The author may be contacted at: * maurocarniel@tin.it</p> * * @author Mauro Carniel * @version 1.0 */ public class Controller extends HttpServlet { /** collections of pairs client request, action class */ private ActionsCollection actions = null; /** identifier of session identifiers stored in the servlet context */ public static final String SESSION_IDS = "SESSION_IDS"; /** identifier of user sessions info stored in the servlet context */ public static final String USER_SESSIONS = "USER_SESSIONS"; /** identifier of internationalization settings (Resources object) stored in the servlet context */ public static final String RESOURCES_FACTORY = "RESOURCES_FACTORY"; /** session identifiers generator */ public static final String SESSION_ID_GENERATOR = "SESSION_ID_GENERATOR"; /** action classes */ public static final String ACTION_CLASSES = "ACTION_CLASSES"; /** class that derives from ControllerCallbacks */ public static final String CONTROLLER_CALLBACKS = "CONTROLLER_CALLBACKS"; /** receiver class used in combination with "ClientUtils.getData" method to comunicate with a remote client via HTTP; default value: "DefaultObjectReceiver" */ private ObjectReceiver objectReceiver = new DefaultObjectReceiver(); private ControllerCallbacks controllerCallbacksObj = null; /** collection of request names always available, also before logging in */ private HashSet alwaysAvailableRequests = new HashSet(); /** * Initialize global variables. */ public void init() throws ServletException { // initialize the logger class (LoggerMethods object), if it is defined in web.xml by "logger" parameter... boolean logOk = false; try { String logger = super.getInitParameter("logger"); if (logger != null) { LoggerMethods loggerObject = (LoggerMethods)Class.forName(logger).newInstance(); Logger.init(loggerObject,Logger.LOG_ALL); } Logger.info("NONAME",this.getClass().getName(),"init","Initialized log manager"); logOk = true; } catch (Throwable ex) { this.getServletContext().log("Error on initializing logger class",ex); } // initialize action classes collection, if it is defined in web.xml by "actionClasses" parameter... try { String actionClasses = super.getInitParameter("actionClasses"); if (actionClasses != null) { actions = (ActionsCollection)Class.forName(actionClasses).newInstance(); this.getServletContext().setAttribute(ACTION_CLASSES,actions); } } catch (Throwable ex) { String msg = "Error on initializing action classes collection"; if (logOk) Logger.error("NONAME",this.getClass().getName(),"init",msg,ex); else this.getServletContext().log(msg,ex); } // initialize connection resource, if it is defined in web.xml by "connectionSource" parameter... try { String connectionSource = super.getInitParameter("connectionSource"); if (connectionSource != null) { ConnectionManager.initConnectionSource(this, connectionSource); } } catch (Throwable ex) { String msg = "Error on initializing connection source"; if (logOk) Logger.error("NONAME",this.getClass().getName(),"init",msg,ex); else this.getServletContext().log(msg,ex); } // initialize internationalization settings (Resources object), if it is defined in web.xml by "resourceFactory" parameter... try { String resourceFactory = super.getInitParameter("resourceFactory"); if (resourceFactory != null) { ServerResourcesFactory factory = (ServerResourcesFactory) Class.forName(resourceFactory).newInstance(); factory.init(getServletContext()); this.getServletContext().setAttribute(RESOURCES_FACTORY,factory); } } catch (Throwable ex) { String msg = "Error on initializing internationalization settings"; if (logOk) Logger.error("NONAME",this.getClass().getName(),"init",msg,ex); else this.getServletContext().log(msg,ex); } // initialize the session identifiers generator (SessionIdGenerator object), if it is defined in web.xml by "sessionIdGenerator" parameter... try { String sessionIdGenerator = super.getInitParameter("sessionIdGenerator"); if (sessionIdGenerator != null) { SessionIdGenerator generator = (SessionIdGenerator) Class.forName(sessionIdGenerator).newInstance(); this.getServletContext().setAttribute(SESSION_ID_GENERATOR,generator); } } catch (Throwable ex) { String msg = "Error on initializing session identifiers generator"; if (logOk) Logger.error("NONAME",this.getClass().getName(),"init",msg,ex); else this.getServletContext().log(msg,ex); } // initialize the controller callbacks class (object derived by ControllerCallbacks), if it is defined in web.xml by "controllerCallbacks" parameter... try { String controllerCallbacks = super.getInitParameter("controllerCallbacks"); if (controllerCallbacks != null) { controllerCallbacksObj = (ControllerCallbacks)Class.forName(controllerCallbacks).newInstance(); this.getServletContext().setAttribute(CONTROLLER_CALLBACKS,controllerCallbacksObj); } } catch (Throwable ex) { String msg = "Error on initializing controller callbacks class"; if (logOk) Logger.error("NONAME",this.getClass().getName(),"init",msg,ex); else this.getServletContext().log(msg,ex); } if (controllerCallbacksObj != null) controllerCallbacksObj.afterInit(super.getServletContext()); // read optional web.xml property, related to the list of request names that are always available, // even when the user has not yet logged in; list of request names must be separated by a comma try { String alwaysAvailableRequests = super.getInitParameter("alwaysAvailableRequests"); if (alwaysAvailableRequests != null) { String[] names = alwaysAvailableRequests.split(","); this.alwaysAvailableRequests.clear(); for(int i=0;i<names.length;i++) this.alwaysAvailableRequests.add(names[i].trim()); } } catch (Throwable ex) { String msg = "Error on initializing controller callbacks class"; if (logOk) Logger.error("NONAME",this.getClass().getName(),"init",msg,ex); else this.getServletContext().log(msg,ex); } // initialize objects receiver... try { String objectsReceiverClassName = super.getInitParameter("objectsReceiver"); if (objectsReceiverClassName != null) { objectReceiver = (ObjectReceiver)Class.forName(objectsReceiverClassName).newInstance(); } } catch (Throwable ex) { String msg = "Error on initializing objects receiver class"; if (logOk) Logger.error("NONAME",this.getClass().getName(),"init",msg,ex); else this.getServletContext().log(msg,ex); } if (logOk) Logger.info("NONAME",this.getClass().getName(),"init","Servlet Initialization completed."); } /** * Process the HTTP GET request: this method is used to receive browser HTTP requests (like document requests...) */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { String sessionId = request.getParameter("sessionId"); String docId = request.getParameter("docId"); // retrieve session identifiers... HashSet authenticatedIds = (HashSet)this.getServletContext().getAttribute(SESSION_IDS); if (authenticatedIds == null) { authenticatedIds = new HashSet(); this.getServletContext().setAttribute(SESSION_IDS, authenticatedIds); } if (sessionId == null) { // the URI does not contain the session identifier Logger.error( "NONAME", this.getClass().getName(), "doGet", "Session identifier not specified", null ); PrintWriter pw = response.getWriter(); pw.println("<html><head><title>ERROR</title></html>"); pw.println("<body>Access denied: you must specify a session identifier</body>"); pw.println("</html>"); pw.close(); } else if (authenticatedIds.contains(sessionId)) { if (docId == null) { Logger.error( "NONAME", this.getClass().getName(), "doGet", "Document identifier not specified", null ); PrintWriter pw = response.getWriter(); pw.println("<html><head><title>ERROR</title></html>"); pw.println( "<body>Document not available: you must specify a document identifier</body>"); pw.println("</html>"); pw.close(); } else { // document retrieving... Object doc = getServletContext().getAttribute(docId); new TimeoutDocIdThread(docId); if (doc == null) { Logger.error( "NONAME", this.getClass().getName(), "doGet", "Document identifier not valid", null ); PrintWriter pw = response.getWriter(); pw.println("<html><head><title>ERROR</title></html>"); pw.println( "<body>Document not available: you must specify a valid document identifier</body>"); pw.println("</html>"); pw.close(); } else { if (docId.endsWith(".xls")) { response.setContentType("application/vnd.ms-excel"); } else if (docId.endsWith(".xls") || docId.endsWith(".csv")) { response.setContentType("application/vnd.ms-excel"); } else if (docId.endsWith(".pdf")) { response.setContentType("application/pdf"); } else if (docId.endsWith(".rtf")) { response.setContentType("application/rtf"); } else if (docId.endsWith(".html")) { response.setContentType("text/html"); } else if (docId.endsWith(".xml")) { response.setContentType("text/xml"); } OutputStream out = response.getOutputStream(); out.write( (byte[]) doc); out.close(); } } } else { // the session identifier not valid... Logger.error( "NONAME", this.getClass().getName(), "doGet", "Session identifier not valid: '" + sessionId + "'", null ); PrintWriter pw = response.getWriter(); pw.println("<html><head><title>ERROR</title></html>"); pw.println( "<body>Access denied: you must specify a valid session identifier</body>"); pw.println("</html>"); pw.close(); } } catch (Throwable ex) { Logger.error( "NONAME", this.getClass().getName(), "doGet", "Error on receiving a GET request", ex ); try { PrintWriter pw = response.getWriter(); pw.println("<html><head><title>ERROR</title></html>"); pw.println("<body>An error occours on processing the request:<br>"+ex.toString()+"</body>"); pw.println("</html>"); pw.close(); } catch (IOException ex1) { } } } /** * Process the HTTP POST request: this method is coupled with ClientUtils.getData method. */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // retrieve user sessions info and the session identifiers... Response answer = null; HashSet authenticatedIds = (HashSet)this.getServletContext().getAttribute(SESSION_IDS); if (authenticatedIds==null) { authenticatedIds = new HashSet(); this.getServletContext().setAttribute(SESSION_IDS,authenticatedIds); } Hashtable userSessions = (Hashtable)this.getServletContext().getAttribute(USER_SESSIONS); if (userSessions==null) { userSessions = new Hashtable(); this.getServletContext().setAttribute(USER_SESSIONS,userSessions); } // retrieve internationalization settings (Resources object)... ServerResourcesFactory factory = (ServerResourcesFactory)this.getServletContext().getAttribute(RESOURCES_FACTORY); // read the client request... try { Command command = objectReceiver.getObjectFromRequest(request); if (!ConnectionManager.isConnectionSourceCreated() || command.getMethodName().equals("databaseAlreadyExixts") || alwaysAvailableRequests.contains(command.getMethodName())) { Object action = actions.get( command.getMethodName() ); if (action!=null && action instanceof Action) answer = ((Action)action).executeCommand( command.getInputParam(), null, request, response, request.getSession(true), this.getServletContext() ); else if (action!=null && action instanceof GenericAction) { answer = processGenericAction(request,response,factory,userSessions,action.getClass(),command); } } else if (command.getSessionId()==null && !command.getMethodName().equals("login")) { // received a client request BEFORE authentication... answer = new ErrorResponse("Cannot process the request: authentication needed!"); } else if (command.getSessionId()!=null && !authenticatedIds.contains(command.getSessionId())) { // received a client request BEFORE authentication... answer = new ErrorResponse("Cannot process the request: authentication needed!"); } else if (command.getSessionId()!=null && authenticatedIds.contains(command.getSessionId()) && command.getMethodName().equals("logout")) { // received a logout client request... authenticatedIds.remove(command.getSessionId()); userSessions.remove(command.getSessionId()); answer = new TextResponse(factory.getResources(((UserSessionParameters)userSessions.get(command.getSessionId())).getLanguageId()).getResource("User disconnected")); } else { // received a client request BEFORE authentication AND method is login // OR // received a client request AFTER authentication... Object action = actions.get( command.getMethodName() ); if (action!=null && action instanceof Action) answer = ((Action)action).executeCommand( command.getInputParam(), command.getSessionId()==null?null:(UserSessionParameters)userSessions.get(command.getSessionId()), request, response, request.getSession(true), this.getServletContext() ); else if (action!=null && action instanceof GenericAction) { answer = processGenericAction(request,response,factory,userSessions,action.getClass(),command); } else { String msg = "Client request not supported"; if (command.getSessionId()!=null) { // translate the error message... msg = factory.getResources(((UserSessionParameters)userSessions.get(command.getSessionId())).getLanguageId()).getResource(msg); } answer = new ErrorResponse( msg + ": '" + command.getMethodName() + "'" ); } } } catch (Throwable ex) { this.getServletContext().log("Error on processing client request",ex); try { objectReceiver.setObjectToResponse(response, new ErrorResponse(ex.getMessage())); } catch (Exception exx1) { this.getServletContext().log("Error on processing client request",exx1); response.getOutputStream().close(); } return; } try { objectReceiver.setObjectToResponse(response, answer); } catch (Exception ex1) { this.getServletContext().log("Error on processing client request",ex1); response.getOutputStream().close(); return; } } private Response processGenericAction(HttpServletRequest request, HttpServletResponse response,ServerResourcesFactory factory,Hashtable userSessions,Class genericAction,Command command) throws Throwable { GenericAction action = (GenericAction)genericAction.newInstance(); action.context = this.getServletContext(); action.request = request; action.response = response; action.userSession = request.getSession(true); action.userSessionPars = command.getSessionId()==null?null:(UserSessionParameters)userSessions.get(command.getSessionId()); Object[] args = null; if (command.getInputParam() instanceof Object[]) { args = (Object[])command.getInputParam(); } else args = new Object[]{command.getInputParam()}; Class[] argsType = new Class[args.length]; boolean nullValue = false; for(int i=0;i<args.length;i++) { if (args[i]==null) { nullValue = true; break; } else argsType[i] = args[i].getClass(); } if (!nullValue) try { return (Response) genericAction.getMethod(command.getMethodName(),argsType).invoke(action, args); } catch (NoSuchMethodException ex) { } // NoSuchMethodException or nullValue... Method[] mm = genericAction.getMethods(); ArrayList aux = new ArrayList(); for(int i=0;i<mm.length;i++) if (mm[i].getName().equals(command.getMethodName()) && mm[i].getParameterTypes().length==args.length) aux.add(mm[i]); Method m = null; boolean methodFound = false; for(int i=0;i<aux.size();i++) { m = (Method) aux.get(i); methodFound = true; for(int j=0;j<args.length;j++) if (args[j]!=null && !m.getParameterTypes()[j].isAssignableFrom(args[j].getClass())) { methodFound = false; break; } if (methodFound) { return (Response)m.invoke(action,args); } } String msg = "Client request not supported"; if (command.getSessionId()!=null) { // translate the error message... msg = factory.getResources(((UserSessionParameters)userSessions.get(command.getSessionId())).getLanguageId()).getResource(msg); } return new ErrorResponse( msg + ": '" + command.getMethodName() + "'" ); } public void destroy() { if (controllerCallbacksObj != null) controllerCallbacksObj.destroy(super.getServletContext()); super.destroy(); } /** * <p>Title: OpenSwing Framework</p> * <p>Description: Inner class< used to remove the document identifier from the context: * this is done after 5 minutes because some browser will call twice the same document request.</p> */ class TimeoutDocIdThread extends Thread { /** document identifier to remove */ private String docId; public TimeoutDocIdThread(String docId) { this.docId = docId; start(); } public void run() { try { sleep(300000); } catch (InterruptedException ex) { } try { getServletContext().removeAttribute(docId); } catch (Exception ex1) { } } } }