IGNITE-12628 Add tests for jmx metrics return types - Fixes #7369.

Signed-off-by: Ivan Rakov <irakov@apache.org>
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridJobStealingSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridJobStealingSelfTest.java
index 1d01282..26dcfa6 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/GridJobStealingSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/GridJobStealingSelfTest.java
@@ -51,6 +51,7 @@
 import org.apache.ignite.testframework.junits.common.GridCommonTest;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -301,6 +302,19 @@
             ret.get(ignite3.cluster().localNode().id());
     }
 
+    /**
+     * @throws Exception If fatiled.
+     */
+    @Ignore("https://issues.apache.org/jira/browse/IGNITE-12629")
+    @Test
+    public void testJobStealingMbeanValidity() throws Exception {
+        String[] beansToValidate = new String[] {
+            "org.apache.ignite.spi.collision.jobstealing.JobStealingCollisionSpi$JobStealingCollisionSpiMBeanImpl",
+            "org.apache.ignite.spi.failover.jobstealing.JobStealingFailoverSpi$JobStealingFailoverSpiMBeanImpl"};
+
+        validateMbeans(ignite1, beansToValidate);
+    }
+
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
         IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridMBeansTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridMBeansTest.java
index db82643..5bbbbe3 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/GridMBeansTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/GridMBeansTest.java
@@ -17,12 +17,12 @@
 
 package org.apache.ignite.internal;
 
+import javax.management.ObjectName;
 import org.apache.ignite.configuration.ExecutorConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
