/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 */
package com.ericsson.ssa.container.sim;

import com.ericsson.ssa.config.SipFactoryFacade;
import com.ericsson.ssa.dd.Parameter;

import org.apache.catalina.InstanceEvent;
import org.apache.catalina.Loader;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.util.InstanceSupport;

import java.io.IOException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.ListIterator;
import java.util.Map;
import java.util.logging.Level;

// inserted by hockey (automatic)
import java.util.logging.Logger;

import javax.servlet.Servlet;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.sip.SipApplicationSessionListener;
import javax.servlet.sip.SipErrorListener;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipSessionAttributeListener;
import javax.servlet.sip.SipSessionListener;
import javax.servlet.sip.TimerListener;
		

public class SipServletWrapper extends StandardWrapper {
    private static final long serialVersionUID = 3545793282447063096L;
    private static Logger logger = Logger.getLogger("SipContainer");
    private com.ericsson.ssa.dd.Servlet servlet;
    private boolean unloading = false;
    private SipServletFacade instance;
    private SipFactoryFacade sipFactory = null;
    javax.servlet.Servlet servletInstance;

    public SipServletWrapper(SipFactoryFacade sipFactory,
        com.ericsson.ssa.dd.Servlet servlet) {
        super();
        this.sipFactory = sipFactory;
        this.servlet = servlet;
        setName(servlet.getServletName());
    }

