//$Header: /cvsroot-fuse/mec-as2/39/mendelson/util/clientserver/ClientServerSessionHandler.java,v 1.1 2012/04/18 14:10:41 heller Exp $ package de.mendelson.util.clientserver; import de.mendelson.util.clientserver.messages.ClientServerMessage; import de.mendelson.util.clientserver.messages.LoginRequest; import de.mendelson.util.clientserver.messages.LoginState; import de.mendelson.util.clientserver.messages.QuitRequest; import de.mendelson.util.clientserver.messages.ServerInfo; import de.mendelson.util.clientserver.messages.ServerLogMessage; import de.mendelson.util.clientserver.user.PermissionDescription; import de.mendelson.util.clientserver.user.User; import de.mendelson.util.clientserver.user.UserAccess; import de.mendelson.util.log.LogFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; /* * Copyright (C) mendelson-e-commerce GmbH Berlin Germany * * This software is subject to the license agreement set forth in the license. * Please read and agree to all terms before using this software. * Other product and brand names are trademarks of their respective owners. */ /** * Session handler for the server implemetation * @author S.Heller * @version $Revision: 1.1 $ */ public class ClientServerSessionHandler extends IoHandlerAdapter { private static final String SESSION_ATTRIB_USER = "user"; /**User readable description of user permissions*/ private PermissionDescription permissionDescription = null; private Logger logger = Logger.getAnonymousLogger(); /**Synchronized structure to perform user defined processing on the server depending on the *incoming message object type */ private final List<ClientServerProcessing> processingList = Collections.synchronizedList(new ArrayList<ClientServerProcessing>()); /**Stores the product name, this is displayed on login requests */ private String productName = ""; /**Stores all sessions*/ private final List<IoSession> sessions = Collections.synchronizedList(new ArrayList<IoSession>()); private PasswordValidationHandler loginHandler; private Logger serverSessionLogger = Logger.getAnonymousLogger(); public ClientServerSessionHandler(Logger logger, String[] validClientIds) { if (logger != null) { this.logger = logger; } this.loginHandler = new PasswordValidationHandler(validClientIds); this.serverSessionLogger.setUseParentHandlers(false); // Create a file handler that uses 3 logfiles, each with a limit of 1Mbyte String serverSessionLogPattern = "client_server_session%g.log"; int limit = 1000000; // 1 Mb int numLogFiles = 3; try { FileHandler fileHandler = new FileHandler(serverSessionLogPattern, limit, numLogFiles); this.serverSessionLogger.addHandler(fileHandler); fileHandler.setFormatter(new LogFormatter()); } catch (Exception e) { logger.warning("Unable to initialize the server session handler: " + e.getMessage()); } } /**Logs something to the clients log - but only if the level is higher than the *defined loglevelThreshold */ public void log(Level logLevel, String message) { this.logger.log(logLevel, message); } private void logSession(IoSession session, Level logLevel, String message) { StringBuilder builder = new StringBuilder(); builder.append("Session "); builder.append(session.getId()).append(" "); if (session.getRemoteAddress() != null) { builder.append("[").append(session.getRemoteAddress()).append("] "); } builder.append(message); this.serverSessionLogger.log(logLevel, builder.toString()); } @Override /**The session has been opened: send a server info object*/ public void sessionOpened(IoSession session) { this.logSession(session, Level.INFO, "Incoming connection"); //send information about what this server is ServerInfo info = new ServerInfo(); info.setProductname(this.productName); session.write(info); //request a login LoginState state = new LoginState(); state.setState(LoginState.STATE_AUTHENTICATION_REQUIRED); session.write(state); } public void setPermissionDescription(PermissionDescription permissionDescription) { this.permissionDescription = permissionDescription; } /**Incoming message on the server site*/ @Override public void messageReceived(IoSession session, Object message) { if (message instanceof QuitRequest) { session.close(false); return; } //it is a login request if (message instanceof LoginRequest) { LoginRequest request = (LoginRequest) message; UserAccess access = new UserAccess(this.logger); //validate passwd first, close session if it fails User definedUser = access.readUser(request.getUserName()); if (definedUser != null && this.permissionDescription != null) { definedUser.setPermissionDescription(this.permissionDescription); } User transmittedUser = new User(); transmittedUser.setName(request.getUserName()); transmittedUser.setPasswdCrypted(request.getCryptedPasswd()); int validationState = this.loginHandler.validate(definedUser, transmittedUser, request.getClientId()); if (validationState == PasswordValidationHandler.STATE_FAILURE) { this.logSession(session, Level.INFO, "Authentication failed for user " + request.getUserName()); LoginState state = new LoginState(); state.setUser(transmittedUser); state.setState(LoginState.STATE_AUTHENTICATION_FAILURE); session.write(state); return; } else if (validationState == PasswordValidationHandler.STATE_INCOMPATIBLE_CLIENT) { this.logSession(session, Level.INFO, "Authentication failed: incompatible client"); LoginState state = new LoginState(); state.setUser(transmittedUser); state.setState(LoginState.STATE_INCOMPATIBLE_CLIENT); session.write(state); boolean closeImmediately = false; session.close(closeImmediately); return; } else if (validationState == PasswordValidationHandler.STATE_PASSWORD_REQUIRED) { this.logSession(session, Level.INFO, "Authentication failed, no password send for user " + request.getUserName()); LoginState state = new LoginState(); state.setUser(transmittedUser); state.setState(LoginState.STATE_AUTHENTICATION_FAILURE_PASSWORD_REQUIRED); session.write(state); return; } session.setAttribute(SESSION_ATTRIB_USER, request.getUserName()); synchronized (this.sessions) { this.sessions.add(session); } this.logSession(session, Level.INFO, "Authentication successful, user " + definedUser.getName() + " logged in"); //success! LoginState state = new LoginState(); state.setUser(definedUser); state.setState(LoginState.STATE_AUTHENTICATION_SUCCESS); session.write(state); return; } boolean loggedIn = session.containsAttribute(SESSION_ATTRIB_USER); //user not logged in so far if (!loggedIn) { LoginState state = new LoginState(); User userObj = new User(); if (this.permissionDescription != null) { userObj.setPermissionDescription(this.permissionDescription); } state.setUser(userObj); //login before requesting server services state.setState(LoginState.STATE_AUTHENTICATION_FAILURE); session.write(state); session.close(false); return; } //here starts the user defined processing to extend the server functionality this.performUserDefinedProcessing(session, (ClientServerMessage) message); } /**User defined extensions for the server processing*/ private synchronized void performUserDefinedProcessing(IoSession session, ClientServerMessage message) { boolean processed = false; for (int i = 0; i < this.processingList.size(); i++) { processed |= this.processingList.get(i).process(session, message); } if (!processed) { this.log(Level.WARNING, "performUserDefinedProcessing: inbound message of class " + message.getClass().getName() + " has not been processed."); } } /**User defined actions for messages sent by any client. The user may extend the framework by *implementing a ServerProcessing interface */ public void addServerProcessing(ClientServerProcessing serverProcessing) { synchronized (this.processingList) { this.processingList.add(serverProcessing); } } /**Sends a message object to all connected clients*/ public void broadcast(Object data) { synchronized (this.sessions) { for (IoSession session : this.sessions) { if (session.isConnected()) { session.write(data); } } } } /**Sends a log message to all connected clients*/ public void broadcastLogMessage(Level level, String message, Object[] parameter) { ServerLogMessage serverMessage = new ServerLogMessage(); serverMessage.setLevel(level); serverMessage.setMessage(message); serverMessage.setParameter(parameter); this.broadcast(serverMessage); } /**Sends a log message to all connected clients*/ public void broadcastLogMessage(Level level, String message) { this.broadcastLogMessage(level, message, null); } @Override public void sessionClosed(IoSession session) throws Exception { String user = (String) session.getAttribute(SESSION_ATTRIB_USER); if (user != null) { this.logSession(session, Level.INFO, "Closed"); synchronized (this.sessions) { this.sessions.remove(session); } //this.log(Level.INFO, "Session closed for user " + user); } } @Override public void sessionIdle(IoSession session, IdleStatus status) { // disconnect an idle client session.close(false); } @Override public void exceptionCaught(IoSession session, Throwable cause) { this.logSession(session, Level.WARNING, "Exception caught: " + cause.getMessage()); cause.printStackTrace(); // Close connection when unexpected exception is caught. session.close(true); } public int getConnectedClients() { return (this.sessions.size()); } public void setProductName(String productName) { this.productName = productName; } }