alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * $Header: /cvsroot/mvnforum/mvnforum/src/com/mvnforum/auth/OnlineUserManager.java,v 1.26.2.1 2005/06/17 17:49:29 minhnn Exp $
 * $Author: minhnn $
 * $Revision: 1.26.2.1 $
 * $Date: 2005/06/17 17:49:29 $
 *
 * ====================================================================
 *
 * Copyright (C) 2002-2005 by MyVietnam.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * All copyright notices regarding mvnForum MUST remain intact
 * in the scripts and in the outputted HTML.
 * The "powered by" text/logo with a link back to
 * http://www.mvnForum.com and http://www.MyVietnam.net in the
 * footer of the pages MUST remain visible when the pages
 * are viewed on the internet or intranet.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Support can be obtained from support forums at:
 * http://www.mvnForum.com/mvnforum/index
 *
 * Correspondence and Marketing Questions can be sent to:
 * info@MyVietnam.net
 *
 * @author: Minh Nguyen  minhnn@MyVietnam.net
 * @author: Mai  Nguyen  mai.nh@MyVietnam.net
 */
package com.mvnforum.auth;

import java.sql.Timestamp;
import java.util.*;

import javax.servlet.http.*;

import com.mvnforum.*;
import com.mvnforum.db.DAOFactory;
import net.myvietnam.mvncore.exception.*;
import net.myvietnam.mvncore.security.Encoder;
import net.myvietnam.mvncore.security.FloodControl;
import net.myvietnam.mvncore.util.*;

public class OnlineUserManager {
    private static final int REMOVE_INTERVAL = 2000; //update every 2 second

    private static final String MVNFORUM_SESSION_USERNAME = "mvnforum.membername";
    private static final String MVNFORUM_SESSION_PASSWORD = "mvnforum.encodedpassword";

    private static final String MVNFORUM_COOKIE_USERNAME = "mvnforum.membername";
    private static final String MVNFORUM_COOKIE_PASSWORD = "mvnforum.encodedpassword";

    private static final String MVNFORUM_COOKIE_PATH     = "/";

    public static final String PASSWORD_OF_METHOD_REALM         = "Realm";
    public static final String PASSWORD_OF_METHOD_CUSTOMIZATION = "Remote";

    //static variable
    private static OnlineUserManager instance = new OnlineUserManager();

    //instance variable
    private Map userMap = new TreeMap();
    private long timeOfLastRemoveAction = 0;
    private transient Vector onlineUserListeners;

    private Authenticator authenticator = null;

    private OnlineUserManager() {
    }

    public static OnlineUserManager getInstance() {
        return instance;
    }

    public Authenticator getAuthenticator() {
        return authenticator;
    }

    public void setAuthenticator(Authenticator authenticator) {
        this.authenticator = authenticator;
    }

