diff --git a/hazelcast-spring/src/main/java/com/hazelcast/spring/AbstractHazelcastBeanDefinitionParser.java b/hazelcast-spring/src/main/java/com/hazelcast/spring/AbstractHazelcastBeanDefinitionParser.java index 3c909e2fbeda..371a24f0fb01 100644 --- a/hazelcast-spring/src/main/java/com/hazelcast/spring/AbstractHazelcastBeanDefinitionParser.java +++ b/hazelcast-spring/src/main/java/com/hazelcast/spring/AbstractHazelcastBeanDefinitionParser.java @@ -131,6 +131,13 @@ protected BeanDefinitionBuilder createBeanBuilder(Class clazz) { return builder; } + protected BeanDefinitionBuilder createBeanBuilder(String className) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(className); + builder.setScope(configBuilder.getBeanDefinition().getScope()); + builder.setLazyInit(configBuilder.getBeanDefinition().isLazyInit()); + return builder; + } + protected BeanDefinitionBuilder createAndFillBeanBuilder(Node node, Class clazz, String propertyName, BeanDefinitionBuilder parent, String... exceptPropertyNames) { BeanDefinitionBuilder builder = createBeanBuilder(clazz); diff --git a/hazelcast-spring/src/main/java/com/hazelcast/spring/HazelcastClientBeanDefinitionParser.java b/hazelcast-spring/src/main/java/com/hazelcast/spring/HazelcastClientBeanDefinitionParser.java index faf6cb7daa65..abac4fd26022 100644 --- a/hazelcast-spring/src/main/java/com/hazelcast/spring/HazelcastClientBeanDefinitionParser.java +++ b/hazelcast-spring/src/main/java/com/hazelcast/spring/HazelcastClientBeanDefinitionParser.java @@ -348,6 +348,20 @@ private void handleLoadBalancer(Node node) { configBuilder.addPropertyValue("loadBalancer", new RandomLB()); } else if ("round-robin".equals(type)) { configBuilder.addPropertyValue("loadBalancer", new RoundRobinLB()); + } else if ("custom".equals(type)) { + NamedNodeMap attributes = node.getAttributes(); + Node classNameNode = attributes.getNamedItem("class-name"); + String className = classNameNode != null ? getTextContent(classNameNode) : null; + Node implNode = attributes.getNamedItem("implementation"); + String implementation = implNode != null ? getTextContent(implNode) : null; + isTrue(className != null ^ implementation != null, "Exactly one of 'class-name' or 'implementation'" + + " attributes is required to create LoadBalancer!"); + if (className != null) { + BeanDefinitionBuilder loadBalancerBeanDefinition = createBeanBuilder(className); + configBuilder.addPropertyValue("loadBalancer", loadBalancerBeanDefinition.getBeanDefinition()); + } else { + configBuilder.addPropertyReference("loadBalancer", implementation); + } } } diff --git a/hazelcast-spring/src/main/resources/hazelcast-spring-4.0.xsd b/hazelcast-spring/src/main/resources/hazelcast-spring-4.0.xsd index 46ac23ad4bcf..8b94fdd88c5c 100644 --- a/hazelcast-spring/src/main/resources/hazelcast-spring-4.0.xsd +++ b/hazelcast-spring/src/main/resources/hazelcast-spring-4.0.xsd @@ -3090,9 +3090,11 @@ + + diff --git a/hazelcast-spring/src/test/java/com/hazelcast/spring/InvalidApplicationContextTest.java b/hazelcast-spring/src/test/java/com/hazelcast/spring/InvalidApplicationContextTest.java new file mode 100644 index 000000000000..eb09b9b5b612 --- /dev/null +++ b/hazelcast-spring/src/test/java/com/hazelcast/spring/InvalidApplicationContextTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved. + * + * Licensed 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 com.hazelcast.spring; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class InvalidApplicationContextTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void shouldFailWhenLoadBalancerContainsClassNameAndImplementationAttribute() { + expectedException.expect(BeanDefinitionStoreException.class); + expectedException.expectMessage("Unexpected exception parsing XML document from class path resource [com/hazelcast/spring/" + + "customLoadBalancer-invalidApplicationContext.xml]; nested exception is " + + "java.lang.IllegalArgumentException: Exactly one of 'class-name' or 'implementation' attributes is " + + "required to create LoadBalancer!"); + + new ClassPathXmlApplicationContext("com\\hazelcast\\spring\\customLoadBalancer-invalidApplicationContext.xml"); + } +} diff --git a/hazelcast-spring/src/test/java/com/hazelcast/spring/TestCustomLoadBalancerContext.java b/hazelcast-spring/src/test/java/com/hazelcast/spring/TestCustomLoadBalancerContext.java new file mode 100644 index 000000000000..09aadc357829 --- /dev/null +++ b/hazelcast-spring/src/test/java/com/hazelcast/spring/TestCustomLoadBalancerContext.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved. + * + * Licensed 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 com.hazelcast.spring; + +import com.hazelcast.client.HazelcastClient; +import com.hazelcast.client.LoadBalancer; +import com.hazelcast.client.config.ClientConfig; +import com.hazelcast.client.impl.clientside.HazelcastClientProxy; +import com.hazelcast.client.test.CustomLoadBalancer; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.test.annotation.QuickTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; + +import javax.annotation.Resource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(CustomSpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = {"customLoadBalancer-applicationContext.xml"}) +@Category(QuickTest.class) +public class TestCustomLoadBalancerContext { + + @Resource(name = "client1") + private HazelcastClientProxy client1; + + @Resource(name = "client2") + private HazelcastClientProxy client2; + + @BeforeClass + @AfterClass + public static void start() { + HazelcastClient.shutdownAll(); + Hazelcast.shutdownAll(); + } + + @Test + public void testCustomLoadBalancer() { + ClientConfig config1 = client1.getClientConfig(); + LoadBalancer loadBalancer1 = config1.getLoadBalancer(); + assertTrue(loadBalancer1 instanceof CustomLoadBalancer); + assertEquals("default-name", ((CustomLoadBalancer) loadBalancer1).getName()); + + ClientConfig config2 = client2.getClientConfig(); + LoadBalancer loadBalancer2 = config2.getLoadBalancer(); + assertTrue(loadBalancer2 instanceof CustomLoadBalancer); + assertEquals("custom-balancer-name", ((CustomLoadBalancer) loadBalancer2).getName()); + } +} diff --git a/hazelcast-spring/src/test/resources/com/hazelcast/spring/customLoadBalancer-applicationContext.xml b/hazelcast-spring/src/test/resources/com/hazelcast/spring/customLoadBalancer-applicationContext.xml new file mode 100644 index 000000000000..0c013bf921db --- /dev/null +++ b/hazelcast-spring/src/test/resources/com/hazelcast/spring/customLoadBalancer-applicationContext.xml @@ -0,0 +1,68 @@ + + + + + + + + + classpath:/hazelcast-default.properties + + + + + + + + + + + ${cluster.members} + + + + + + + + + 127.0.0.1:5700 + 127.0.0.1:5701 + + + + + + + 127.0.0.1:5700 + 127.0.0.1:5701 + + + + + + + + diff --git a/hazelcast-spring/src/test/resources/com/hazelcast/spring/customLoadBalancer-invalidApplicationContext.xml b/hazelcast-spring/src/test/resources/com/hazelcast/spring/customLoadBalancer-invalidApplicationContext.xml new file mode 100644 index 000000000000..c66c3127838e --- /dev/null +++ b/hazelcast-spring/src/test/resources/com/hazelcast/spring/customLoadBalancer-invalidApplicationContext.xml @@ -0,0 +1,39 @@ + + + + + + + + 127.0.0.1:5700 + 127.0.0.1:5701 + + + + + + + + diff --git a/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfig.java b/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfig.java index 3cbb591f1dd4..8f94e5553644 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfig.java +++ b/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfig.java @@ -53,6 +53,7 @@ import static com.hazelcast.internal.config.DeclarativeConfigUtil.SYSPROP_CLIENT_CONFIG; import static com.hazelcast.internal.config.DeclarativeConfigUtil.validateSuffixInSystemProperty; import static com.hazelcast.internal.util.Preconditions.checkFalse; +import static com.hazelcast.internal.util.Preconditions.checkHasText; import static com.hazelcast.internal.util.Preconditions.isNotNull; import static com.hazelcast.partition.strategy.StringPartitioningStrategy.getBaseName; @@ -84,6 +85,11 @@ public class ClientConfig { */ private LoadBalancer loadBalancer; + /** + * Load balancer class name. Used internally with declarative configuration. + */ + private String loadBalancerClassName; + /** * List of listeners that Hazelcast will automatically add as a part of initialization process. * Currently only supports {@link com.hazelcast.core.LifecycleListener}. @@ -128,6 +134,7 @@ public ClientConfig(ClientConfig config) { securityConfig = new ClientSecurityConfig(config.securityConfig); networkConfig = new ClientNetworkConfig(config.networkConfig); loadBalancer = config.loadBalancer; + loadBalancerClassName = config.loadBalancerClassName; listenerConfigs = new LinkedList<>(); for (ListenerConfig listenerConfig : config.listenerConfigs) { listenerConfigs.add(new ListenerConfig(listenerConfig)); @@ -599,7 +606,9 @@ public LoadBalancer getLoadBalancer() { } /** - * Sets the {@link LoadBalancer} + * Sets the {@link LoadBalancer}. + *

+ * If a load balancer class name was set, it will be removed. * * @param loadBalancer {@link LoadBalancer} * @return configured {@link com.hazelcast.client.config.ClientConfig} for chaining @@ -607,9 +616,36 @@ public LoadBalancer getLoadBalancer() { */ public ClientConfig setLoadBalancer(LoadBalancer loadBalancer) { this.loadBalancer = loadBalancer; + this.loadBalancerClassName = null; + return this; + } + + /** + * Gets load balancer class name + * + * @return load balancer class name + * @see com.hazelcast.client.LoadBalancer + */ + public String getLoadBalancerClassName() { + return loadBalancerClassName; + } + + /** + * Sets load balancer class name. + *

+ * If a load balancer implementation was set, it will be removed. + * + * @param loadBalancerClassName {@link LoadBalancer} + * @return configured {@link com.hazelcast.client.config.ClientConfig} for chaining + * @see com.hazelcast.client.LoadBalancer + */ + public ClientConfig setLoadBalancerClassName(@Nonnull String loadBalancerClassName) { + this.loadBalancerClassName = checkHasText(loadBalancerClassName, "Load balancer class name must contain text"); + this.loadBalancer = null; return this; } + /** * Gets the classLoader * @@ -923,7 +959,7 @@ public ClientConfig setMetricsConfig(@Nonnull ClientMetricsConfig metricsConfig) @Override public int hashCode() { return Objects.hash(backupAckToClientEnabled, classLoader, clusterName, configPatternMatcher, connectionStrategyConfig, - flakeIdGeneratorConfigMap, instanceName, labels, listenerConfigs, loadBalancer, + flakeIdGeneratorConfigMap, instanceName, labels, listenerConfigs, loadBalancer, loadBalancerClassName, managedContext, metricsConfig, nativeMemoryConfig, nearCacheConfigMap, networkConfig, properties, proxyFactoryConfigs, queryCacheConfigs, reliableTopicConfigMap, securityConfig, serializationConfig, userCodeDeploymentConfig, userContext); @@ -949,6 +985,7 @@ public boolean equals(Object obj) { && Objects.equals(flakeIdGeneratorConfigMap, other.flakeIdGeneratorConfigMap) && Objects.equals(instanceName, other.instanceName) && Objects.equals(labels, other.labels) && Objects.equals(listenerConfigs, other.listenerConfigs) && Objects.equals(loadBalancer, other.loadBalancer) + && Objects.equals(loadBalancerClassName, other.loadBalancerClassName) && Objects.equals(managedContext, other.managedContext) && Objects.equals(metricsConfig, other.metricsConfig) && Objects.equals(nativeMemoryConfig, other.nativeMemoryConfig) && Objects.equals(nearCacheConfigMap, other.nearCacheConfigMap) @@ -970,6 +1007,7 @@ public String toString() { + ", securityConfig=" + securityConfig + ", networkConfig=" + networkConfig + ", loadBalancer=" + loadBalancer + + ", loadBalancerClassName=" + loadBalancerClassName + ", listenerConfigs=" + listenerConfigs + ", instanceName='" + instanceName + '\'' + ", configPatternMatcher=" + configPatternMatcher diff --git a/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfigXmlGenerator.java b/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfigXmlGenerator.java index bdb576e245d5..dff6cd9afa0c 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfigXmlGenerator.java +++ b/hazelcast/src/main/java/com/hazelcast/client/config/ClientConfigXmlGenerator.java @@ -345,9 +345,14 @@ private static void loadBalancer(XmlGenerator gen, LoadBalancer loadBalancer) { } else if (loadBalancer instanceof RoundRobinLB) { type = "round-robin"; } else { - throw new IllegalArgumentException("Unknown load-balancer type: " + loadBalancer); + type = "custom"; + } + + if ("custom".equals(type)) { + gen.node("load-balancer", loadBalancer.getClass().getName(), "type", type); + } else { + gen.node("load-balancer", null, "type", type); } - gen.node("load-balancer", null, "type", type); } private static void nearCaches(XmlGenerator gen, Map nearCacheMap) { diff --git a/hazelcast/src/main/java/com/hazelcast/client/config/impl/ClientDomConfigProcessor.java b/hazelcast/src/main/java/com/hazelcast/client/config/impl/ClientDomConfigProcessor.java index 32f7a5b28950..df5352b66230 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/config/impl/ClientDomConfigProcessor.java +++ b/hazelcast/src/main/java/com/hazelcast/client/config/impl/ClientDomConfigProcessor.java @@ -114,7 +114,7 @@ public ClientDomConfigProcessor(boolean domLevel3, ClientConfig clientConfig) { } @Override - public void buildConfig(Node rootNode) throws Exception { + public void buildConfig(Node rootNode) { for (Node node : childElements(rootNode)) { String nodeName = cleanNodeName(node); if (occurrenceSet.contains(nodeName)) { @@ -385,9 +385,16 @@ private void handleLoadBalancer(Node node) { clientConfig.setLoadBalancer(new RandomLB()); } else if ("round-robin".equals(type)) { clientConfig.setLoadBalancer(new RoundRobinLB()); + } else if ("custom".equals(type)) { + String loadBalancerClassName = parseCustomLoadBalancerClassName(node); + clientConfig.setLoadBalancerClassName(loadBalancerClassName); } } + protected String parseCustomLoadBalancerClassName(Node node) { + return getTextContent(node); + } + private void handleNetwork(Node node) { ClientNetworkConfig clientNetworkConfig = new ClientNetworkConfig(); for (Node child : childElements(node)) { diff --git a/hazelcast/src/main/java/com/hazelcast/client/config/impl/YamlClientDomConfigProcessor.java b/hazelcast/src/main/java/com/hazelcast/client/config/impl/YamlClientDomConfigProcessor.java index 54ddaf48b39a..4f8b4f1421bc 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/config/impl/YamlClientDomConfigProcessor.java +++ b/hazelcast/src/main/java/com/hazelcast/client/config/impl/YamlClientDomConfigProcessor.java @@ -109,6 +109,11 @@ protected SerializationConfig parseSerialization(final Node node) { return serializationConfig; } + @Override + protected String parseCustomLoadBalancerClassName(Node node) { + return getAttribute(node, "class-name"); + } + private void fillGlobalSerializer(Node child, SerializationConfig serializationConfig) { GlobalSerializerConfig globalSerializerConfig = new GlobalSerializerConfig(); String attrClassName = getAttribute(child, "class-name"); diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/FailoverClientConfigSupport.java b/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/FailoverClientConfigSupport.java index 831d38ac8c8e..dbbb83b0c811 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/FailoverClientConfigSupport.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/FailoverClientConfigSupport.java @@ -131,6 +131,9 @@ private static void checkValidAlternative(ClientConfig mainConfig, ClientConfig if (notEqual(mainConfig.getLoadBalancer(), alternativeConfig.getLoadBalancer())) { throwInvalidConfigurationException(mainClusterName, alterNativeClusterName, "loadBalancer"); } + if (notEqual(mainConfig.getLoadBalancerClassName(), alternativeConfig.getLoadBalancerClassName())) { + throwInvalidConfigurationException(mainClusterName, alterNativeClusterName, "loadBalancerClassName"); + } if (notEqual(mainConfig.getListenerConfigs(), alternativeConfig.getListenerConfigs())) { throwInvalidConfigurationException(mainClusterName, alterNativeClusterName, "listeners"); } diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/HazelcastClientInstanceImpl.java b/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/HazelcastClientInstanceImpl.java index b0448ceb9263..f44c53255d57 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/HazelcastClientInstanceImpl.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/clientside/HazelcastClientInstanceImpl.java @@ -312,7 +312,15 @@ private void startMetrics() { private LoadBalancer initLoadBalancer(ClientConfig config) { LoadBalancer lb = config.getLoadBalancer(); if (lb == null) { - lb = new RoundRobinLB(); + if (config.getLoadBalancerClassName() != null) { + try { + return ClassLoaderUtil.newInstance(config.getClassLoader(), config.getLoadBalancerClassName()); + } catch (Exception e) { + rethrow(e); + } + } else { + lb = new RoundRobinLB(); + } } return lb; } diff --git a/hazelcast/src/main/resources/hazelcast-client-config-4.0.xsd b/hazelcast/src/main/resources/hazelcast-client-config-4.0.xsd index 5dd18cdd6af8..1d4f0ad4a903 100644 --- a/hazelcast/src/main/resources/hazelcast-client-config-4.0.xsd +++ b/hazelcast/src/main/resources/hazelcast-client-config-4.0.xsd @@ -112,11 +112,11 @@ - - - - - + + + + + @@ -249,14 +249,19 @@ - - - - - - - - + + + + + + + + + + + + + @@ -827,8 +832,8 @@ - - + + diff --git a/hazelcast/src/main/resources/hazelcast-client-full-example.xml b/hazelcast/src/main/resources/hazelcast-client-full-example.xml index 740e949ce42b..4e189cdb066e 100644 --- a/hazelcast/src/main/resources/hazelcast-client-full-example.xml +++ b/hazelcast/src/main/resources/hazelcast-client-full-example.xml @@ -557,6 +557,8 @@ It has the attribute "type". The valid values for the type of the load balancer are" * "random": The member the operations to be sent to is chosen randomly. * "round-robin": The member the operations to be sent to is chosen in a round-robin fashion. + * "custom": The member the operations to be sent to is chosen by provided load balancer implementation. + The implementation class name is specified as text content. --> diff --git a/hazelcast/src/main/resources/hazelcast-client-full-example.yaml b/hazelcast/src/main/resources/hazelcast-client-full-example.yaml index 98f4066ea28f..f12b5b64a07b 100644 --- a/hazelcast/src/main/resources/hazelcast-client-full-example.yaml +++ b/hazelcast/src/main/resources/hazelcast-client-full-example.yaml @@ -510,6 +510,8 @@ hazelcast-client: # It has a scalar sub-node called "type". The valid values for the type of the load balancer are: # * "random": The member the operations to be sent to is chosen randomly. # * "round-robin": The member the operations to be sent to is chosen in a round-robin fashion. + # * "custom": The member the operations to be sent to is chosen by provided load balancer implementation. + # The implementation class name is specified in additional "class-name" key. # load-balancer: type: random diff --git a/hazelcast/src/test/java/com/hazelcast/client/bluegreen/FailoverConfigTest.java b/hazelcast/src/test/java/com/hazelcast/client/bluegreen/FailoverConfigTest.java index b28ab378f3af..11ab10f882a8 100644 --- a/hazelcast/src/test/java/com/hazelcast/client/bluegreen/FailoverConfigTest.java +++ b/hazelcast/src/test/java/com/hazelcast/client/bluegreen/FailoverConfigTest.java @@ -54,15 +54,15 @@ public void testAllClientConfigsAreHandledInMultipleClientConfigSupport() { "setNearCacheConfigMap", "getFlakeIdGeneratorConfigMap", "findFlakeIdGeneratorConfig", "getFlakeIdGeneratorConfig", "addFlakeIdGeneratorConfig", "setFlakeIdGeneratorConfigMap", "setReliableTopicConfigMap", "getReliableTopicConfigMap", "getCredentials", "setCredentials", "setClusterName", - "getListenerConfigs", "setListenerConfigs", "getLoadBalancer", "setLoadBalancer", "setClassLoader", - "getManagedContext", "setManagedContext", "getExecutorPoolSize", "setExecutorPoolSize", "getProxyFactoryConfigs", - "setProxyFactoryConfigs", "getSerializationConfig", "setSerializationConfig", "getNativeMemoryConfig", - "setNativeMemoryConfig", "getLicenseKey", "setLicenseKey", "addQueryCacheConfig", "getQueryCacheConfigs", - "setQueryCacheConfigs", "getInstanceName", "setInstanceName", "getConnectionStrategyConfig", - "setConnectionStrategyConfig", "getUserCodeDeploymentConfig", "setUserCodeDeploymentConfig", - "getOrCreateQueryCacheConfig", "getOrNullQueryCacheConfig", "addLabel", "setLabels", - "setUserContext", "getUserContext", "setMetricsConfig", "load", - "setBackupAckToClientEnabled", "isBackupAckToClientEnabled", "getMetricsConfig", "equals", "hashCode", "toString"); + "getListenerConfigs", "setListenerConfigs", "getLoadBalancer", "setLoadBalancer", "getLoadBalancerClassName", + "setLoadBalancerClassName", "setClassLoader", "getManagedContext", "setManagedContext", "getExecutorPoolSize", + "setExecutorPoolSize", "getProxyFactoryConfigs", "setProxyFactoryConfigs", "getSerializationConfig", + "setSerializationConfig", "getNativeMemoryConfig", "setNativeMemoryConfig", "getLicenseKey", "setLicenseKey", + "addQueryCacheConfig", "getQueryCacheConfigs", "setQueryCacheConfigs", "getInstanceName", "setInstanceName", + "getConnectionStrategyConfig", "setConnectionStrategyConfig", "getUserCodeDeploymentConfig", "setUserCodeDeploymentConfig", + "getOrCreateQueryCacheConfig", "getOrNullQueryCacheConfig", "addLabel", "setLabels", "setUserContext", + "getUserContext", "setMetricsConfig", "load", "setBackupAckToClientEnabled", "isBackupAckToClientEnabled", + "getMetricsConfig", "equals", "hashCode", "toString", "setInstanceTrackingConfig", "getInstanceTrackingConfig"); Method[] declaredMethods = ClientConfig.class.getDeclaredMethods(); for (Method method : declaredMethods) { String methodName = method.getName(); diff --git a/hazelcast/src/test/java/com/hazelcast/client/config/AbstractClientConfigBuilderTest.java b/hazelcast/src/test/java/com/hazelcast/client/config/AbstractClientConfigBuilderTest.java index 94891f489e90..9e2634fc36f8 100644 --- a/hazelcast/src/test/java/com/hazelcast/client/config/AbstractClientConfigBuilderTest.java +++ b/hazelcast/src/test/java/com/hazelcast/client/config/AbstractClientConfigBuilderTest.java @@ -450,6 +450,9 @@ public void testSecurityConfig() { @Test public abstract void testLoadBalancerRoundRobin(); + @Test + public abstract void testLoadBalancerCustom(); + @Test public abstract void testWhitespaceInNonSpaceStrings(); diff --git a/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigLoadBalancerTest.java b/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigLoadBalancerTest.java new file mode 100644 index 000000000000..9cdcef4d4c18 --- /dev/null +++ b/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigLoadBalancerTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved. + * + * Licensed 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 com.hazelcast.client.config; + +import com.hazelcast.client.LoadBalancer; +import com.hazelcast.client.impl.clientside.ClientTestUtil; +import com.hazelcast.client.impl.clientside.HazelcastClientInstanceImpl; +import com.hazelcast.client.test.CustomLoadBalancer; +import com.hazelcast.client.test.TestHazelcastFactory; +import com.hazelcast.client.util.RandomLB; +import com.hazelcast.client.util.RoundRobinLB; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.test.HazelcastParallelClassRunner; +import com.hazelcast.test.annotation.ParallelJVMTest; +import com.hazelcast.test.annotation.QuickTest; +import org.junit.After; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +@RunWith(HazelcastParallelClassRunner.class) +@Category({QuickTest.class, ParallelJVMTest.class}) +public class ClientConfigLoadBalancerTest { + + private final TestHazelcastFactory hazelcastFactory = new TestHazelcastFactory(); + + @After + public void tearDown() { + hazelcastFactory.terminateAll(); + } + + @Test + public void shouldCreateRoundRobinLoadBalancerWhenNoConfigProvided() { + hazelcastFactory.newHazelcastInstance(); + + HazelcastInstance instance = hazelcastFactory.newHazelcastClient(new ClientConfig()); + HazelcastClientInstanceImpl client = ClientTestUtil.getHazelcastClientInstanceImpl(instance); + + LoadBalancer actual = client.getLoadBalancer(); + assertTrue(actual instanceof RoundRobinLB); + } + + @Test + public void shouldUseConfigClassLoaderInstanceWhenClassNameNotSpecified() { + hazelcastFactory.newHazelcastInstance(); + + LoadBalancer loadBalancer = new RandomLB(); + + ClientConfig config = new ClientConfig(); + config.setLoadBalancer(loadBalancer); + + HazelcastInstance instance = hazelcastFactory.newHazelcastClient(config); + HazelcastClientInstanceImpl client = ClientTestUtil.getHazelcastClientInstanceImpl(instance); + + LoadBalancer actual = client.getLoadBalancer(); + assertSame(loadBalancer, actual); + } + + @Test + public void shouldCreateCustomLoadBalancerWhenConfigInstanceNotProvidedAndClassNameSpecified() { + hazelcastFactory.newHazelcastInstance(); + + ClientConfig clientConfig = new ClientConfig(); + clientConfig.setLoadBalancerClassName("com.hazelcast.client.test.CustomLoadBalancer"); + + HazelcastInstance instance = hazelcastFactory.newHazelcastClient(clientConfig); + HazelcastClientInstanceImpl client = ClientTestUtil.getHazelcastClientInstanceImpl(instance); + + LoadBalancer actual = client.getLoadBalancer(); + assertTrue(actual instanceof CustomLoadBalancer); + } +} diff --git a/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigTest.java b/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigTest.java index 58a02a7e6afb..35b0b81842d5 100644 --- a/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigTest.java +++ b/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigTest.java @@ -16,9 +16,11 @@ package com.hazelcast.client.config; +import com.hazelcast.client.LoadBalancer; import com.hazelcast.client.test.Employee; import com.hazelcast.client.test.PortableFactory; import com.hazelcast.client.test.TestHazelcastFactory; +import com.hazelcast.client.util.RandomLB; import com.hazelcast.config.Config; import com.hazelcast.config.SerializationConfig; import com.hazelcast.core.HazelcastInstance; @@ -38,6 +40,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelJVMTest.class}) @@ -135,4 +139,28 @@ public void testReliableTopic() { assertEquals(100, newConfig.getReadBatchSize()); } + + @Test + public void testSettingLoaderBalancerShouldClearLoadBalancerClassName() { + LoadBalancer loadBalancer = new RandomLB(); + + ClientConfig clientConfig = new ClientConfig(); + clientConfig.setLoadBalancerClassName("com.hazelcast.client.test.CustomLoadBalancer"); + clientConfig.setLoadBalancer(loadBalancer); + + assertNull(clientConfig.getLoadBalancerClassName()); + assertSame(loadBalancer, clientConfig.getLoadBalancer()); + } + + @Test + public void testSettingLoadBalancerClassNameShouldClearLoadBalancer() { + LoadBalancer loadBalancer = new RandomLB(); + + ClientConfig clientConfig = new ClientConfig(); + clientConfig.setLoadBalancer(loadBalancer); + clientConfig.setLoadBalancerClassName("com.hazelcast.client.test.CustomLoadBalancer"); + + assertEquals("com.hazelcast.client.test.CustomLoadBalancer", clientConfig.getLoadBalancerClassName()); + assertNull(clientConfig.getLoadBalancer()); + } } diff --git a/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigXmlGeneratorTest.java b/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigXmlGeneratorTest.java index e74bba418912..584cde8a6d4d 100644 --- a/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigXmlGeneratorTest.java +++ b/hazelcast/src/test/java/com/hazelcast/client/config/ClientConfigXmlGeneratorTest.java @@ -17,6 +17,7 @@ package com.hazelcast.client.config; import com.hazelcast.client.LoadBalancer; +import com.hazelcast.client.test.CustomLoadBalancer; import com.hazelcast.client.util.RandomLB; import com.hazelcast.config.AliasedDiscoveryConfig; import com.hazelcast.config.ConfigCompatibilityChecker; @@ -78,6 +79,7 @@ import static com.hazelcast.config.NearCacheConfig.LocalUpdatePolicy.CACHE_ON_UPDATE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @RunWith(HazelcastParallelClassRunner.class) @@ -436,8 +438,21 @@ public void proxyFactory() { @Test public void loadBalancer() { clientConfig.setLoadBalancer(new RandomLB()); - LoadBalancer actual = newConfigViaGenerator().getLoadBalancer(); + ClientConfig newClientConfig = newConfigViaGenerator(); + LoadBalancer actual = newClientConfig.getLoadBalancer(); assertTrue(actual instanceof RandomLB); + String actualClassName = newClientConfig.getLoadBalancerClassName(); + assertNull(actualClassName); + } + + @Test + public void loadBalancerCustom() { + clientConfig.setLoadBalancer(new CustomLoadBalancer()); + ClientConfig newClientConfig = newConfigViaGenerator(); + LoadBalancer actual = newClientConfig.getLoadBalancer(); + assertNull(actual); + String actualClassName = newClientConfig.getLoadBalancerClassName(); + assertEquals("com.hazelcast.client.test.CustomLoadBalancer", actualClassName); } private NearCacheConfig createNearCacheConfig(String name) { diff --git a/hazelcast/src/test/java/com/hazelcast/client/config/XmlClientConfigBuilderTest.java b/hazelcast/src/test/java/com/hazelcast/client/config/XmlClientConfigBuilderTest.java index fbe6a0832a5e..e13f8ea6474e 100644 --- a/hazelcast/src/test/java/com/hazelcast/client/config/XmlClientConfigBuilderTest.java +++ b/hazelcast/src/test/java/com/hazelcast/client/config/XmlClientConfigBuilderTest.java @@ -58,6 +58,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -340,6 +341,7 @@ public void testLoadBalancerRandom() { ClientConfig config = buildConfig(xml); assertInstanceOf(RandomLB.class, config.getLoadBalancer()); + assertNull(config.getLoadBalancerClassName()); } @Override @@ -352,6 +354,20 @@ public void testLoadBalancerRoundRobin() { ClientConfig config = buildConfig(xml); assertInstanceOf(RoundRobinLB.class, config.getLoadBalancer()); + assertNull(config.getLoadBalancerClassName()); + } + + @Override + @Test + public void testLoadBalancerCustom() { + String xml = HAZELCAST_CLIENT_START_TAG + + "com.hazelcast.client.test.CustomLoadBalancer" + + HAZELCAST_CLIENT_END_TAG; + + ClientConfig config = buildConfig(xml); + + assertNull(config.getLoadBalancer()); + assertEquals("com.hazelcast.client.test.CustomLoadBalancer", config.getLoadBalancerClassName()); } @Override diff --git a/hazelcast/src/test/java/com/hazelcast/client/config/YamlClientConfigBuilderTest.java b/hazelcast/src/test/java/com/hazelcast/client/config/YamlClientConfigBuilderTest.java index ceef4538792b..0aa6062493fb 100644 --- a/hazelcast/src/test/java/com/hazelcast/client/config/YamlClientConfigBuilderTest.java +++ b/hazelcast/src/test/java/com/hazelcast/client/config/YamlClientConfigBuilderTest.java @@ -52,6 +52,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** @@ -306,6 +307,7 @@ public void testLoadBalancerRandom() { ClientConfig config = buildConfig(yaml); assertInstanceOf(RandomLB.class, config.getLoadBalancer()); + assertNull(config.getLoadBalancerClassName()); } @Override @@ -319,6 +321,22 @@ public void testLoadBalancerRoundRobin() { ClientConfig config = buildConfig(yaml); assertInstanceOf(RoundRobinLB.class, config.getLoadBalancer()); + assertNull(config.getLoadBalancerClassName()); + } + + @Override + @Test + public void testLoadBalancerCustom() { + String yaml = "" + + "hazelcast-client:\n" + + " load-balancer:\n" + + " type: custom\n" + + " class-name: com.hazelcast.client.test.CustomLoadBalancer\n"; + + ClientConfig config = buildConfig(yaml); + + assertNull(config.getLoadBalancer()); + assertEquals("com.hazelcast.client.test.CustomLoadBalancer", config.getLoadBalancerClassName()); } @Test diff --git a/hazelcast/src/test/java/com/hazelcast/client/test/CustomLoadBalancer.java b/hazelcast/src/test/java/com/hazelcast/client/test/CustomLoadBalancer.java new file mode 100644 index 000000000000..c68ab52f7e26 --- /dev/null +++ b/hazelcast/src/test/java/com/hazelcast/client/test/CustomLoadBalancer.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved. + * + * Licensed 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 com.hazelcast.client.test; + +import com.hazelcast.client.LoadBalancer; +import com.hazelcast.client.config.ClientConfig; +import com.hazelcast.cluster.Cluster; +import com.hazelcast.cluster.Member; + +public class CustomLoadBalancer implements LoadBalancer { + + private static final String DEFAULT_NAME = "default-name"; + + private String name; + + public CustomLoadBalancer() { + this.name = DEFAULT_NAME; + } + + @Override + public void init(Cluster cluster, ClientConfig config) { + } + + @Override + public Member next() { + return null; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +}