/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.as.test.manualmode.messaging.mgmt;
 
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_RUNTIME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.START;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STOP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION;
import static org.jboss.as.messaging.CommonAttributes.CONNECTION_FACTORY;
import static org.jboss.as.messaging.CommonAttributes.HORNETQ_SERVER;
import static org.jboss.as.messaging.CommonAttributes.JMS_BRIDGE;
import static org.jboss.as.messaging.CommonAttributes.JMS_QUEUE;
import static org.jboss.as.messaging.CommonAttributes.SECURITY_ENABLED;
import static org.jboss.as.messaging.CommonAttributes.STARTED;
import static org.jboss.as.messaging.CommonAttributes.SUBSYSTEM;
import static org.jboss.as.messaging.CommonAttributes.CONNECTOR;
import static org.jboss.as.messaging.CommonAttributes.ENTRIES;
import static org.jboss.as.messaging.CommonAttributes.DURABLE;
import static org.jboss.as.messaging.MessagingExtension.SUBSYSTEM_NAME;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.container.test.api.TargetsContainer;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.arquillian.api.ServerSetupTask;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.client.helpers.Operations;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.messaging.jms.ConnectionFactoryAttributes;
import org.jboss.as.messaging.jms.bridge.JMSBridgeDefinition;
import org.jboss.as.test.integration.management.base.AbstractMgmtTestBase;
import org.jboss.as.test.manualmode.web.websocket.CliUtilsForConnectors;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.dmr.ModelNode;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
 * @author baranowb
 *
 */
@RunWith(Arquillian.class)
@RunAsClient
public class JMSBridgeManagementTestCase extends AbstractMgmtTestBase {
 
    private ManagementClient managementClient;
    @ArquillianResource
    private static ContainerController container;
   
