/** * Copyright (C) 2011 JTalks.org Team * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jtalks.jcommune.web.filters; import org.apache.commons.lang.StringUtils; import org.jtalks.jcommune.service.security.SecurityService; import org.jtalks.jcommune.web.logging.LoggerMdc; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * This filter binds a current user to the thread therefore allowing to show her username in each line of logs (which is * helpful when it comes to combining several actions of user and looking after the logs). It's implemented by using * features like {@link org.slf4j.MDC}, we need to register a username in the beginning and then unregister it in the * end of the request so that the memory doesn't leak. <p>Note, that if a user is anonymous, we don't have means to * distinguish between them except by using a session id, and that's what we're actually doing. </p>See logger * configuration to see where the username is going to appear, for instance in log4j it may look like {@code * %X{userName}}. * * @author Anuar_Nurmakanov * @see LoggerMdc */ public class LoggingConfigurationFilter implements Filter { /** * A prefix to be used an anonymous user 'username'. */ private static final String ANONYMOUS_PREFIX = "anonymous-"; /** * How much of session id we need to cut to construct a 'username' for anonymous user. We don't want this number to * be big because it will complicate reading of logs. */ private static final Integer SESSION_ID_LENGTH = 4; private final SecurityService securityService; private final LoggerMdc loggerMdc; /** * @param securityService to get current user for registration in logging context * @param loggerMdc to register and unregister user in MDC, we don't want to use static methods of logger */ public LoggingConfigurationFilter(SecurityService securityService, LoggerMdc loggerMdc) { this.securityService = securityService; this.loggerMdc = loggerMdc; } /** * {@inheritDoc} */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String currentUserName = securityService.getCurrentUserUsername(); String sessionId = ((HttpServletRequest) request).getSession().getId();//session always gets created boolean successfulRegistered = registerCurrentUserName(currentUserName, sessionId); try { chain.doFilter(request, response); } finally { if (successfulRegistered) { loggerMdc.unregisterUser(); } } } /** * Register the user in the MDC under USER_KEY. * * @param userName the name of current user * @return true if the username was registered in MDC, false only if the user is anonymous and session id is empty. * If true, you have to unregister the user at some point otherwise the memory will leak. */ private boolean registerCurrentUserName(String userName, String sessionId) { if (!StringUtils.isEmpty(userName)) { loggerMdc.registerUser(userName); return true; } else if (isSessionIdValid(sessionId)) { loggerMdc.registerUser(ANONYMOUS_PREFIX + StringUtils.right(sessionId, SESSION_ID_LENGTH)); return true; } else { return false; } } /** * Determines whether session id is possible to use to construct a 'username' for an anonymous user. * * @param sessionId the session id of the anonymous user * @return true if the id is not null */ private boolean isSessionIdValid(String sessionId) { return !StringUtils.isEmpty(sessionId); } /** * {@inheritDoc} */ @Override public void destroy() { //empty } /** * {@inheritDoc} */ @Override public void init(FilterConfig filterConfig) throws ServletException { //empty } }