/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.catalina.mapper;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import org.apache.catalina.Container;
import org.apache.catalina.ContainerListener;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.websocket.server.WsContextListener;

public class TestMapperListener extends TomcatBaseTest {

    @Test
    public void testTomcatRestartListenerCount_Bug56717() throws IOException,
            LifecycleException {
        // The test runs Tomcat twice, tests that it has started successfully,
        // and compares the counts of listeners registered on containers
        // after the first and the second starts.
        // Sample request is from TestTomcat#testSingleWebapp()

        Tomcat tomcat = getTomcatInstance();

        File appDir = new File(getBuildDirectory(), "webapps/examples");
        // app dir is relative to server home
        Context ctxt = tomcat.addWebapp(null, "/examples",
                appDir.getAbsolutePath());
        ctxt.addApplicationListener(WsContextListener.class.getName());
        tomcat.start();

        ByteChunk res;
        String text;
        res = getUrl("http://localhost:" + getPort()
                + "/examples/servlets/servlet/HelloWorldExample");
        text = res.toString();
        Assert.assertTrue(text, text.contains("<a href=\"../helloworld.html\">"));

        List<ListenersInfo> listenersFirst = new ArrayList<>();
        populateListenersInfo(listenersFirst, tomcat.getEngine());

        tomcat.stop();
        tomcat.start();

        res = getUrl("http://localhost:" + getPort()
                + "/examples/servlets/servlet/HelloWorldExample");
        text = res.toString();
        Assert.assertTrue(text, text.contains("<a href=\"../helloworld.html\">"));

        List<ListenersInfo> listenersSecond = new ArrayList<>();
        populateListenersInfo(listenersSecond, tomcat.getEngine());

        Assert.assertEquals(listenersFirst.size(), listenersSecond.size());
        for (int i = 0, len = listenersFirst.size(); i < len; i++) {
            ListenersInfo a = listenersFirst.get(i);
            ListenersInfo b = listenersSecond.get(i);
            boolean equal = a.container.getClass() == b.container.getClass()
                    && a.containerListeners.length == b.containerListeners.length
                    && a.lifecycleListeners.length == b.lifecycleListeners.length;
            if (!equal) {
                Assert.fail("The lists of listeners differ:\n" + a + "\n" + b);
            }
        }
    }

    private static class ListenersInfo {
        public final Container container;
        public final ContainerListener[] containerListeners;
        public final LifecycleListener[] lifecycleListeners;

        public ListenersInfo(Container container,
                ContainerListener[] containerListeners,
                LifecycleListener[] lifecycleListeners) {
            this.container = container;
            this.containerListeners = containerListeners;
            this.lifecycleListeners = lifecycleListeners;
        }

        @Override
        public String toString() {
            StringBuilder buf = new StringBuilder();
            buf.append("[container: \"").append(container)
                    .append("\"\n containerListeners.length: ")
                    .append(containerListeners.length)
                    .append(", lifecycleListeners.length: ")
                    .append(lifecycleListeners.length)
                    .append("\n containerListeners: ")
                    .append(Arrays.asList(containerListeners))
                    .append("\n lifecycleListeners: ")
                    .append(Arrays.asList(lifecycleListeners)).append("\n]");
            return buf.toString();
        }
    }

    private static void populateListenersInfo(List<ListenersInfo> list,
            Container container) {
        list.add(new ListenersInfo(container, container
                .findContainerListeners(), container.findLifecycleListeners()));
        for (Container child : container.findChildren()) {
            populateListenersInfo(list, child);
        }
    }
}
