/**
 * 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.camel.management;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.openmbean.TabularData;

import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.util.StringHelper;
import org.junit.Ignore;

/**
 * @version 
 */
public class ManagedCamelContextTest extends ManagementTestSupport {

    @Override
    protected CamelContext createCamelContext() throws Exception {
        CamelContext context = super.createCamelContext();
        // to force a different management name than the camel id
        context.getManagementNameStrategy().setNamePattern("19-#name#");
        return context;
    }

    public void testManagedCamelContext() throws Exception {
        // JMX tests dont work well on AIX CI servers (hangs them)
        if (isPlatform("aix")) {
            return;
        }

        MBeanServer mbeanServer = getMBeanServer();

        ObjectName on = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=context,name=\"camel-1\"");

        assertTrue("Should be registered", mbeanServer.isRegistered(on));
        String name = (String) mbeanServer.getAttribute(on, "CamelId");
        assertEquals("camel-1", name);

        String managementName = (String) mbeanServer.getAttribute(on, "ManagementName");
        assertEquals("19-camel-1", managementName);

        String uptime = (String) mbeanServer.getAttribute(on, "Uptime");
        assertNotNull(uptime);

        String status = (String) mbeanServer.getAttribute(on, "State");
        assertEquals("Started", status);

        Boolean messageHistory = (Boolean) mbeanServer.getAttribute(on, "MessageHistory");
        assertEquals(Boolean.TRUE, messageHistory);

        Integer total = (Integer) mbeanServer.getAttribute(on, "TotalRoutes");
        assertEquals(2, total.intValue());

        Integer started = (Integer) mbeanServer.getAttribute(on, "StartedRoutes");
        assertEquals(2, started.intValue());

        // invoke operations
        MockEndpoint mock = getMockEndpoint("mock:result");
        mock.expectedBodiesReceived("Hello World");
        mbeanServer.invoke(on, "sendBody", new Object[]{"direct:start", "Hello World"}, new String[]{"java.lang.String", "java.lang.Object"});
        assertMockEndpointsSatisfied();

        resetMocks();
        mock.expectedBodiesReceived("Hello World");
        mbeanServer.invoke(on, "sendStringBody", new Object[]{"direct:start", "Hello World"}, new String[]{"java.lang.String", "java.lang.String"});
        assertMockEndpointsSatisfied();

        Object reply = mbeanServer.invoke(on, "requestBody", new Object[]{"direct:foo", "Hello World"}, new String[]{"java.lang.String", "java.lang.Object"});
        assertEquals("Bye World", reply);

        reply = mbeanServer.invoke(on, "requestStringBody", new Object[]{"direct:foo", "Hello World"}, new String[]{"java.lang.String", "java.lang.String"});
        assertEquals("Bye World", reply);

        resetMocks();
        mock = getMockEndpoint("mock:result");
        mock.expectedBodiesReceived("Hello World");
        mock.expectedHeaderReceived("foo", 123);
        Map<String, Object> headers = new HashMap<String, Object>();
        headers.put("foo", 123);
        mbeanServer.invoke(on, "sendBodyAndHeaders", new Object[]{"direct:start", "Hello World", headers}, new String[]{"java.lang.String", "java.lang.Object", "java.util.Map"});
        assertMockEndpointsSatisfied();

        resetMocks();
        mock = getMockEndpoint("mock:result");
        mock.expectedBodiesReceived("Hello World");
        mock.expectedHeaderReceived("foo", 123);
        reply = mbeanServer.invoke(on, "requestBodyAndHeaders", new Object[]{"direct:start", "Hello World", headers}, new String[]{"java.lang.String", "java.lang.Object", "java.util.Map"});
        assertEquals("Hello World", reply);
        assertMockEndpointsSatisfied();

        // test can send
        Boolean can = (Boolean) mbeanServer.invoke(on, "canSendToEndpoint", new Object[]{"direct:start"}, new String[]{"java.lang.String"});
        assertEquals(true, can.booleanValue());
        can = (Boolean) mbeanServer.invoke(on, "canSendToEndpoint", new Object[]{"timer:foo"}, new String[]{"java.lang.String"});
        assertEquals(false, can.booleanValue());

