/**
 * Copyright (c) 2017-2024 Linagora
 * 
 * This program/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 program/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 program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.binding.soap;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.nio.file.Paths;

import javax.management.MalformedObjectNameException;

import org.apache.axis2.Constants;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.junit.ClassRule;
import org.junit.Test;
import org.ow2.petals.binding.soap.listener.incoming.jetty.AxisServletServer;
import org.ow2.petals.binding.soap.listener.incoming.jetty.SoapServletServer;
import org.ow2.petals.component.framework.AbstractComponent;
import org.ow2.petals.component.framework.DefaultBootstrap;
import org.ow2.petals.component.framework.exception.NegativeValueException;
import org.ow2.petals.component.framework.exception.StrictPositiveValueException;
import org.ow2.petals.component.framework.junit.mbean.AbstractBootstrapTest;
import org.ow2.petals.component.framework.junit.rule.ComponentUnderTest;
import org.ow2.petals.junit.rules.log.handler.InMemoryLogHandler;

import com.ebmwebsourcing.easycommons.lang.reflect.ReflectionHelper;

/**
 * Unit tests of {@link SoapBootstrap}
 * 
 * @author Christophe DENEUX - Linagora
 * 
 */
public class SoapBootstrapTest extends AbstractBootstrapTest {

    @ClassRule
    public static final InMemoryLogHandler IN_MEMORY_LOG_HANDLER = new InMemoryLogHandler();

    public SoapBootstrapTest() throws MalformedObjectNameException {
        super();
    }

    /**
     * Check that the component embeds the right default configuration in its JBI descriptor (values set to their
     * default value in jbi.xml)
     */
    @Test
    public void defaultConfiguration_definedInJbiDescriptor() throws Exception {

        final DefaultBootstrap bootstrap = this.initBootstrap(new SoapBootstrap());
        this.embeddedJmxSrvCon.registerConfigurationInstallerMBean(bootstrap);

        this.assertDefaultConfigurationValues(bootstrap);
    }

