/**
 * 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: OSGiInstanceTldCacheListener.java 21566 2011-08-08 12:28:12Z cazauxj $
 * --------------------------------------------------------------------------
 */

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

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

import org.apache.catalina.InstanceEvent;
import org.apache.catalina.InstanceListener;
import org.apache.jasper.EmbeddedServletOptions;
import org.apache.jasper.compiler.TldLocationsCache;
import org.apache.jasper.servlet.JspServlet;

/**
 * Instance listener of the JSP Servlet in order to add the taglibs found into the cache.
 * @author Florent Benoit
 */
public class OSGiInstanceTldCacheListener implements InstanceListener {

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

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

    /**
     * Lifecycle event for the instance.
     * @param event the event of the instance.
     */
    @SuppressWarnings("unchecked")
    @Override
    public void instanceEvent(final InstanceEvent event) {
        // Only react when the instance when just alive (but not already inited)
        if (InstanceEvent.AFTER_INIT_EVENT.equals(event.getType())) {

            // The servlet is a JspServlet
            try {
                JspServlet jspServlet = (JspServlet) event.getServlet();
                Field optionsField = JspServlet.class.getDeclaredField("options");
                optionsField.setAccessible(true);
                EmbeddedServletOptions options = (EmbeddedServletOptions) optionsField.get(jspServlet);
                TldLocationsCache tldLocationCache = options.getTldLocationsCache();

                // get the method
                Method getUriFromTldMethod = TldLocationsCache.class.getDeclaredMethod("getUriFromTld", String.class,
                        InputStream.class);
                // it is private
                getUriFromTldMethod.setAccessible(true);

                // mapping field
                Field mappingsField = TldLocationsCache.class.getDeclaredField("mappings");
                mappingsField.setAccessible(true);

                // Add each taglib into the cache
                if (resourcesToMonitor != null) {
                    for (URL url : resourcesToMonitor) {
                        InputStream stream = null;
                        try {
                            URLConnection urlConnection = url.openConnection();
                            urlConnection.setDefaultUseCaches(false);

                            stream = urlConnection.getInputStream();
                            String uri = (String) getUriFromTldMethod.invoke(tldLocationCache, url.toString(), stream);
                            Hashtable<String, String[]> mappings = (Hashtable<String, String[]>) mappingsField
                                    .get(tldLocationCache);
                            if (uri != null && mappings.get(uri) == null) {
                                mappings.put(uri, new String[] {url.toString(), url.getPath().substring(1)});
                            }
                        } 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);
                        } finally {
                            try {
                                stream.close();
                            } catch (IOException e) {
                                throw new IllegalStateException("Unable to add the TLD", e);
                            }
                        }
                    }
                }

            } catch (SecurityException e) {
                throw new IllegalStateException("Unable to get TLDCache", e);
            } catch (NoSuchFieldException e) {
                throw new IllegalStateException("Unable to get TLDCache", e);
            } catch (IllegalArgumentException e) {
                throw new IllegalStateException("Unable to get TLDCache", e);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Unable to get TLDCache", e);
            } catch (NoSuchMethodException e) {
                throw new IllegalStateException("Unable to get TLDCache", e);
            }
        }
    }

}