        // stop Camel
        mbeanServer.invoke(on, "stop", null, null);
    }

    public void testManagedCamelContextCreateEndpoint() throws Exception {
        // JMX tests dont work well on AIX CI servers (hangs them)
        if (isPlatform("aix")) {
            return;
        }

        MBeanServer mbeanServer = getMBeanServer();

        ObjectName on = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=context,name=\"camel-1\"");

        assertNull(context.hasEndpoint("seda:bar"));

        // create a new endpoint
        Object reply = mbeanServer.invoke(on, "createEndpoint", new Object[]{"seda:bar"}, new String[]{"java.lang.String"});
        assertEquals(Boolean.TRUE, reply);

        assertNotNull(context.hasEndpoint("seda:bar"));

        ObjectName seda = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=endpoints,name=\"seda://bar\"");
        boolean registered = mbeanServer.isRegistered(seda);
        assertTrue("Should be registered " + seda, registered);

        // create it again
        reply = mbeanServer.invoke(on, "createEndpoint", new Object[]{"seda:bar"}, new String[]{"java.lang.String"});
        assertEquals(Boolean.FALSE, reply);

        registered = mbeanServer.isRegistered(seda);
        assertTrue("Should be registered " + seda, registered);
    }

    public void testManagedCamelContextRemoveEndpoint() throws Exception {
        // JMX tests dont work well on AIX CI servers (hangs them)
        if (isPlatform("aix")) {
            return;
        }

        MBeanServer mbeanServer = getMBeanServer();

        ObjectName on = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=context,name=\"camel-1\"");

        assertNull(context.hasEndpoint("seda:bar"));

        // create a new endpoint
        Object reply = mbeanServer.invoke(on, "createEndpoint", new Object[]{"seda:bar"}, new String[]{"java.lang.String"});
        assertEquals(Boolean.TRUE, reply);

        assertNotNull(context.hasEndpoint("seda:bar"));

        ObjectName seda = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=endpoints,name=\"seda://bar\"");
        boolean registered = mbeanServer.isRegistered(seda);
        assertTrue("Should be registered " + seda, registered);

        // remove it
        Object num = mbeanServer.invoke(on, "removeEndpoints", new Object[]{"seda:*"}, new String[]{"java.lang.String"});
        assertEquals(1, num);

        assertNull(context.hasEndpoint("seda:bar"));
        registered = mbeanServer.isRegistered(seda);
        assertFalse("Should not be registered " + seda, registered);

        // remove it again
        num = mbeanServer.invoke(on, "removeEndpoints", new Object[]{"seda:*"}, new String[]{"java.lang.String"});
        assertEquals(0, num);

        assertNull(context.hasEndpoint("seda:bar"));
        registered = mbeanServer.isRegistered(seda);
        assertFalse("Should not be registered " + seda, registered);
    }

    public void testFindComponentsInClasspath() throws Exception {
        // JMX tests dont work well on AIX CI servers (hangs them)
        if (isPlatform("aix")) {
            return;
        }

        MBeanServer mbeanServer = getMBeanServer();

        ObjectName on = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=context,name=\"camel-1\"");

        assertTrue("Should be registered", mbeanServer.isRegistered(on));

        @SuppressWarnings("unchecked")
        Map<String, Properties> info = (Map<String, Properties>) mbeanServer.invoke(on, "findComponents", null, null);
        assertNotNull(info);

        assertTrue(info.size() > 20);
        Properties prop = info.get("seda");
        assertNotNull(prop);
        assertEquals("seda", prop.get("name"));
        assertEquals("org.apache.camel", prop.get("groupId"));
        assertEquals("camel-core", prop.get("artifactId"));
    }

    public void testManagedCamelContextCreateRouteStaticEndpointJson() throws Exception {
        // JMX tests dont work well on AIX CI servers (hangs them)
        if (isPlatform("aix")) {
            return;
        }

        MBeanServer mbeanServer = getMBeanServer();
        ObjectName on = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=context,name=\"camel-1\"");

        // get the json
        String json = (String) mbeanServer.invoke(on, "createRouteStaticEndpointJson", null, null);
        assertNotNull(json);
        assertEquals(7, StringHelper.countChar(json, '{'));
        assertEquals(7, StringHelper.countChar(json, '}'));
        assertTrue(json.contains("{ \"uri\": \"direct://start\" }"));
        assertTrue(json.contains("{ \"uri\": \"direct://foo\" }"));
    }

    public void testManagedCamelContextExplainEndpointUri() throws Exception {
        // JMX tests dont work well on AIX CI servers (hangs them)
        if (isPlatform("aix")) {
            return;
        }

        MBeanServer mbeanServer = getMBeanServer();
        ObjectName on = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=context,name=\"camel-1\"");

        // get the json
        String json = (String) mbeanServer.invoke(on, "explainEndpointJson", new Object[]{"log:foo?groupDelay=2000&groupSize=5", false},
                new String[]{"java.lang.String", "boolean"});
        assertNotNull(json);

        assertEquals(5, StringHelper.countChar(json, '{'));
        assertEquals(5, StringHelper.countChar(json, '}'));
        assertTrue(json.contains("\"groupDelay\": { \"kind\": \"parameter\", \"type\": \"integer\", \"javaType\": \"java.lang.Long\", \"deprecated\": \"false\", \"value\": \"2000\","
                + " \"description\": \"Set the initial delay for stats (in millis)\" },"));
        assertTrue(json.contains("\"groupSize\": { \"kind\": \"parameter\", \"type\": \"integer\", \"javaType\": \"java.lang.Integer\", \"deprecated\": \"false\", \"value\": \"5\","
                + " \"description\": \"An integer that specifies a group size for throughput logging.\" }"));
        assertTrue(json.contains("\"loggerName\": { \"kind\": \"path\", \"type\": \"string\", \"javaType\": \"java.lang.String\", \"deprecated\": \"false\","
                + " \"value\": \"foo\", \"description\": \"The logger name to use\" }"));
    }

    public void testManagedCamelContextExplainEip() throws Exception {
        // JMX tests dont work well on AIX CI servers (hangs them)
        if (isPlatform("aix")) {
            return;
        }

        MBeanServer mbeanServer = getMBeanServer();
        ObjectName on = ObjectName.getInstance("org.apache.camel:context=19-camel-1,type=context,name=\"camel-1\"");

        // get the json
        String json = (String) mbeanServer.invoke(on, "explainEipJson", new Object[]{"transform", false}, new String[]{"java.lang.String", "boolean"});
        assertNotNull(json);

        assertTrue(json.contains("\"label\": \"transformation\""));
        assertTrue(json.contains("\"expression\": { \"kind\": \"element\", \"required\": \"true\", \"type\": \"object\""));
    }

    @Override
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start").to("mock:result");

                from("direct:foo").transform(constant("Bye World"));
            }
        };
    }

}