-
-import javax.management.ObjectName;
 import org.junit.Test;
 
 /**
@@ -86,4 +86,34 @@
 
         assertEquals(expAttributeVal, attributeVal);
     }
+
+    /**
+     * @throws Exception If failed to validate methods.
+     */
+    @Test
+    public void testBeansClasses() throws Exception {
+        String[] clsNames = new String[]{"org.apache.ignite.internal.ClusterLocalNodeMetricsMXBeanImpl",
+            "org.apache.ignite.internal.ClusterMetricsMXBeanImpl",
+            "org.apache.ignite.internal.IgniteKernal",
+            "org.apache.ignite.internal.IgnitionMXBeanAdapter",
+            "org.apache.ignite.internal.StripedExecutorMXBeanAdapter",
+            "org.apache.ignite.internal.ThreadPoolMXBeanAdapter",
+            "org.apache.ignite.internal.TransactionMetricsMxBeanImpl",
+            "org.apache.ignite.internal.TransactionsMXBeanImpl",
+            "org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsMXBeanImpl",
+            "org.apache.ignite.internal.processors.cache.persistence.DataStorageMXBeanImpl",
+            "org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.PageLockTrackerMXBeanImpl",
+            "org.apache.ignite.internal.processors.cluster.BaselineAutoAdjustMXBeanImpl",
+            "org.apache.ignite.internal.processors.odbc.ClientListenerProcessor$ClientProcessorMXBeanImpl",
+            "org.apache.ignite.internal.worker.FailureHandlingMxBeanImpl",
+            "org.apache.ignite.spi.checkpoint.sharedfs.SharedFsCheckpointSpi$SharedFsCheckpointSpiMBeanImpl",
+            "org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$TcpCommunicationSpiMBeanImpl",
+            "org.apache.ignite.spi.deployment.local.LocalDeploymentSpi$LocalDeploymentSpiMBeanImpl",
+            "org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi$TcpDiscoverySpiMBeanImpl",
+            "org.apache.ignite.spi.eventstorage.memory.MemoryEventStorageSpi$MemoryEventStorageSpiMBeanImpl",
+            "org.apache.ignite.spi.failover.always.AlwaysFailoverSpi$AlwaysFailoverSpiMBeanImpl",
+            "org.apache.ignite.spi.loadbalancing.roundrobin.RoundRobinLoadBalancingSpi$RoundRobinLoadBalancingSpiMBeanImpl"};
+
+        validateMbeans(G.allGrids().get(0), clsNames);
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridMbeansMiscTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridMbeansMiscTest.java
new file mode 100644
index 0000000..d4b20c7
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/GridMbeansMiscTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.ignite.internal;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.checkpoint.CheckpointSpi;
+import org.apache.ignite.spi.checkpoint.sharedfs.SharedFsCheckpointSpi;
+import org.apache.ignite.spi.collision.CollisionSpi;
+import org.apache.ignite.spi.collision.fifoqueue.FifoQueueCollisionSpi;
+import org.apache.ignite.spi.collision.priorityqueue.PriorityQueueCollisionSpi;
+import org.apache.ignite.spi.deployment.DeploymentSpi;
+import org.apache.ignite.spi.deployment.local.LocalDeploymentSpi;
+import org.apache.ignite.spi.loadbalancing.LoadBalancingSpi;
+import org.apache.ignite.spi.loadbalancing.adaptive.AdaptiveLoadBalancingSpi;
+import org.apache.ignite.spi.loadbalancing.weightedrandom.WeightedRandomLoadBalancingSpi;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Checks mbeans validity for miscelenious spis.
+ */
+public class GridMbeansMiscTest extends GridCommonAbstractTest {
+    /** */
+    private CollisionSpi collisionSpi;
+
+    /** */
+    private LoadBalancingSpi loadBalancingSpi;
+
+    /** */
+    private DeploymentSpi deploymentSpi;
+
+    /** */
+    private CheckpointSpi checkpointSpi;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg =  super.getConfiguration(igniteInstanceName);
+
+        if (collisionSpi != null)
+            cfg.setCollisionSpi(collisionSpi);
+
+        if (loadBalancingSpi != null)
+            cfg.setLoadBalancingSpi(loadBalancingSpi);
+
+        if (deploymentSpi != null)
+            cfg.setDeploymentSpi(deploymentSpi);
+
+        if (checkpointSpi != null)
+            cfg.setCheckpointSpi(checkpointSpi);
+
+        return cfg;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testMbeansSet1() throws Exception {
+        collisionSpi = new FifoQueueCollisionSpi();
+        loadBalancingSpi = new WeightedRandomLoadBalancingSpi();
+        deploymentSpi = new LocalDeploymentSpi();
+        checkpointSpi = new SharedFsCheckpointSpi();
+
+        String[] beansToValidate = new String[] {
+            "org.apache.ignite.spi.collision.fifoqueue.FifoQueueCollisionSpi$FifoQueueCollisionSpiMBeanImpl",
+            "org.apache.ignite.spi.loadbalancing.weightedrandom.WeightedRandomLoadBalancingSpi$WeightedRandomLoadBalancingSpiMBeanImpl",
+            "org.apache.ignite.spi.deployment.local.LocalDeploymentSpi$LocalDeploymentSpiMBeanImpl",
+            "org.apache.ignite.spi.checkpoint.sharedfs.SharedFsCheckpointSpi$SharedFsCheckpointSpiMBeanImpl"
+        };
+
+        doTest(beansToValidate);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testMbeansSet2() throws Exception {
+        collisionSpi = new PriorityQueueCollisionSpi();
+        loadBalancingSpi = new AdaptiveLoadBalancingSpi();
+
+        String[] beansToValidate = new String[] {
+            "org.apache.ignite.spi.collision.priorityqueue.PriorityQueueCollisionSpi$PriorityQueueCollisionSpiMBeanImpl",
+            "org.apache.ignite.spi.loadbalancing.adaptive.AdaptiveLoadBalancingSpi$AdaptiveLoadBalancingSpiMBeanImpl"
+        };
+
+        doTest(beansToValidate);
+    }
+
+    /** */
+    private void doTest(String[] beansToValidate) throws Exception {
+        try {
+            Ignite ignite = startGrid();
+
+            validateMbeans(ignite, beansToValidate);
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/checkpoint/GridCheckpointTaskSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/checkpoint/GridCheckpointTaskSelfTest.java
index a9db0ae..2fe7683 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/managers/checkpoint/GridCheckpointTaskSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/checkpoint/GridCheckpointTaskSelfTest.java
@@ -119,6 +119,14 @@
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testCacheCheckpointSpiMbeanValidity() throws Exception {
+        validateMbeans(grid(1), "org.apache.ignite.spi.checkpoint.cache.CacheCheckpointSpi$CacheCheckpointSpiMBeanImpl");
+    }
+
+    /**
      * Failover test task.
      */
     @ComputeTaskSessionFullSupport
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/EvictionAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/EvictionAbstractTest.java
index bcc0799..6191f4d 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/EvictionAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/EvictionAbstractTest.java
@@ -736,6 +736,24 @@
     }
 
     /**
+     * @throws Exception if failed.
+     */
+    @Test
+    public void testEvictionPolicyMbeanValidity() throws Exception {
+        try {
+            Ignite ignite = startGrids(2);
+
+            //Instantiate policy object to know exact class.
+            EvictionPolicy plc = createPolicy(0);
+
+            validateMbeans(ignite, plc.getClass().getName());
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
      * @throws Exception If failed.
      */
     protected void checkPartitioned() throws Exception {
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
index 593d1c3..d86cfd4 100755
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.testframework.junits.common;
 
 import java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -41,6 +42,7 @@
 import javax.cache.integration.CompletionListener;
 import javax.management.MBeanServer;
 import javax.management.MBeanServerInvocationHandler;
+import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
@@ -118,6 +120,7 @@
 import org.apache.ignite.lang.IgniteInClosure;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteRunnable;
+import org.apache.ignite.mxbean.MXBeanDescription;
 import org.apache.ignite.resources.IgniteInstanceResource;
 import org.apache.ignite.resources.LoggerResource;
 import org.apache.ignite.testframework.GridTestNode;
@@ -131,6 +134,7 @@
 import org.jetbrains.annotations.Nullable;
 
 import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_EVENT_DRIVEN_SERVICE_PROCESSOR_ENABLED;
 import static org.apache.ignite.IgniteSystemProperties.getBoolean;
 import static org.apache.ignite.cache.CacheMode.LOCAL;
@@ -2375,4 +2379,137 @@
 
         return MBeanServerInvocationHandler.newProxyInstance(mbeanSrv, mbeanName, cls, true);
     }
+
+    /**
+     * Checks that return types of all registered ignite metrics methods are correct.
+     * Also checks that all classes from {@code namesToCheck} are registered as mbeans.
+     *
+     * @param ignite Ignite instance to collect metrics from.
+     * @param namesToCheck Mbean classes names that must be registered in {@code MBeanServer}.
+     * @throws Exception If failed to obtain mbeans.
+     */
+    protected void validateMbeans(Ignite ignite, String... namesToCheck) throws Exception {
+        logMbeansValidation(getNotRegisteredMbeans(ignite, namesToCheck), "Not registered mbeans");
+        logMbeansValidation(getInvalidMbeansMethods(ignite), "Invalid metrics methods");
+    }
+
+    /**
+     * @param ignite Ignite instance to collect metrics from.
+     * @param namesToCheck Mbean classes names that must be registered in {@code MBeanServer}.
+     * @return {@code Set} of class names that are contained in {@code namesToCheck}
+     *          but not registered in {@code MBeanServer}.
+     */
+    protected Set<String> getNotRegisteredMbeans(Ignite ignite, String... namesToCheck) {
+        MBeanServer srv = ignite.configuration().getMBeanServer();
+
+        Set<String> beancClsNames = srv.queryMBeans(null, null).stream()
+            .map(ObjectInstance::getClassName)
+            .collect(toSet());
+
+        return Arrays.stream(namesToCheck)
+            .filter(nameToCheck -> beancClsNames.stream().noneMatch(clsName -> clsName.contains(nameToCheck)))
+            .collect(toSet());
+    }
+
+    /**
+     * @param ignite Ignite instance to collect metrics from.
+     * @return {@code Set} of metrics methods that have forbidden return types.
+     * @throws Exception If failed to obtain metrics.
+     */
+    protected Set<String> getInvalidMbeansMethods(Ignite ignite) throws Exception {
+        Set<String> sysMetricsPackages = new HashSet<>();
+        sysMetricsPackages.add("sun.management");
+        sysMetricsPackages.add("javax.management");
+
+        MBeanServer srv = ignite.configuration().getMBeanServer();
+
+        Set<String> invalidMethods = new HashSet<>();
+
+        final Set<ObjectInstance> instances = srv.queryMBeans(null, null);
+
+        for (ObjectInstance instance: instances) {
+            final String clsName = instance.getClassName();
+
+            if (sysMetricsPackages.stream().anyMatch(clsName::startsWith))
+                continue;
+
+            Class c;
+
+            try {
+                c = Class.forName(clsName);
+            }
+            catch (ClassNotFoundException e) {
+                log.warning("Failed to load class: " + clsName);
+
+                continue;
+            }
+
+            for (Class interf : c.getInterfaces()) {
+                for (Method m : interf.getMethods()) {
+                    if (!m.isAnnotationPresent(MXBeanDescription.class))
+                        continue;
+
+                    if (!validateMetricsMethod(m))
+                        invalidMethods.add(m.toString());
+                }
+            }
+        }
+
+        return invalidMethods;
+    }
+
+    /**  */
+    private void logMbeansValidation(Set<String> invalidSet, String errorMsgPrefix) {
+        if (!invalidSet.isEmpty()) {
+            log.info("****************************************");
+            log.info(errorMsgPrefix + ":");
+
+            invalidSet.stream()
+                .sorted()
+                .forEach(log::info);
+
+            log.info("****************************************");
+
+            fail(errorMsgPrefix + " detected^");
+        }
+    }
+
+    /**
+     * Validates return type for metrics method.
+     * Validity rules are not carved in stone and can be changed in future.
+     * See https://issues.apache.org/jira/browse/IGNITE-12629.
+     *
+     * @param m Metric method to check.
+     * @return {@code True} if method return type is allowed.
+     */
+    private boolean validateMetricsMethod(Method m) {
+        Set<String> primitives = new HashSet<>();
+        primitives.add("char");
+        primitives.add("short");
+        primitives.add("int");
+        primitives.add("long");
+        primitives.add("double");
+        primitives.add("float");
+        primitives.add("byte");
+        primitives.add("boolean");
+        primitives.add("void");
+
+        Set<String> allowedPackages = new HashSet<>();
+        allowedPackages.add("java.lang");
+        allowedPackages.add("java.util");
+
+        final String returnTypeName = m.getGenericReturnType().getTypeName();
+
+        if (primitives.stream().anyMatch(type -> type.equals(returnTypeName) || (type + "[]").equals(returnTypeName)))
+            return true;
+
+        String[] parts = returnTypeName.split("[<>,]");
+
+        for (String part: parts) {
+            if (allowedPackages.stream().noneMatch(pack -> part.trim().startsWith(pack)))
+                return false;
+        }
+
+        return true;
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
index aeead5f..711048e 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
@@ -33,6 +33,7 @@
 import org.apache.ignite.internal.GridLifecycleAwareSelfTest;
 import org.apache.ignite.internal.GridLifecycleBeanSelfTest;
 import org.apache.ignite.internal.GridMBeansTest;
+import org.apache.ignite.internal.GridMbeansMiscTest;
 import org.apache.ignite.internal.GridNodeMetricsLogSelfTest;
 import org.apache.ignite.internal.GridPeerDeploymentRetryModifiedTest;
 import org.apache.ignite.internal.GridPeerDeploymentRetryTest;
@@ -183,6 +184,7 @@
     GridNodeMetricsLogSelfTest.class,
     GridLocalIgniteSerializationTest.class,
     GridMBeansTest.class,
+    GridMbeansMiscTest.class,
     TransactionsMXBeanImplTest.class,
     SetTxTimeoutOnPartitionMapExchangeTest.class,
     DiscoveryDataDeserializationFailureHanderTest.class,
diff --git a/modules/zookeeper/src/test/java/org/apache/ignite/spi/discovery/zk/internal/ZookeeperDiscoveryMiscTest.java b/modules/zookeeper/src/test/java/org/apache/ignite/spi/discovery/zk/internal/ZookeeperDiscoveryMiscTest.java
index f51da78..18493c9 100644
--- a/modules/zookeeper/src/test/java/org/apache/ignite/spi/discovery/zk/internal/ZookeeperDiscoveryMiscTest.java
+++ b/modules/zookeeper/src/test/java/org/apache/ignite/spi/discovery/zk/internal/ZookeeperDiscoveryMiscTest.java
@@ -462,6 +462,21 @@
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testZkMbeansValidity() throws Exception {
+        try {
+            Ignite ignite = startGrid();
+
+            validateMbeans(ignite, "org.apache.ignite.spi.discovery.zk.ZookeeperDiscoverySpi$ZookeeperDiscoverySpiMBeanImpl");
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
      *
      */
     private static class ValidationTestAffinity extends RendezvousAffinityFunction {