package org.pac4j.cas.logout;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.session.HashMapBackedSessionMappingStorage;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.jasig.cas.client.session.SingleSignOutHandler;
import org.pac4j.cas.client.CasClient;
import org.pac4j.core.context.J2EContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>This class is the logout handler for the {@link CasClient}, inspired by the {@link SingleSignOutHandler} of the Apereo CAS client.</p>
 *
 * <p>It is automatically defined for the {@link J2EContext} by the {@link org.pac4j.cas.config.CasConfiguration} when no {@link CasLogoutHandler} is defined.</p>
 *
 * <p>As web sessions are saved in a specific storage, the {@link org.jasig.cas.client.session.SingleSignOutHttpSessionListener} must also be declared to clean this storage when sessions are destroyed (to avoid an out of memory).</p>
 *
 * @author Jerome Leleu
 * @since 1.4.0
 */
public class CasSingleSignOutHandler implements CasLogoutHandler<J2EContext> {
    
    protected static final Logger logger = LoggerFactory.getLogger(CasSingleSignOutHandler.class);

    private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();

    private boolean eagerlyCreateSessions = true;

    private LogoutStrategy logoutStrategy = isServlet30() ? new Servlet30LogoutStrategy() : new Servlet25LogoutStrategy();
    
    public CasSingleSignOutHandler() { }

    @Override
    public void recordSession(final J2EContext context, final String ticket) {
        final HttpSession session = context.getRequest().getSession(this.eagerlyCreateSessions);

        if (session == null) {
            logger.debug("No session currently exists (and none created). Cannot record session information for single sign out.");
            return;
        }

        final String sessionId = session.getId();
        try {
            this.sessionMappingStorage.removeBySessionById(sessionId);
        } catch (final Exception e) {
            logger.warn("failed to remove session by id: ", sessionId);
            // ignore if the session is already marked as invalid.  Nothing we can do!
        }
        sessionMappingStorage.addSessionById(ticket, session);
    }
    
    @Override
    public void destroySessionBack(final J2EContext context, final String ticket) {
        final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(ticket);
        if (session != null) {
            String sessionID = session.getId();

            logger.debug("Invalidating session [{}] for ticket [{}]", sessionID, ticket);

            try {
                session.invalidate();
            } catch (final IllegalStateException e) {
                logger.debug("Error invalidating session", e);
            }
            this.logoutStrategy.logout(context.getRequest());
        }
    }

    private static boolean isServlet30() {
        try {
            return HttpServletRequest.class.getMethod("logout") != null;
        } catch (final NoSuchMethodException e) {
            return false;
        }
    }

    /**
     * Abstracts the ways we can force logout with the Servlet spec.
     */
    private interface LogoutStrategy {

        void logout(HttpServletRequest request);
    }

    private static class Servlet25LogoutStrategy implements LogoutStrategy {

        @Override
        public void logout(final HttpServletRequest request) {
            // nothing additional to do here
        }
    }

    private static class Servlet30LogoutStrategy implements LogoutStrategy {

        public void logout(final HttpServletRequest request) {
            try {
                request.logout();
            } catch (final ServletException e) {
                logger.debug("Error performing request.logout.");
            }
        }
    }

    public boolean isEagerlyCreateSessions() {
        return eagerlyCreateSessions;
    }

    public void setEagerlyCreateSessions(boolean eagerlyCreateSessions) {
        this.eagerlyCreateSessions = eagerlyCreateSessions;
    }

    public SessionMappingStorage getSessionMappingStorage() {
        return sessionMappingStorage;
    }

    public void setSessionMappingStorage(SessionMappingStorage sessionMappingStorage) {
        this.sessionMappingStorage = sessionMappingStorage;
    }
}

@@ -28,93 +28,8 @@
 t;%0A%0A
-import javax.servlet.ServletException;%0Aimport javax.servlet.http.HttpServletRequest;%0A
 impo
@@ -63,16 +63,16 @@
 ession;%0A
+
 %0Aimport 
@@ -1066,21 +1066,17 @@
 text%3E %7B%0A
-    
 %0A
+
     prot
@@ -1323,136 +1323,8 @@
 e;%0A%0A
-    private LogoutStrategy logoutStrategy = isServlet30() ? new Servlet30LogoutStrategy() : new Servlet25LogoutStrategy();%0A    %0A
     
@@ -2161,13 +2161,9 @@
   %7D%0A
-    
 %0A
+
     
@@ -2626,32 +2626,32 @@
 eException e) %7B%0A
+
                 
@@ -2715,1034 +2715,8 @@
   %7D%0A
-            this.logoutStrategy.logout(context.getRequest());%0A        %7D%0A    %7D%0A%0A    private static boolean isServlet30() %7B%0A        try %7B%0A            return HttpServletRequest.class.getMethod(%22logout%22) != null;%0A        %7D catch (final NoSuchMethodException e) %7B%0A            return false;%0A        %7D%0A    %7D%0A%0A    /**%0A     * Abstracts the ways we can force logout with the Servlet spec.%0A     */%0A    private interface LogoutStrategy %7B%0A%0A        void logout(HttpServletRequest request);%0A    %7D%0A%0A    private static class Servlet25LogoutStrategy implements LogoutStrategy %7B%0A%0A        @Override%0A        public void logout(final HttpServletRequest request) %7B%0A            // nothing additional to do here%0A        %7D%0A    %7D%0A%0A    private static class Servlet30LogoutStrategy implements LogoutStrategy %7B%0A%0A        public void logout(final HttpServletRequest request) %7B%0A            try %7B%0A                request.logout();%0A            %7D catch (final ServletException e) %7B%0A                logger.debug(%22Error performing request.logout.%22);%0A            %7D%0A
     

simplify CAS logout handling