    /**
     * MemberUtil method to be called from Processor.
     * It assumes that to input parameters are
     * MemberName      for username
     * MemberMatkhau   for password
     */
    public void processLogin(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException, AssertionException, DatabaseException, BadInputException, FloodException {

        String memberName      = ParamUtil.getParameter(request, "MemberName", true);
        StringUtil.checkGoodName(memberName);// check for better security
        String memberPassword  = "";
        String memberPasswordMD5  = ParamUtil.getParameter(request, "md5pw", false);
        if (memberPasswordMD5.length() == 0 || (memberPasswordMD5.endsWith("==") == false)) {
            // md5 is not valid, try to use unencoded password method
            memberPassword  = ParamUtil.getParameterPassword(request, "MemberMatkhau", 3, 0);
        }

        String currentIP = request.getRemoteAddr();
        try {
            // Control the login action, we dont want user to try too many login attempt
            FloodControl.ensureNotReachMaximum(MVNForumGlobal.FLOOD_ID_LOGIN, currentIP);

            OnlineUser user = null;
            if (memberPassword.length() > 0) {
                // that is we cannot find the md5 password
                user = login(request, response, memberName, memberPassword, false);
            } else {
                // have the md5, go ahead
                user = login(request, response, memberName, memberPasswordMD5, true);
            }
            ((OnlineUserImpl)user).setAuthenticationType(OnlineUser.AUTHENTICATION_TYPE_HTML_FORM);
        } catch (AuthenticationException ex) {
            // only increase login count if unsucessful
            FloodControl.increaseCount(MVNForumGlobal.FLOOD_ID_LOGIN, currentIP);

            if (ex.getReason() == NotLoginException.WRONG_PASSWORD) {
                request.setAttribute("MemberName", memberName);// so user dont have to retype USER NAME
            }
            throw ex;
        } catch (FloodException fe) {
            Locale locale = I18nUtil.getLocaleInRequest(request);
            Integer maxWrongLogins = new Integer(FloodControl.getActionsPerHour(MVNForumGlobal.FLOOD_ID_LOGIN));
            //throw new FloodException("You have reached the maximum number of wrong login actions for this page. Please try this page later. This is to prevent forum from being flooded.");
            String localizedMessage = MVNForumResourceBundle.getString(locale, "mvncore.exception.FloodException.login_too_many_times", new Object[]{ maxWrongLogins });
            throw new FloodException(localizedMessage);
        }
    }

    /**
     * NOTE: This method MUST be the only way to authenticate a user
     * NOTE: the parameter response can be equals null
     */
    protected OnlineUser login(HttpServletRequest request, HttpServletResponse response,
                               String memberName, String memberPassword, boolean passwordEncoded)
        throws AuthenticationException, DatabaseException, AssertionException {

        try {
            StringUtil.checkGoodName(memberName);
        } catch (Exception ex) {
            throw new AuthenticationException(ex.getMessage(), NotLoginException.WRONG_NAME);
        }
        String encodedPassword;
        OnlineUser user;

        if (passwordEncoded) {
            encodedPassword = memberPassword;
            user = ManagerFactory.getOnlineUserFactory().getAuthenticatedUser(request, response, memberName, encodedPassword, true);
        } else {
            encodedPassword = ManagerFactory.getOnlineUserFactory().getEncodedPassword(memberName, memberPassword);
            //user = ManagerFactory.getOnlineUserFactory().getAuthenticatedUser(request, response, memberName, memberPassword, false);
            user = ManagerFactory.getOnlineUserFactory().getAuthenticatedUser(request, response, memberName, encodedPassword, true);
        }

        HttpSession session = request.getSession();
        String sessionID = session.getId();
        setOnlineUser(sessionID, user);

        // now save the login info in the session only if we support
        // encoded passwords
        if (null != encodedPassword) {
            session.setAttribute(MVNFORUM_SESSION_USERNAME, memberName);
            session.setAttribute(MVNFORUM_SESSION_PASSWORD, encodedPassword);
        }

        boolean fromLoginPage = ParamUtil.getParameterBoolean(request, "FromLoginPage");
        if ( fromLoginPage && (response != null) ) {
            manageAutoLogin(memberName, encodedPassword, request, response);
        }

        // Now call the postLogin method, in the default implementation, the default folder
        // is checked and created if not existed
        ManagerFactory.getOnlineUserFactory().postLogin(request, response, user);

        return user;
    }

    protected void manageAutoLogin(String memberName, String encodedPassword,
                                   HttpServletRequest request, HttpServletResponse response) {

        boolean autoLogin = ParamUtil.getParameterBoolean(request, "AutoLogin");
        if (autoLogin) {
            int autoLoginExpire = (60 * 60 * 24) * 1; // 1 day
            try {
                autoLoginExpire = ParamUtil.getParameterInt(request, "AutoLoginExpire");
            } catch (Exception ex) {
                // do nothing
            }
            Cookie nameCookie = new Cookie(MVNFORUM_COOKIE_USERNAME, Encoder.encodeURL(memberName));
            nameCookie.setMaxAge(autoLoginExpire);
            nameCookie.setPath(MVNFORUM_COOKIE_PATH);

            Cookie passwordCookie = new Cookie(MVNFORUM_COOKIE_PASSWORD, encodedPassword);
            passwordCookie.setMaxAge(autoLoginExpire);
            passwordCookie.setPath(MVNFORUM_COOKIE_PATH);

            response.addCookie(nameCookie);
            response.addCookie(passwordCookie);
        }
    }

    public void logout(HttpServletRequest request, HttpServletResponse response)
        throws DatabaseException, AssertionException, AuthenticationException {

        HttpSession session = request.getSession();
        String sessionID = session.getId();

        OnlineUser oldUser = getOnlineUser(request);
        String cssPath = oldUser.getCssPath();
        String logoPath = oldUser.getLogoPath();

        OnlineUser user = null;
        if (authenticator == null) {
            // temporary hack, if no authenticator has been set,
            // then we use the old method
            // @todo: more thought on this later
            user = ManagerFactory.getOnlineUserFactory().getAnonymousUser(request);
            user.setCssPath(cssPath);
            user.setLogoPath(logoPath);
        }
        setOnlineUser(sessionID, user);
        ManagerFactory.getOnlineUserFactory().logout(request, response);

        // now always clear the session information
        session.setAttribute(MVNFORUM_SESSION_USERNAME, null);
        session.setAttribute(MVNFORUM_SESSION_PASSWORD, null);
    }

    public void deleteCookie(HttpServletRequest request, HttpServletResponse response) {

        Cookie nameCookie = new Cookie(MVNFORUM_COOKIE_USERNAME, "");
        nameCookie.setPath(MVNFORUM_COOKIE_PATH);
        nameCookie.setMaxAge(0);// delete this cookie

        Cookie passwordCookie = new Cookie(MVNFORUM_COOKIE_PASSWORD, "");
        passwordCookie.setPath(MVNFORUM_COOKIE_PATH);
        passwordCookie.setMaxAge(0);// delete this cookie

        response.addCookie(nameCookie);
        response.addCookie(passwordCookie);
    }

    public OnlineUser getOnlineUser(HttpServletRequest request)
        throws AuthenticationException, AssertionException, DatabaseException {

        long currentTime = System.currentTimeMillis();
        if (currentTime - timeOfLastRemoveAction > REMOVE_INTERVAL) {//update every minute
            removeTimeoutUsers();
            // update MostOnline here
            int currentOnlineUserCount = userMap.size();
            fireDataChanged(new OnlineUserEvent(this, currentOnlineUserCount));
        }

        HttpSession session = request.getSession();
        String sessionID = session.getId();
        OnlineUser user = getOnlineUser(sessionID);
        if (user == null) {

            boolean enableLoginInfoInCookie = MVNForumConfig.getEnableLoginInfoInCookie();
            boolean enableLoginInfoInSession = MVNForumConfig.getEnableLoginInfoInSession();
            boolean enableLoginInfoInRealm = MVNForumConfig.getEnableLoginInfoInRealm();
            boolean enableLoginInfoInCustomization = MVNForumConfig.getEnableLoginInfoInCustomization();

            if ((user == null) && enableLoginInfoInSession) {
                String memberName = ParamUtil.getAttribute(session, MVNFORUM_SESSION_USERNAME);
                String encodedPassword = ParamUtil.getAttribute(session, MVNFORUM_SESSION_PASSWORD);
                if ( (memberName.length() > 0) && (encodedPassword.length() > 0)) {
                    try {
                        user = login(request, null, memberName, encodedPassword, true);
                        ((OnlineUserImpl) user).setAuthenticationType(OnlineUser.AUTHENTICATION_TYPE_SESSION);
                    } catch (AuthenticationException e) {
                        // do nothing, some time the login info in the session
                        // is not correct, we dont consider this case as error
                    }
                }
            }
            if ((user == null) && enableLoginInfoInCookie) {
                String memberName = "";
                String encodedPassword = "";
                Cookie[] cookies = request.getCookies();
                if (cookies != null) {
                    for (int i = 0; i < cookies.length; i++) {
                        Cookie cookie = cookies[i];
                        String cookieName = cookie.getName();
                        if (cookieName.equals(MVNFORUM_COOKIE_USERNAME)) {
                            memberName = Encoder.decodeURL(cookie.getValue()) ;
                        } else if (cookieName.equals(MVNFORUM_COOKIE_PASSWORD)) {
                            encodedPassword = cookie.getValue();
                        }
                    }
                }
                if ( (memberName.length() > 0) && (encodedPassword.length() > 0) &&
                     (encodedPassword.equalsIgnoreCase(PASSWORD_OF_METHOD_REALM) == false) &&
                     (encodedPassword.equalsIgnoreCase(PASSWORD_OF_METHOD_CUSTOMIZATION) == false) ) {
                    try {
                        user = login(request, null, memberName, encodedPassword, true);
                        ((OnlineUserImpl)user).setAuthenticationType(OnlineUser.AUTHENTICATION_TYPE_COOKIE);
                    } catch (AuthenticationException e) {
                        // do nothing, some time the login info in the cookie
                        // is not correct, we dont consider this case as error
                    }
                }
            }
            if ((user == null) && enableLoginInfoInRealm && (null != request.getRemoteUser())) {
                String memberName = StringUtil.getEmptyStringIfNull(request.getRemoteUser());
                if ( memberName.length() > 0 ) {
                    try {
                        DAOFactory.getMemberDAO().findByAlternateKey_MemberName(memberName);
                        user = login(request, null, memberName, PASSWORD_OF_METHOD_REALM, true);
                        ((OnlineUserImpl)user).setAuthenticationType(OnlineUser.AUTHENTICATION_TYPE_REALM);
                    } catch (ObjectNotFoundException oe) {
                        // ignore
                    }
                }
            }
            if ((user == null) && enableLoginInfoInCustomization) {
                if (authenticator == null) {
                    authenticator = ManagerFactory.getAuthenticator();
                }
                if (authenticator != null) {
                    String memberName = StringUtil.getEmptyStringIfNull(authenticator.getRemoteUser(request));
                    if (memberName.length() > 0) {
                        user = login(request, null, memberName, PASSWORD_OF_METHOD_CUSTOMIZATION, true);
                        ((OnlineUserImpl)user).setAuthenticationType(OnlineUser.AUTHENTICATION_TYPE_CUSTOMIZATION);
                    }
                }
            }
            if (user == null) {
                user = ManagerFactory.getOnlineUserFactory().getAnonymousUser(request);
                setOnlineUser(sessionID, user);
            }
        } else { //user != null
            // now we have the user, check if it is a Guest and we can login in REALM
            boolean enableLoginInfoInRealm = MVNForumConfig.getEnableLoginInfoInRealm();
            if (user.isGuest() && enableLoginInfoInRealm && (null != request.getRemoteUser())) {
                String memberName = StringUtil.getEmptyStringIfNull(request.getRemoteUser());
                if ( memberName.length() > 0 ) {
                    try {
                        DAOFactory.getMemberDAO().findByAlternateKey_MemberName(memberName);
                        user = login(request, null, memberName, PASSWORD_OF_METHOD_REALM, true);
                        ((OnlineUserImpl)user).setAuthenticationType(OnlineUser.AUTHENTICATION_TYPE_REALM);
                    } catch (ObjectNotFoundException oe) {
                        // ignore
                    }
                }
            }

            // now we have the user, check if it is a Guest and we can login in CUSTOM
            boolean enableLoginInfoInCustomization = MVNForumConfig.getEnableLoginInfoInCustomization();
            if (authenticator == null) {
                authenticator = ManagerFactory.getAuthenticator();
            }
            if (authenticator != null) {
                if (user.isGuest() && enableLoginInfoInCustomization && (null != authenticator.getRemoteUser(request))) {
                    String memberName = StringUtil.getEmptyStringIfNull(authenticator.getRemoteUser(request));
                    if (memberName.length() > 0) {
                        user = login(request, null, memberName, PASSWORD_OF_METHOD_CUSTOMIZATION, true);
                        ( (OnlineUserImpl) user).setAuthenticationType(OnlineUser.AUTHENTICATION_TYPE_CUSTOMIZATION);
                    }
                }
            }
        }
        user.getOnlineUserAction().updateLastRequestTime();
        return user;
    }

    public Collection getOnlineUserActions(int sortOption, boolean duplicateUsers) {

        Collection collection = userMap.values();
        // @todo: find a better sollution, I dont want to copy the Collection
        // this ArrayList is used to copy the values
        ArrayList retValue = new ArrayList(collection.size());

        // Get members first
        if (duplicateUsers) {
            for (Iterator memberIterator = collection.iterator(); memberIterator.hasNext(); ) {
                OnlineUser onlineUser = (OnlineUser) memberIterator.next();
                if (onlineUser.isMember()) {
                    OnlineUserAction onlineUserAction = onlineUser.getOnlineUserAction();
                    retValue.add(onlineUserAction);
                }
            }
        } else {
            // now we combine the duplicated users
            Hashtable distinctUserActions = new Hashtable();
            OnlineUserAction lastUserAction = null;
            for (Iterator memberIterator = collection.iterator(); memberIterator.hasNext(); ) {
                OnlineUser onlineUser = (OnlineUser) memberIterator.next();
                if (onlineUser.isMember()) {
                    OnlineUserAction onlineUserAction = onlineUser.getOnlineUserAction();
                    String memberName = onlineUserAction.getMemberName();
                    onlineUserAction.resetSessionCount();
                    lastUserAction = (OnlineUserAction) distinctUserActions.get(memberName);
                    if (lastUserAction == null) {
                        distinctUserActions.put(memberName, onlineUserAction);
                    } else if (onlineUserAction.getLastRequestTime().after(lastUserAction.getLastRequestTime())) {
                        distinctUserActions.put(memberName, onlineUserAction);
                        onlineUserAction.increaseSessionCount(lastUserAction.getSessionCount());
                    } else {
                        lastUserAction.increaseSessionCount(1);
                    }
                }
            }
            // now add the distinct member to the returned value
            Collection distinctCollection = distinctUserActions.values();
            for (Iterator iterator = distinctCollection.iterator(); iterator.hasNext(); ) {
                OnlineUserAction onlineUserAction = (OnlineUserAction)iterator.next();
                retValue.add(onlineUserAction);
            }
        }

        // then get guest last
        for (Iterator guestIterator = collection.iterator(); guestIterator.hasNext(); ) {
            OnlineUser onlineUser = (OnlineUser)guestIterator.next();
            if (onlineUser.isGuest()) {
                OnlineUserAction onlineUserAction = onlineUser.getOnlineUserAction();
                retValue.add(onlineUserAction);
            }
        }
        return retValue;
    }

    public void updateOnlineUserAction(HttpServletRequest request, Action action)
        throws AssertionException, DatabaseException, AuthenticationException {

        if (action != null) {
            OnlineUser onlineUser = getOnlineUser(request);
            OnlineUserAction onlineUserAction = onlineUser.getOnlineUserAction();
            // the setAction has package-default access
            // so this is the only way to set action to a user
            onlineUserAction.setAction(action);
        }
    }

    public synchronized boolean isUserOnline(String username) {

        Collection collection = userMap.values();
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            OnlineUser onlineUser = (OnlineUser)iterator.next();
            String currentUser = onlineUser.getMemberName();
            if (username.equalsIgnoreCase(currentUser)) {
                return true;
            }
        }
        return false;
    }

/************************************************************************
 * private methods
 ************************************************************************/
    private synchronized OnlineUser getOnlineUser(String sessionID) {
        return (OnlineUser)userMap.get(sessionID);
    }