    public void service(ServletRequest request) throws Exception {
        getInstanceSupport()
            .fireInstanceEvent(InstanceEvent.BEFORE_DISPATCH_EVENT, instance);
        try {
            allocate().service(request, null);
        } catch (ServletException e) {
            logger.log(Level.SEVERE, "Exception in Servlet.service()", e);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Exception in Servlet.service()", e);
        }

        getInstanceSupport()
            .fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT, instance); 
    }

    public void invokeServlet(SipServletRequest request)
        throws Exception {
        try {
            if (servletInstance == null) {
                allocate();
            }

            // Trigger J2EEInstanceListener
            getInstanceSupport()
                .fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
                servletInstance);
            instance.service(request, null);
            getInstanceSupport()
                .fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                servletInstance);
        } catch (ServletException e) {
            logger.log(Level.SEVERE, "Exception allocating servlet ", e);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Exception allocating servlet ", e);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Exception allocating servlet ", e);
            throw e;
        }
    }

    public Servlet allocate() throws ServletException {
        if (unloading) {
            throw new ServletException("The servlet is currently unloading: " +
                getName());
        }

        if (instance == null) {
            synchronized (this) {
                if (instance == null) {
                    instance = doLoad();
                }
            }
        }

        return instance;
    }

    public synchronized void load() throws ServletException {
        doLoad();
    }

    public synchronized void unload() throws ServletException {
        unloading = true;

        try {
            getInstanceSupport()
                .fireInstanceEvent(InstanceEvent.BEFORE_DESTROY_EVENT, instance);

            //check against nullpointer if the app is not deployed.
            if (instance != null) {
                instance.destroy();
            }
        } catch (Throwable t) {
            throw new ServletException(sm.getString(
                    "standardWrapper.destroyException", getName()), t);
        } finally {
            try {
                getInstanceSupport()
                    .fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT,
                    instance);
            } catch (Throwable t) {
                logger.log(Level.WARNING, "Should not happen!!! (TR reported)",
                    t);
            }

            instance = null;
            unloading = false;
        }
    }

    public String getInitParameter(String name) {
        Parameter parameter = servlet.getInitParameter(name);

        if (parameter != null) {
            return parameter.getValue();
        }

        return null;
    }

    public Enumeration getInitParameters() {
        synchronized (servlet) {
            return Collections.enumeration(servlet.getInitParameters().entrySet());
        }
    }

    /* (non-Javadoc)
     * @see org.apache.catalina.core.StandardWrapper#getInitParameterNames()
     */
    public Enumeration getInitParameterNames() {
        Map initParams = servlet.getInitParameters();

        if ((initParams == null) || initParams.isEmpty()) {
            // will return empty enumeration.
            return Collections.enumeration(Collections.emptyMap().keySet());
        }

        return Collections.enumeration(servlet.getInitParameters().keySet());
    }

    public String getServletName() {
        return servlet.getServletName();
    }

    protected SipServletFacade doLoad() throws ServletException {
        SipServletFacade facade;
        Loader loader = getLoader();

        if (loader == null) {
            unavailable(null);
            throw new ServletException("Missing loader: " + getName());
        }

        ClassLoader classLoader = getLoader().getClassLoader();
        Class servletClass = null;

        try {
            if (classLoader != null) {
                try {
                    // On the first load, we should uase the class loader to load the
                    // servlet byte code
                    servletClass = classLoader.loadClass(getServletClass());
                } catch (ClassNotFoundException e) {
                    // This can occcur in servlet reload since the ClassLoader
                    // doesn't contain /WEB-INF/classes in
                    // the class loader (but still can throw a
                    // ClassNotFoundException)
                    servletClass = Class.forName(getServletClass());
                }
            } else {
                servletClass = Class.forName(getServletClass());
            }
        } catch (ClassNotFoundException ex) {
            unavailable(null);

            throw new ServletException(String.format(
                    "Wrapper cannot find servlet class %1$s or a " +
                    "class it depends on", getServletClass()), ex);
        }

        Servlet target = null;

        try {
            target = (Servlet) servletClass.newInstance();
            servletInstance = target;

            facade = new SipServletFacade(target, servlet);
            registerListeners(target);

            // Trigger J2EEInstanceListener
            getInstanceSupport()
                .fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, target);

            facade.init(this);

            // Trigger J2EEInstanceListener
            getInstanceSupport()
                .fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, target);
        } catch (InstantiationException ex) {
            unavailable(null);
            // Trigger J2EEInstanceListener
            getInstanceSupport()
                .fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, target);

            throw new ServletException(String.format(
                    "Servlet class %1$s cannot be instantiated.",
                    getServletClass()), ex);
        } catch (IllegalAccessException ex) {
            unavailable(null);

            // Trigger J2EEInstanceListener
            getInstanceSupport()
                .fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, target);

            throw new ServletException(ex);
        }

        return facade;
    }

    public void registerListeners(Servlet instance) {
        ArrayList<String> sipServletsWithListeners = sipFactory.getSipApplicationListeners()
                                                               .getSipServletsWithListeners();
        ListIterator<String> it = sipServletsWithListeners.listIterator();

        boolean foundMe = false;

        while (it.hasNext()) {
            String servlet = it.next();

            if (servlet.equals(getServletClass())) {
                foundMe = true;

                break;
            }
        }

        if (!foundMe) {
            // We should only assign listeners if this SipServlet class
            // is present in sip.xml (according to SSA spec!)
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "registerListener no sip.xml entry");
            }

            return;
        }

        if (instance instanceof TimerListener) {
            sipFactory.getSipApplicationListeners()
                      .setTimerListener((TimerListener) instance);
        }

        if (instance instanceof SipApplicationSessionListener) {
            sipFactory.getSipApplicationListeners()
                      .addSipApplicationSessionListener((SipApplicationSessionListener) instance);
        }

        if (instance instanceof SipSessionListener) {
            sipFactory.getSipApplicationListeners()
                      .addSipSessionListener((SipSessionListener) instance);
        }

        if (instance instanceof SipErrorListener) {
            sipFactory.getSipApplicationListeners()
                      .addSipErrorListener((SipErrorListener) instance);
        }

        if (instance instanceof SipSessionAttributeListener) {
            sipFactory.getSipApplicationListeners()
                      .addSipSessionAttributeListener((SipSessionAttributeListener) instance);
        }

        if (instance instanceof ServletContextListener) {
            sipFactory.getSipApplicationListeners()
                      .addServletContextListener((ServletContextListener) instance);
        }
    }

}
