/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2011 Bull S.A.S.
 * Contact: jonas-team@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 (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
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.ow2.jonas.web.tomcat7.osgi;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.TldConfig;
import org.apache.jasper.servlet.JspServlet;

/**
 * This TLD listener will manage TLD resources that are exposed through OSGi.
 * For example jsf service may expose some TLDs and this listener will register them into Tomcat.
 * @author Florent Benoit
 */
public class OSGiTldListener implements LifecycleListener {

    /**
     * Instance that will be used to managed the OSGi TLDs resources.
     */
    private TldConfig tldConfig;

    /**
     * List of resources to analyze.
     */
    private List<URL> resourcesToMonitor = null;

    /**
     * Tld Scan Stream method.
     */
    private Method tldScanStreamMethod = null;

    /**
     * Default constructor.
     * @param resourcesToMonitor the TLDs resources that needs to be scanned
     */
    public OSGiTldListener(final List<URL> resourcesToMonitor) {
        this.resourcesToMonitor = resourcesToMonitor;
        this.tldConfig = new TldConfig();

        try {
            this.tldScanStreamMethod = tldConfig.getClass().getDeclaredMethod("tldScanStream", InputStream.class);
            // private method
            this.tldScanStreamMethod.setAccessible(true);
        } catch (SecurityException e) {
            throw new IllegalStateException("Unable to find the given method", e);
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("Unable to find the given method", e);
        }

    }

    /**
     * Notify the tld scanner depending on the events.
     * @param event the lifecycle event
     */
    public void lifecycleEvent(final LifecycleEvent event) {
        Context context = null;
        // Identify the context we are associated with
        try {
            context = (Context) event.getLifecycle();
        } catch (ClassCastException e) {
            return;
        }

        if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
            // Init our internal tldconfig object
            tldConfig.lifecycleEvent(event);
        } else if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            // needs to analyze the OSGi TLD resources
            if (resourcesToMonitor != null) {
                for (URL url : resourcesToMonitor) {
                    try {
                        URLConnection urlConnection = url.openConnection();
                        urlConnection.setDefaultUseCaches(false);

                        tldScanStreamMethod.invoke(tldConfig, urlConnection.getInputStream());
                    } catch (IOException e) {
                        throw new IllegalStateException("Unable to add the TLD", e);
                    } catch (IllegalAccessException e) {
                        throw new IllegalStateException("Unable to add the TLD", e);
                    } catch (InvocationTargetException e) {
                        throw new IllegalStateException("Unable to add the TLD", e);
                    }
                }
            }


            String[] listeners = tldConfig.getTldListeners();
            if (listeners != null) {
                for (String listener : listeners) {
                    context.addApplicationListener(listener);
                }
            }

            addTldIntoCache(context);

        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            // Strop our internal tldconfig object
            tldConfig.lifecycleEvent(event);
        }
    }

    /**
     * Adds a listener on the JSP servlet in order to add TagLib into the taglib cache.
     * @param context the given standard context.
     */
    protected void addTldIntoCache(final Context context) {

        // find all the declared servlets/wrappers
        Container[] childs = context.findChildren();
        // Childrens are all Wrappers
        if (childs != null) {
            for (Container child : childs) {
                Wrapper wrapper = (Wrapper) child;
                String servletClass = wrapper.getServletClass();
                if (JspServlet.class.getName().equals(servletClass)) {
                    wrapper.addInstanceListener(new OSGiInstanceTldCacheListener(resourcesToMonitor));
                }


            }
        }
    }


}