    private synchronized void setOnlineUser(String sessionID, OnlineUser user) {
        if (null == user) {
            userMap.remove(sessionID);
        } else {
            userMap.put(sessionID, user);
        }
    }

    private synchronized void removeTimeoutUsers() {
        long currentTimeMillis = System.currentTimeMillis();
        // try to resolve problem with synchronization on the class-varible : timeOfLastRemoveAction
        if (currentTimeMillis - timeOfLastRemoveAction < REMOVE_INTERVAL) {
            return;
        }
        // okie now, go ahead
        timeOfLastRemoveAction = currentTimeMillis;

        Timestamp currentTime = DateUtil.getCurrentGMTTimestamp();

        Collection collection = userMap.values();
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            OnlineUser onlineUser = (OnlineUser)iterator.next();
            OnlineUserAction onlineUserAction = onlineUser.getOnlineUserAction();
            long duration = currentTime.getTime() - onlineUserAction.getLastRequestTime().getTime();
            if (duration > MVNForumConfig.SESSION_DURATION) {
                iterator.remove();
            }
        }
    }

    /************************************************************************
     * Event method
     ************************************************************************/
    public synchronized void removeOnlineUserListener(OnlineUserListener l) {
        if (onlineUserListeners != null && onlineUserListeners.contains(l)) {
            Vector v = (Vector) onlineUserListeners.clone();
            v.removeElement(l);
            onlineUserListeners = v;
        }
    }
    public synchronized void addOnlineUserListener(OnlineUserListener l) {
        Vector v = onlineUserListeners == null ? new Vector(2) : (Vector) onlineUserListeners.clone();
        if (!v.contains(l)) {
            v.addElement(l);
            onlineUserListeners = v;
        }
    }

    protected void fireDataChanged(OnlineUserEvent e) {
        if (onlineUserListeners != null) {
            Vector listeners = onlineUserListeners;
            int count = listeners.size();
            for (int i = 0; i < count; i++) {
                ((OnlineUserListener) listeners.elementAt(i)).dataChanged(e);
            }
        }
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.