    /**
     * Check to set invalid values
     */
    @Test
    public void setInvalidValues() throws Exception {

        this.embeddedJmxSrvCon.registerConfigurationInstallerMBean(this.initBootstrap(new SoapBootstrap()));

        // -------------------------------
        // About the consume side
        // -------------------------------
        try {
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_PORT, -1);
        } catch (final NegativeValueException e) {
            // NOP, it's the expected exception
        }
        try {
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_PORT, 0);
        } catch (final StrictPositiveValueException e) {
            // NOP, it's the expected exception
        }

        // -------------------------------
        // About the provide side
        // -------------------------------
        // 1 - Negative values are invalid
        try {
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_MAX_HTTP_CONNECTIONS_PER_HOST,
                    Integer.valueOf(-1));
        } catch (final NegativeValueException e) {
            // NOP, it's the expected exception
        }
    }

    /**
     * Check to set valid values
     */
    @Test
    public void setValues() throws Exception {

        final DefaultBootstrap bootstrap = this.initBootstrap(new SoapBootstrap());
        this.embeddedJmxSrvCon.registerConfigurationInstallerMBean(bootstrap);

        // -------------------------------
        // About the consume side
        // -------------------------------
        this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_HOST, "localhost");
        final String httpHost = "127.0.0.1";
        this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_HOST, httpHost);
        assertEquals(httpHost, this.jmxClient.getBootstrapAttributeAsString(SoapBootstrap.ATTR_NAME_HTTP_HOST));
        final int httpPort = 8080;
        this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_PORT, httpPort);
        assertEquals(httpPort, this.jmxClient.getBootstrapAttributeAsInt(SoapBootstrap.ATTR_NAME_HTTP_PORT));

        // -------------------------------
        // About the provide side
        // -------------------------------
        this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_MAX_HTTP_CONNECTIONS_PER_HOST, 0);
        final int httpConPerHost = 6;
        this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_MAX_HTTP_CONNECTIONS_PER_HOST, httpConPerHost);
        assertEquals(httpConPerHost,
                this.jmxClient.getBootstrapAttributeAsInt(SoapBootstrap.ATTR_NAME_MAX_HTTP_CONNECTIONS_PER_HOST));
    }

    private void assertDefaultConfigurationValues(final DefaultBootstrap bootstrap) throws Exception {

        assertEquals(SoapConstants.HttpServer.DEFAULT_HTTP_PORT,
                this.jmxClient.getBootstrapAttributeAsInt(SoapBootstrap.ATTR_NAME_HTTP_PORT));
        assertEquals(bootstrap.getJbiComponentConfiguration().getComponent().getProcessorMaxPoolSize().getValue(),
                this.jmxClient.getBootstrapAttributeAsInt(SoapBootstrap.ATTR_NAME_MAX_HTTP_CONNECTIONS_PER_HOST));
    }

    /**
     * Check that valid values set through the component bootstrap through JMX are correctly used by component.
     */
    @Test
    // @Ignore("Enable this test when CDK JUnit will be able to run with files '.mar'")
    public void setValuesAreUsed() throws Throwable {
        // To access the component bootstrap through JMX, the component has not to be installed, only loaded
        final ComponentUnderTest componentUnderTest = new ComponentUnderTest(false, false)
                .addLogHandler(IN_MEMORY_LOG_HANDLER.getHandler()).addEmbeddedJmxSrv(this.embeddedJmxSrvCon);
        componentUnderTest.addFile(AbstractComponentTest.addresingMarFileName(), "install");
        componentUnderTest.create();

        try {
            final AbstractComponent component = componentUnderTest.getComponentObject();
            assertNotNull(component);
            assertTrue(component instanceof SoapComponent);
            final SoapComponent soapComponent = (SoapComponent) component;

            // Set values using the component bootstrap through JMX
            final String httpHost = "127.0.0.1";
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_HOST, httpHost);
            final int httpPort = 8099;
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_PORT, Integer.valueOf(httpPort));
            final int httpAcceptors = 7;
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_ACCEPTORS,
                    Integer.valueOf(httpAcceptors));
            final int httpBacklogSize = 321;
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTP_BACKLOG_SIZE,
                    Integer.valueOf(httpBacklogSize));
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_ENABLED, true);
            final int httpsPort = 9098;
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_PORT, Integer.valueOf(httpsPort));
            final int maxHttpConnectionsPerHost = 13;
            final int httpsAcceptors = 8;
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_ACCEPTORS,
                    Integer.valueOf(httpsAcceptors));
            final int httpsBacklogSize = 123;
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_BACKLOG_SIZE,
                    Integer.valueOf(httpsBacklogSize));
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_KEYSTORE_TYPE, "JKS");
            final String keystoreFile = Paths.get(this.getClass().getClassLoader().getResource("test.jks").toURI())
                    .toFile().getAbsolutePath();
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_KEYSTORE_FILE, keystoreFile);
            final String keystorePwd = "testStorePass";
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_KEYSTORE_PWD, keystorePwd);
            final String keystoreKeyPwd = "testKeyPass";
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_HTTPS_KEY_PWD, keystoreKeyPwd);
            this.jmxClient.setBootstrapAttribute(SoapBootstrap.ATTR_NAME_MAX_HTTP_CONNECTIONS_PER_HOST,
                    maxHttpConnectionsPerHost);

            // We continue component lifecycle to be able to check values are correctly used
            componentUnderTest.install();
            // componentUnderTest.deployServiceConfigurations();
            componentUnderTest.start();

            // Configuration assertions on embedded HTTP server side
            final AxisServletServer axisServletServer = soapComponent.getExternalListenerManager().getHttpServer();
            final SoapServletServer soapServletServer = (SoapServletServer) ReflectionHelper
                    .getFieldValue(AxisServletServer.class, axisServletServer, "server", false);
            final Server httpServer = (Server) ReflectionHelper.getFieldValue(SoapServletServer.class,
                    soapServletServer, "server", false);

            final SoapComponentContext soapComponentContext = soapComponent.getSoapContext();
            assertEquals(String.format("http://%s:%d/", httpHost, httpPort),
                    soapComponentContext.getSoapServerConfig().getBaseURL(Constants.TRANSPORT_HTTP));
            assertEquals(String.format("https://%s:%d/", httpHost, httpsPort),
                    soapComponentContext.getSoapServerConfig().getBaseURL(Constants.TRANSPORT_HTTPS));
            assertNotNull(httpServer.getConnectors());
            assertEquals(2, httpServer.getConnectors().length);
            assertTrue(httpServer.getConnectors()[0] instanceof ServerConnector);
            assertTrue(httpServer.getConnectors()[1] instanceof ServerConnector);
            final ServerConnector httpServerConnector;
            final ServerConnector httpsServerConnector;
            if (httpServer.getConnectors()[0].getDefaultConnectionFactory() instanceof SslConnectionFactory) {
                httpServerConnector = (ServerConnector) httpServer.getConnectors()[1];
                httpsServerConnector = (ServerConnector) httpServer.getConnectors()[0];
            } else {
                httpServerConnector = (ServerConnector) httpServer.getConnectors()[0];
                httpsServerConnector = (ServerConnector) httpServer.getConnectors()[1];
            }
            // Assertion about HTTP connector of the embedded HTTP server
            assertEquals(httpHost, httpServerConnector.getHost());
            assertEquals(httpPort, httpServerConnector.getPort());
            assertEquals(httpAcceptors, httpServerConnector.getAcceptors());
            assertEquals(httpBacklogSize, httpServerConnector.getAcceptQueueSize());

            // Assertion about HTTPS connector of the embedded HTTP server
            assertEquals(httpHost, httpsServerConnector.getHost());
            assertEquals(httpsPort, httpsServerConnector.getPort());
            assertEquals(httpsAcceptors, httpsServerConnector.getAcceptors());
            assertEquals(httpsBacklogSize, httpsServerConnector.getAcceptQueueSize());

            // Configuration assertions on HTTP client side
            assertEquals(maxHttpConnectionsPerHost, soapComponentContext.getConnMaxSize());

        } finally {
            componentUnderTest.delete();
        }
    }
}
