/**
 * JaDOrT: JASMINe Deployment Orchestration Tool
 * Copyright (C) 2008-2009 Bull S.A.S.
 * Copyright (C) 2008-2009 France Telecom R&D
 * Contact: jasmine@ow2.org
 *
 * 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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: OnlyAllowUsersWithSessionFilter.java 2965 2009-01-12 12:14:51Z alitokmen $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.filters;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Filter that only allows users with a valid session. This class should be
 * compiled for Java 1.4 at most (I think the current configuration points to
 * Java 1.3 with Servlet specification 2.3).
 * 
 * @author Malek Chahine
 * @author Remy Bresson
 * @author S. Ali Tokmen
 */
public final class OnlyAllowUsersWithSessionFilter implements Filter, OnlyAllowUsersWithSessionFilterMBean {

    /**
     * Whether the filter is active.
     */
    private boolean active = false;

    /**
     * MBean server. We assume the first MBean server with the J2EEServer MBean
     * registered to it is the MBean server of the application.
     */
    private MBeanServer mBeanServer;

    /**
     * Filter's MBean object name.
     */
    private ObjectName filterON;

    /**
     * {@inheritDoc} Connects to the MBean server and registers the Filter
     * MBean.
     */
    public void init(final FilterConfig filterConfig) throws ServletException {
        try {
            // Find the J2EEServer
            ObjectName j2eeServer = null;

            // Construct the list of MBeanServers. WebLogic doesn't support
            // MBeanServerFactory.findMBeanServer(null), we need to find its
            // MBeanServer in the context instead
            List mBeanServers = new ArrayList();
            try {
                mBeanServers.addAll(MBeanServerFactory.findMBeanServer(null));
            } catch (Exception ignored) {
                // Ignore
            }
            try {
                InitialContext ctx = new InitialContext();
                Object webLogicMBeanServer = ctx.lookup("java:comp/env/jmx/runtime");
                if (webLogicMBeanServer != null) {
                    mBeanServers.add(webLogicMBeanServer);
                }
            } catch (Exception ignored) {
                // Ignore
            }
            if (mBeanServers.size() == 0) {
                throw new IllegalStateException("Cannot find any MBeanServer!");
            }

            // Find the J2EEServer in the MBeanServers
            for (Iterator i = mBeanServers.iterator(); i.hasNext();) {
                Set j2eeServerNames;
                MBeanServer server = (MBeanServer) i.next();

                // Standard J2EE servers
                j2eeServerNames = server.queryNames(new ObjectName("*:j2eeType=J2EEServer,*"), null);
                if (j2eeServerNames.size() > 0) {
                    j2eeServer = (ObjectName) j2eeServerNames.iterator().next();
                    this.mBeanServer = server;
                    break;
                }

                // WebLogic
                j2eeServerNames = server.queryNames(new ObjectName("*:Type=ServerRuntime,*"), null);
                if (j2eeServerNames.size() > 0) {
                    j2eeServer = (ObjectName) j2eeServerNames.iterator().next();
                    this.mBeanServer = server;
                    break;
                }
            }
            if (this.mBeanServer == null) {
                throw new IllegalStateException("Cannot locate the J2EEServer or ServerRuntime MBean in any MBeanServer");
            }

            // Build the filter ObjectName and register it on the MBeanServer
            ServletContext context = filterConfig.getServletContext();
            Method getContextPath = context.getClass().getMethod("getContextPath", null);
            String contextPath = (String) getContextPath.invoke(context, null);
            if (contextPath == null || contextPath.length() < 1) {
                contextPath = "/";
            }
            this.filterON = new ObjectName(j2eeServer.getDomain() + ":type=J2EEFilter,path=" + contextPath);
            this.mBeanServer.registerMBean(this, this.filterON);
        } catch (Exception e) {
            throw new ServletException("Cannot instanciate the OnlyAllowUsersWithSessionFilter", e);
        }
    }

    /**
     * {@inheritDoc} Unregisters the Filter MBean from the MBean server.
     */
    public void destroy() {
        try {
            this.mBeanServer.unregisterMBean(this.filterON);
        } catch (Exception ignored) {
            // TODO: never mind...
        }
    }

    /**
     * {@inheritDoc} This filter has a programmable behavior:
     * <ul>
     * <li>If the filter is not set as being active, then the call will be
     * forwarded in the chain using
     * {@link FilterChain#doFilter(ServletRequest, ServletResponse)}</li>
     * <li>If the filter is set as being active:</li>
     * <ul>
     * <li>If the {@link ServletRequest} has a valid session, then the call will
     * be forwarded in the chain using
     * {@link FilterChain#doFilter(ServletRequest, ServletResponse)}</li>
     * <li>Else, the response is set to an error (
     * {@link HttpServletResponse#SC_SERVICE_UNAVAILABLE}) and all further
     * processing is blocked.</li>
     * </ul>
     */
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
            throws IOException, ServletException {
        if (this.active && request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
            HttpSession session = req.getSession(false);
            if (session == null) {
                resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "This server is going down for maintenance.\n"
                        + "Please use another server in our server farm or try again later.");
                return;
            }
        }

        chain.doFilter(request, response);
    }

    /**
     * {@inheritDoc}
     */
    public boolean getactive() {
        return this.active;
    }

    /**
     * {@inheritDoc}
     */
    public void setactive(final boolean active) {
        this.active = active;
    }
}
