/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package io.undertow.servlet.core;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.Session;
import io.undertow.server.session.SessionListener;
import io.undertow.servlet.api.ThreadSetupAction;
import io.undertow.servlet.handlers.ServletRequestContext;
import io.undertow.servlet.spec.HttpSessionImpl;

import java.util.HashSet;

/**
 * Class that bridges between Undertow native session listeners and servlet ones.
 *
 * @author Stuart Douglas
 */
public class SessionListenerBridge implements SessionListener {

    public static final String IO_UNDERTOW = "io.undertow";
    private final ThreadSetupAction threadSetup;
    private final ApplicationListeners applicationListeners;
    private final ServletContext servletContext;

    public SessionListenerBridge(final ThreadSetupAction threadSetup, final ApplicationListeners applicationListeners, final ServletContext servletContext) {
        this.threadSetup = threadSetup;
        this.applicationListeners = applicationListeners;
        this.servletContext = servletContext;
    }

    @Override
    public void sessionCreated(final Session session, final HttpServerExchange exchange) {
        final HttpSessionImpl httpSession = SecurityActions.forSession(session, servletContext, true);
        applicationListeners.sessionCreated(httpSession);
    }

    @Override
    public void sessionDestroyed(final Session session, final HttpServerExchange exchange, final SessionDestroyedReason reason) {
        ThreadSetupAction.Handle handle = null;
        try {
            final HttpSessionImpl httpSession = SecurityActions.forSession(session, servletContext, false);
            if (reason == SessionDestroyedReason.TIMEOUT) {
                handle = threadSetup.setup(exchange);
            }
            applicationListeners.sessionDestroyed(httpSession);
            //we make a defensive copy here, as there is no guarantee that the underlying session map
            //is a concurrent map, and as a result a concurrent modification exception may be thrown
            HashSet<String> names = new HashSet<>(session.getAttributeNames());
            for(String attribute : names) {
                session.removeAttribute(attribute);
            }
        } finally {
            if (handle != null) {
                handle.tearDown();
            }
            ServletRequestContext current = SecurityActions.currentServletRequestContext();
            if (current != null && current.getSession() != null && current.getSession().getSession() == session) {
                current.setSession(null);
            }
        }
    }

    @Override
    public void attributeAdded(final Session session, final String name, final Object value) {
        if(name.startsWith(IO_UNDERTOW)) {
            return;
        }
        final HttpSessionImpl httpSession = SecurityActions.forSession(session, servletContext, false);
        applicationListeners.httpSessionAttributeAdded(httpSession, name, value);
        if (value instanceof HttpSessionBindingListener) {
            ((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(httpSession, name, value));
        }
    }

    @Override
    public void attributeUpdated(final Session session, final String name, final Object value, final Object old) {
        if(name.startsWith(IO_UNDERTOW)) {
            return;
        }
        final HttpSessionImpl httpSession = SecurityActions.forSession(session, servletContext, false);
        if (old != value) {
            if (old instanceof HttpSessionBindingListener) {
                ((HttpSessionBindingListener) old).valueUnbound(new HttpSessionBindingEvent(httpSession, name, old));
            }
            applicationListeners.httpSessionAttributeReplaced(httpSession, name, old);
        }
        if (value instanceof HttpSessionBindingListener) {
            ((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(httpSession, name, value));
        }
    }

    @Override
    public void attributeRemoved(final Session session, final String name, final Object old) {
        if(name.startsWith(IO_UNDERTOW)) {
            return;
        }
        final HttpSessionImpl httpSession = SecurityActions.forSession(session, servletContext, false);
        if (old != null) {
            applicationListeners.httpSessionAttributeRemoved(httpSession, name, old);
            if (old instanceof HttpSessionBindingListener) {
                ((HttpSessionBindingListener) old).valueUnbound(new HttpSessionBindingEvent(httpSession, name, old));
            }
        }
    }

    @Override
    public void sessionIdChanged(Session session, String oldSessionId) {
    }
}

@@ -1183,16 +1183,55 @@
 nImpl;%0A%0A
+import java.security.AccessController;%0A
 import j
@@ -3341,19 +3341,57 @@
         
+Session underlying = null;%0A            
 if
- 
 (current
@@ -3434,45 +3434,368 @@
 null
- && current.getSession().getSession()
+) %7B%0A                if(System.getSecurityManager() == null) %7B%0A                    underlying = current.getSession().getSession();%0A                %7D else %7B%0A                    underlying = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(current.getSession()));%0A                %7D%0A            %7D%0A%0A            if (current != null && underlying
  == 

Make session listener work correctly under security manager