    static final PathAddress ADDRESS_HORNETQ = PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME),
            PathElement.pathElement(HORNETQ_SERVER, "default"));
    static final PathAddress ADDRESS_XA_CONNECTION_FACTORY = ADDRESS_HORNETQ.append(PathElement.pathElement(CONNECTION_FACTORY,
            "XAConnectionFactory"));
    static final PathAddress ADDRESS_QUEUE_SOURCE = ADDRESS_HORNETQ.append(PathElement.pathElement(JMS_QUEUE, "sourceQueue"));
    static final PathAddress ADDRESS_QUEUE_TARGET = ADDRESS_HORNETQ.append(PathElement.pathElement(JMS_QUEUE, "targetQueue"));
 
    static final PathAddress ADDRESS_JMS_BRIDGE = PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME),
            PathElement.pathElement(JMS_BRIDGE, "outBridge"));
 
    public static final String CONTAINER = "jbossas-messaging-live";
    public static final String DEPLOYMENT_NAME = "DUMMY";
   
    private List<ServerSetupTask> tasks = new ArrayList<ServerSetupTask>(4);
    private List<ServerSetupTask> tasksToClean = new ArrayList<ServerSetupTask>(4);
   
    public JMSBridgeManagementTestCase(){
        tasks.add(new DisableHornetQSecruity());
        tasks.add(new SetupConnectionFactory());
        tasks.add(new SetupQueuesFactory());
        tasks.add(new SetupJMSBridge());
    }
 
    @Before
    public void setup() throws Exception{
        this.container.start(CONTAINER);
        Assert.assertTrue(this.container.isStarted(CONTAINER));
        this.managementClient = new ManagementClient(TestSuiteEnvironment.getModelControllerClient(), TestSuiteEnvironment.getServerAddress(),
                TestSuiteEnvironment.getServerPort());
        for(ServerSetupTask t:this.tasks){
            this.tasksToClean.add(t);
            t.setup(this.managementClient, CONTAINER);
        }
        reload();
    }
 
    @After
    public void cleanup() throws Exception {
        try {
            for (ServerSetupTask sst : this.tasksToClean) {
                try {
                    sst.tearDown(this.managementClient, CONTAINER);
                } catch (Exception e) {
 
                }
            }
            this.tasksToClean.clear();
            this.reload();
            this.managementClient = null;
        } finally {
            this.container.stop(CONTAINER);
        }
    }
 
    @Deployment(name = DEPLOYMENT_NAME, managed = false)
    @TargetsContainer(CONTAINER)
    public static Archive<?> getDeployment() {
        JavaArchive ja = ShrinkWrap.create(JavaArchive.class, "dummy.jar");
        ja.addPackage(JMSBridgeManagementTestCase.class.getPackage());
        return ja;
    }
   
   
    @Test
    public void testStopStart() throws Exception {
        //actually even checking state seems enough, but lets try a circle.
       
        final ModelControllerClient client = this.managementClient.getControllerClient();
        ModelNode operation = Util.createOperation(READ_ATTRIBUTE_OPERATION, PathAddress.pathAddress(ADDRESS_JMS_BRIDGE));
        operation.get(INCLUDE_RUNTIME).set(true);
        operation.get(NAME).set(STARTED);
        ModelNode result = client.execute(operation);
        Assert.assertTrue("Bridge should start!",result.get(RESULT).asBoolean());
       
        operation = Util.createOperation(STOP, PathAddress.pathAddress(ADDRESS_JMS_BRIDGE));
        result = client.execute(operation);
        operation = Util.createOperation(READ_ATTRIBUTE_OPERATION, PathAddress.pathAddress(ADDRESS_JMS_BRIDGE));
        operation.get(INCLUDE_RUNTIME).set(true);
        operation.get(NAME).set(STARTED);
        result = client.execute(operation);
        Assert.assertFalse("Bridge should stop!",result.get(RESULT).asBoolean());
 
        operation = Util.createOperation(START, PathAddress.pathAddress(ADDRESS_JMS_BRIDGE));
        result = client.execute(operation);
        operation = Util.createOperation(READ_ATTRIBUTE_OPERATION, PathAddress.pathAddress(ADDRESS_JMS_BRIDGE));
        operation.get(INCLUDE_RUNTIME).set(true);
        operation.get(NAME).set(STARTED);
        result = client.execute(operation);
        Assert.assertTrue("Bridge should start!",result.get(RESULT).asBoolean());
    }
 
    @Override
    protected ModelControllerClient getModelControllerClient() {
        return this.managementClient.getControllerClient();
    }

    public void reload() throws Exception {
        CliUtilsForConnectors.reload(managementClient.getControllerClient());
    }
   
   
    static class SetupConnectionFactory implements ServerSetupTask {
 
        @Override
        public void setup(final ManagementClient managementClient, final String containerId) throws Exception {
            final ModelNode operation = Util.createAddOperation(ADDRESS_XA_CONNECTION_FACTORY);
            operation.get(ConnectionFactoryAttributes.Regular.FACTORY_TYPE.getName()).set("XA_GENERIC");
            operation.get(CONNECTOR, "netty").set("netty");
            operation.get(ENTRIES).add("java:jboss/jms/XAConnectionFactory")
                    .add("java:jboss/exported/jms/XAConnectionFactory");
 
            ModelNode result = managementClient.getControllerClient().execute(operation);
            Assert.assertTrue(result.toString(), Operations.isSuccessfulOutcome(result));
 
        }
 
        @Override
        public void tearDown(final ManagementClient managementClient, final String containerId) throws Exception {
            managementClient.getControllerClient().execute(Util.createRemoveOperation(ADDRESS_XA_CONNECTION_FACTORY));
        }
    }
 
    static class SetupQueuesFactory implements ServerSetupTask {
 
        @Override
        public void setup(final ManagementClient managementClient, final String containerId) throws Exception {
            ModelNode result = setUpQueue(managementClient.getControllerClient(), "sourceQueue", ADDRESS_QUEUE_SOURCE);
            Assert.assertTrue(result.toString(), Operations.isSuccessfulOutcome(result));
            result = setUpQueue(managementClient.getControllerClient(), "targetQueue", ADDRESS_QUEUE_TARGET);
            Assert.assertTrue(result.toString(), Operations.isSuccessfulOutcome(result));
        }
 
        @Override
        public void tearDown(final ManagementClient managementClient, final String containerId) throws Exception {
            managementClient.getControllerClient().execute(Util.createRemoveOperation(ADDRESS_QUEUE_SOURCE));
            managementClient.getControllerClient().execute(Util.createRemoveOperation(ADDRESS_QUEUE_TARGET));
        }
 
        private ModelNode setUpQueue(final ModelControllerClient client, final String name, final PathAddress address)
                throws IOException {
            final ModelNode operation = Util.createAddOperation(address);
            operation.get(ENTRIES).add("java:jboss/jms/queue/" + name).add("java:jboss/exported/jms/queue/" + name);
            operation.get(DURABLE.getName()).set(true);
            return client.execute(operation);
 
        }
    }
 
    static class DisableHornetQSecruity implements ServerSetupTask {
        // read/store and in case 'true', change it back to it, since this test does not need security
        private boolean oldValue = true;
 
        @Override
        public void setup(ManagementClient managementClient, String containerId) throws Exception {
            final ModelControllerClient client = managementClient.getControllerClient();
            ModelNode operation = Util.createOperation(READ_ATTRIBUTE_OPERATION, PathAddress.pathAddress(ADDRESS_HORNETQ));
            operation.get(NAME).set(SECURITY_ENABLED.getName());
            ModelNode result = client.execute(operation);
           
            this.oldValue = result.get(RESULT).asBoolean();
            if (this.oldValue) {
                operation = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, PathAddress.pathAddress(ADDRESS_HORNETQ));
                operation.get(NAME).set(SECURITY_ENABLED.getName());
                operation.get(VALUE).set(false);
                result = client.execute(operation);
                Assert.assertTrue("Failed to disable security in HornetQ", Operations.isSuccessfulOutcome(result));
            }
        }
 
        @Override
        public void tearDown(ManagementClient managementClient, String containerId) throws Exception {
            if (this.oldValue) {
                ModelNode operation = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, PathAddress.pathAddress(ADDRESS_HORNETQ));
                operation.get(NAME).set(SECURITY_ENABLED.getName());
                operation.get(VALUE).set(true);
                ModelNode result = managementClient.getControllerClient().execute(operation);
                Assert.assertTrue("Failed to enable security in HornetQ", Operations.isSuccessfulOutcome(result));
            }
        }
 
    }
 
    static class SetupJMSBridge implements ServerSetupTask {
 
        @Override
        public void setup(final ManagementClient managementClient, final String containerId) throws Exception {
            final ModelNode operation = Util.createAddOperation(ADDRESS_JMS_BRIDGE);
 
            operation.get(JMSBridgeDefinition.SOURCE_CONNECTION_FACTORY.getName()).set("java:jboss/jms/XAConnectionFactory");
            operation.get(JMSBridgeDefinition.SOURCE_DESTINATION.getName()).set("java:jboss/jms/queue/sourceQueue");
 
            operation.get(JMSBridgeDefinition.TARGET_CONNECTION_FACTORY.getName()).set("jms/XAConnectionFactory");
            operation.get(JMSBridgeDefinition.TARGET_DESTINATION.getName()).set("/jms/queue/targetQueue");
            operation.get(JMSBridgeDefinition.TARGET_CONTEXT.getName(), "java.naming.factory.initial").set(
                    "org.jboss.naming.remote.client.InitialContextFactory");
            operation.get(JMSBridgeDefinition.TARGET_CONTEXT.getName(), "java.naming.provider.url").
                    set("remote://"+TestSuiteEnvironment.getServerAddress()+":4447");
 
            // other conf opts if need be
            operation.get(JMSBridgeDefinition.QUALITY_OF_SERVICE.getName()).set("ONCE_AND_ONLY_ONCE");
            operation.get(JMSBridgeDefinition.FAILURE_RETRY_INTERVAL.getName()).set(60);
            operation.get(JMSBridgeDefinition.MAX_RETRIES.getName()).set("-1");
            operation.get(JMSBridgeDefinition.MAX_BATCH_SIZE.getName()).set("10");
            operation.get(JMSBridgeDefinition.MAX_BATCH_TIME.getName()).set("500");
            operation.get(JMSBridgeDefinition.ADD_MESSAGE_ID_IN_HEADER.getName()).set("true");
 
            final ModelNode result = managementClient.getControllerClient().execute(operation);
            Assert.assertTrue(result.toString(), Operations.isSuccessfulOutcome(result));
        }
 
        @Override
        public void tearDown(final ManagementClient managementClient, final String containerId) throws Exception {
            managementClient.getControllerClient().execute(Util.createRemoveOperation(ADDRESS_JMS_BRIDGE));
        }
 
    }
 
}