diff --git a/README.md b/README.md index 4d8968d..3134348 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,8 @@ The following is an example declarative configuration. ``` * `self-registration`: Defines if the Discovery SPI plugin will register itself with the Eureka 1 service discovery. It is optional. Default value is `true`. -* `namespace`: Definition for providing different namespaces in order to not to collide with other service registry clients -in eureka-client.properties file. It is optional. Default value is `hazelcast`. +* `namespace`: Definition for providing different namespaces in order not to collide with other service registry + clients in eureka-client.properties file. It is optional. Default value is `hazelcast`. Below you can also find an example of Eureka client properties. @@ -78,9 +78,42 @@ hazelcast.name=hazelcast-test hazelcast.serviceUrl.default=http:// ``` -> `IMPORTANT`: `hazelcast.name` property is crucial for cluster members to discover each other. Please give +> **IMPORTANT**: `hazelcast.name` property is crucial for cluster members to discover each other. Please give identical names in regarding `eureka-client.properties` on EC2 hosts for building cluster of your choice properly. +#### Configuring Eureka Discovery without a properties file + +In some environments adding the `eureka-client.properties` file to the classpath is not feasible. +To support this use case, it is possible to specify the Eureka client properties in the +Hazelcast configuration: Set the `use-classpath-eureka-client-props` property to `false`, +then add the Eureka client properties _without prepending the namespace_, as they will be applied +to the namespace specified with the `namespace` property. + +**NOTE:** If `use-classpath-eureka-client-props` is `true` (its default value), all Eureka client properties +in the Hazelcast configuration will be ignored. + +The following is an example declarative configuration, equivalent to the example given above. + +```xml + + ... + + + + true + hazelcast + false + prod + false + cloud + hazelcast-test + http://your-eureka-server-url + + + + +``` + ### Configuring Eureka Discovery for Hazelcast Client - Add the *hazelcast-eureka-one.jar* dependency to your project. @@ -111,10 +144,42 @@ hazelcast.name=hazelcast-test hazelcast.serviceUrl.default=http:///eureka/v2/ ``` -> `NOTE:` Hazelcast clients do not register themselves to Eureka server with given `namespace` or default namespace, +> **NOTE:** Hazelcast clients do not register themselves to Eureka server with given `namespace` or default namespace, which is `hazelcast`. Therefore, `self-registration` property is overridden and it has no effect. -> `IMPORTANT`: `hazelcast.name` property is crucial for clients to discover cluster members. +> **IMPORTANT:** `hazelcast.name` property is crucial for clients to discover cluster members. + +#### Configuring Eureka Discovery for Hazelcast Client without a properties file + +In some environments adding the `eureka-client.properties` file to the Hazelcast Client classpath is not feasible. +To support this use case, it is possible to specify the Eureka client properties in the +Hazelcast Client configuration: Set the `use-classpath-eureka-client-props` property to `false`, +then add the Eureka client properties _without prepending the namespace_, as they will be applied +to the namespace specified with the `namespace` property. + +**NOTE:** If `use-classpath-eureka-client-props` is `true` (its default value), all Eureka client properties +in the Hazelcast Client configuration will be ignored. + +The following is an example declarative configuration, equivalent to the example given above. + +```xml + + ... + + + + hazelcast + false + prod + false + cloud + hazelcast-test + http://your-eureka-server-url/eureka/v2/ + + + + +``` #### Reusing existing Eureka Client instance If your application provides already configured `EurekaClient` instance e.g. if you are using Spring Cloud, you can reuse your existing client: @@ -124,9 +189,9 @@ EurekaClient eurekaClient = ... EurekaOneDiscoveryStrategyFactory.setEurekaClient(eurekaClient); ``` -When using reused client as above, discovery implementation will **not** send Eureka Server any status changes regarding - application state. Also, if you need to inject `Eureka client` externally, you have to configure discovery programmatically - as shown above code snippet. +When using reused client as above, discovery implementation will **not** send Eureka Server any status changes regarding +application state. Also, if you need to inject `Eureka client` externally, you have to configure discovery +programmatically as shown above code snippet. ## Debugging diff --git a/checkstyle/suppressions.xml b/checkstyle/suppressions.xml index 68d2f85..18e517f 100755 --- a/checkstyle/suppressions.xml +++ b/checkstyle/suppressions.xml @@ -20,4 +20,10 @@ + + + + + + diff --git a/pom.xml b/pom.xml index ff14dc3..d467a93 100644 --- a/pom.xml +++ b/pom.xml @@ -155,6 +155,11 @@ guava ${guava.version} + + commons-configuration + commons-configuration + 1.8 + junit junit diff --git a/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategy.java b/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategy.java index f5c4c3d..16acbe7 100644 --- a/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategy.java +++ b/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategy.java @@ -16,22 +16,20 @@ package com.hazelcast.eureka.one; -import static com.hazelcast.eureka.one.EurekaOneProperties.EUREKA_ONE_SYSTEM_PREFIX; -import static com.hazelcast.eureka.one.EurekaOneProperties.NAMESPACE; -import static com.hazelcast.eureka.one.EurekaOneProperties.SELF_REGISTRATION; - import java.io.IOException; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; import com.google.common.annotations.VisibleForTesting; +import com.hazelcast.config.properties.PropertyDefinition; import com.hazelcast.logging.ILogger; import com.hazelcast.logging.NoLogFactory; import com.hazelcast.nio.Address; @@ -50,8 +48,15 @@ import com.netflix.discovery.DefaultEurekaClientConfig; import com.netflix.discovery.DiscoveryClient; import com.netflix.discovery.EurekaClient; +import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.shared.Application; +import static com.hazelcast.eureka.one.EurekaOneProperties.EUREKA_ONE_SYSTEM_PREFIX; +import static com.hazelcast.eureka.one.EurekaOneProperties.HZ_PROPERTY_DEFINITIONS; +import static com.hazelcast.eureka.one.EurekaOneProperties.NAMESPACE; +import static com.hazelcast.eureka.one.EurekaOneProperties.SELF_REGISTRATION; +import static com.hazelcast.eureka.one.EurekaOneProperties.USE_CLASSPATH_EUREKA_CLIENT_PROPS; + final class EurekaOneDiscoveryStrategy extends AbstractDiscoveryStrategy { @@ -122,6 +127,7 @@ EurekaOneDiscoveryStrategy build() { private final EurekaClient eurekaClient; private final ApplicationInfoManager applicationInfoManager; + private final Boolean useClasspathEurekaClientProps; private final String namespace; private StatusChangeStrategy statusChangeStrategy; @@ -130,6 +136,7 @@ private EurekaOneDiscoveryStrategy(final EurekaOneDiscoveryStrategyBuilder build this.namespace = getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, NAMESPACE, "hazelcast"); boolean selfRegistration = getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, SELF_REGISTRATION, true); + this.useClasspathEurekaClientProps = getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, USE_CLASSPATH_EUREKA_CLIENT_PROPS, true); // override registration if requested if (!selfRegistration) { statusChangeStrategy = new NoopUpdater(); @@ -144,12 +151,31 @@ private EurekaOneDiscoveryStrategy(final EurekaOneDiscoveryStrategyBuilder build } if (builder.eurekaClient == null) { - this.eurekaClient = new DiscoveryClient(applicationInfoManager, new EurekaOneAwareConfig(this.namespace)); + EurekaClientConfig eurekaClientConfig; + if (useClasspathEurekaClientProps) { + eurekaClientConfig = new EurekaOneAwareConfig(this.namespace); + } else { + eurekaClientConfig = new PropertyBasedEurekaClientConfig( + this.namespace, + getEurekaClientProperties(this.namespace, this.getProperties())); + } + this.eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig); } else { this.eurekaClient = builder.eurekaClient; } } + private Map getEurekaClientProperties(String namespace, Map properties) { + Map result = new HashMap(); + for (Map.Entry e : properties.entrySet()) { + result.put(namespace + "." + e.getKey(), e.getValue()); + } + for (PropertyDefinition p : HZ_PROPERTY_DEFINITIONS) { + result.remove(namespace + "." + p.key()); + } + return result; + } + private ApplicationInfoManager initializeApplicationInfoManager(DiscoveryNode localNode) { EurekaInstanceConfig instanceConfig = buildInstanceConfig(localNode); @@ -162,23 +188,26 @@ private ApplicationInfoManager initializeApplicationInfoManager(DiscoveryNode lo private EurekaInstanceConfig buildInstanceConfig(DiscoveryNode localNode) { try { + String value; + if (this.useClasspathEurekaClientProps) { + String configProperty = DynamicPropertyFactory + .getInstance() + .getStringProperty("eureka.client.props", "eureka-client").get(); + + String eurekaPropertyFile = String.format("%s.properties", configProperty); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + URL url = loader.getResource(eurekaPropertyFile); + if (url == null) { + throw new IllegalStateException("Cannot locate " + eurekaPropertyFile + " as a classpath resource."); + } + Properties props = new Properties(); + props.load(url.openStream()); - String configProperty = DynamicPropertyFactory - .getInstance() - .getStringProperty("eureka.client.props", "eureka-client") - .get(); - - String eurekaPropertyFile = String.format("%s.properties", configProperty); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - URL url = loader.getResource(eurekaPropertyFile); - if (url == null) { - throw new IllegalStateException("Cannot locate " + eurekaPropertyFile + " as a classpath resource."); + String key = String.format("%s.datacenter", this.namespace); + value = props.getProperty(key, ""); + } else { + value = String.valueOf(getProperties().get("datacenter")); } - Properties props = new Properties(); - props.load(url.openStream()); - - String key = String.format("%s.datacenter", this.namespace); - String value = props.getProperty(key, ""); if ("cloud".equals(value.trim().toLowerCase())) { return new DelegatingInstanceConfig(new CloudInstanceConfig(this.namespace), localNode); } @@ -275,6 +304,11 @@ void verifyEurekaRegistration() { } while (true); } + @VisibleForTesting + EurekaClient getEurekaClient() { + return eurekaClient; + } + private class EurekaOneAwareConfig extends DefaultEurekaClientConfig { EurekaOneAwareConfig(String namespace) { super(namespace); diff --git a/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategyFactory.java b/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategyFactory.java index 699cae9..1983c3c 100644 --- a/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategyFactory.java +++ b/src/main/java/com/hazelcast/eureka/one/EurekaOneDiscoveryStrategyFactory.java @@ -35,9 +35,12 @@ public class EurekaOneDiscoveryStrategyFactory implements DiscoveryStrategyFactory { - private static final Collection PROPERTY_DEFINITIONS = Lists.newArrayList( - EurekaOneProperties.SELF_REGISTRATION, - EurekaOneProperties.NAMESPACE); + static final Collection PROPERTY_DEFINITIONS = Lists.newArrayList(); + + static { + PROPERTY_DEFINITIONS.addAll(EurekaOneProperties.HZ_PROPERTY_DEFINITIONS); + PROPERTY_DEFINITIONS.addAll(EurekaOneProperties.EUREKA_CLIENT_PROPERTY_DEFINITIONS); + } private static EurekaClient eurekaClient; diff --git a/src/main/java/com/hazelcast/eureka/one/EurekaOneProperties.java b/src/main/java/com/hazelcast/eureka/one/EurekaOneProperties.java index 986cf89..66499bf 100644 --- a/src/main/java/com/hazelcast/eureka/one/EurekaOneProperties.java +++ b/src/main/java/com/hazelcast/eureka/one/EurekaOneProperties.java @@ -16,12 +16,17 @@ package com.hazelcast.eureka.one; +import java.util.Collection; + +import com.google.common.collect.Lists; import com.hazelcast.config.properties.PropertyDefinition; +import com.hazelcast.config.properties.PropertyTypeConverter; import com.hazelcast.config.properties.SimplePropertyDefinition; import com.hazelcast.core.TypeConverter; import static com.hazelcast.config.properties.PropertyTypeConverter.BOOLEAN; import static com.hazelcast.config.properties.PropertyTypeConverter.STRING; +import static com.hazelcast.eureka.one.PropertyBasedEurekaClientConfigConstants.*; /** *

Configuration class of the Hazelcast Discovery Plugin for Eureka 1.

@@ -45,6 +50,16 @@ public final class EurekaOneProperties { */ public static final String EUREKA_ONE_SYSTEM_PREFIX = "hazelcast.eurekaone"; + /** + *

Configuration key: use-classpath-eureka-client-props

+ *

Defines if the Discovery SPI plugin will use the standard Eureka eureka.client.props

+ *

If true, the classpath-based Eureka properties file will be used, + * otherwise the plugin will use properties defined in the discovery-strategy config itself.

+ *

The default value is: true

+ */ + public static final PropertyDefinition USE_CLASSPATH_EUREKA_CLIENT_PROPS = + property("use-classpath-eureka-client-props", BOOLEAN); + /** *

Configuration key: self-registration

*

Defines if the Discovery SPI plugin will register itself with the Eureka 1 service discovery.

@@ -60,6 +75,59 @@ public final class EurekaOneProperties { */ public static final PropertyDefinition NAMESPACE = property("namespace", STRING); + static final Collection HZ_PROPERTY_DEFINITIONS = Lists.newArrayList( + USE_CLASSPATH_EUREKA_CLIENT_PROPS, + SELF_REGISTRATION, + NAMESPACE + ); + + static final Collection EUREKA_CLIENT_PROPERTY_DEFINITIONS = Lists.newArrayList( + property(REGISTRY_REFRESH_INTERVAL_KEY, PropertyTypeConverter.INTEGER), + property(REGISTRATION_REPLICATION_INTERVAL_KEY, PropertyTypeConverter.INTEGER), + property(INITIAL_REGISTRATION_REPLICATION_DELAY_KEY, PropertyTypeConverter.INTEGER), + property(EUREKA_SERVER_URL_POLL_INTERVAL_KEY, PropertyTypeConverter.INTEGER), + property(EUREKA_SERVER_PROXY_HOST_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_PROXY_PORT_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_PROXY_USERNAME_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_PROXY_PASSWORD_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_GZIP_CONTENT_KEY, PropertyTypeConverter.BOOLEAN), + property(EUREKA_SERVER_READ_TIMEOUT_KEY, PropertyTypeConverter.INTEGER), + property(EUREKA_SERVER_CONNECT_TIMEOUT_KEY, PropertyTypeConverter.INTEGER), + property(BACKUP_REGISTRY_CLASSNAME_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_MAX_CONNECTIONS_KEY, PropertyTypeConverter.INTEGER), + property(EUREKA_SERVER_MAX_CONNECTIONS_PER_HOST_KEY, PropertyTypeConverter.INTEGER), + property(EUREKA_SERVER_URL_CONTEXT_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_FALLBACK_URL_CONTEXT_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_PORT_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_FALLBACK_PORT_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_DNS_NAME_KEY, PropertyTypeConverter.STRING), + property(EUREKA_SERVER_FALLBACK_DNS_NAME_KEY, PropertyTypeConverter.STRING), + property(SHOULD_USE_DNS_KEY, PropertyTypeConverter.BOOLEAN), + property(REGISTRATION_ENABLED_KEY, PropertyTypeConverter.BOOLEAN), + property(SHOULD_PREFER_SAME_ZONE_SERVER_KEY, PropertyTypeConverter.BOOLEAN), + property(SHOULD_ALLOW_REDIRECTS_KEY, PropertyTypeConverter.BOOLEAN), + property(SHOULD_LOG_DELTA_DIFF_KEY, PropertyTypeConverter.BOOLEAN), + property(SHOULD_DISABLE_DELTA_KEY, PropertyTypeConverter.BOOLEAN), + property(SHOULD_FETCH_REMOTE_REGION_KEY, PropertyTypeConverter.STRING), + property(CLIENT_REGION_KEY, PropertyTypeConverter.STRING), + property(CLIENT_REGION_FALLBACK_KEY, PropertyTypeConverter.STRING), + property(CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + ".default", PropertyTypeConverter.STRING), + property(SHOULD_FILTER_ONLY_UP_INSTANCES_KEY, PropertyTypeConverter.BOOLEAN), + property(EUREKA_SERVER_CONNECTION_IDLE_TIMEOUT_KEY, PropertyTypeConverter.INTEGER), + property(FETCH_REGISTRY_ENABLED_KEY, PropertyTypeConverter.BOOLEAN), + property(FETCH_SINGLE_VIP_ONLY_KEY, PropertyTypeConverter.STRING), + property(HEARTBEAT_THREADPOOL_SIZE_KEY, PropertyTypeConverter.INTEGER), + property(HEARTBEAT_BACKOFF_BOUND_KEY, PropertyTypeConverter.INTEGER), + property(CACHEREFRESH_THREADPOOL_SIZE_KEY, PropertyTypeConverter.INTEGER), + property(CACHEREFRESH_BACKOFF_BOUND_KEY, PropertyTypeConverter.INTEGER), + property(CONFIG_DOLLAR_REPLACEMENT_KEY, PropertyTypeConverter.STRING), + property(CONFIG_ESCAPE_CHAR_REPLACEMENT_KEY, PropertyTypeConverter.STRING), + property(SHOULD_ONDEMAND_UPDATE_STATUS_KEY, PropertyTypeConverter.BOOLEAN), + property(CLIENT_ENCODER_NAME_KEY, PropertyTypeConverter.STRING), + property(CLIENT_DECODER_NAME_KEY, PropertyTypeConverter.STRING), + property(CLIENT_DATA_ACCEPT_KEY, PropertyTypeConverter.STRING) + ); + // Prevent instantiation private EurekaOneProperties() { } diff --git a/src/main/java/com/hazelcast/eureka/one/PropertyBasedEurekaClientConfig.java b/src/main/java/com/hazelcast/eureka/one/PropertyBasedEurekaClientConfig.java new file mode 100644 index 0000000..6500cad --- /dev/null +++ b/src/main/java/com/hazelcast/eureka/one/PropertyBasedEurekaClientConfig.java @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2008-2017, 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.eureka.one; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import com.netflix.appinfo.EurekaAccept; +import com.netflix.config.DynamicPropertyFactory; +import com.netflix.config.DynamicStringProperty; +import com.netflix.discovery.EurekaClientConfig; +import com.netflix.discovery.shared.transport.DefaultEurekaTransportConfig; +import com.netflix.discovery.shared.transport.EurekaTransportConfig; +import org.apache.commons.configuration.MapConfiguration; + +import static com.hazelcast.eureka.one.PropertyBasedEurekaClientConfigConstants.*; + +/** + * A Map-based implementation of eureka client configuration as required by + * {@link EurekaClientConfig}. + * + *

Adapted from Eureka's DefaultEurekaClientConfig

+ */ +public class PropertyBasedEurekaClientConfig implements EurekaClientConfig { + + /** + * Default Zone + */ + public static final String DEFAULT_ZONE = "defaultZone"; + + private final String namespace; + private final DynamicPropertyFactory configInstance; + private final EurekaTransportConfig transportConfig; + + public PropertyBasedEurekaClientConfig(String namespace, final Map properties) { + this.namespace = namespace.endsWith(".") + ? namespace + : namespace + "."; + + DynamicPropertyFactory.initWithConfigurationSource(new MapConfiguration(properties)); + DynamicPropertyFactory configInstance = DynamicPropertyFactory.getInstance(); + this.configInstance = configInstance; + this.transportConfig = new DefaultEurekaTransportConfig(namespace, configInstance); + } + + /* + * (non-Javadoc) + * + * @see + * com.netflix.discovery.EurekaClientConfig#getRegistryFetchIntervalSeconds + * () + */ + @Override + public int getRegistryFetchIntervalSeconds() { + return configInstance.getIntProperty( + namespace + REGISTRY_REFRESH_INTERVAL_KEY, 30).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig# + * getInstanceInfoReplicationIntervalSeconds() + */ + @Override + public int getInstanceInfoReplicationIntervalSeconds() { + return configInstance.getIntProperty( + namespace + REGISTRATION_REPLICATION_INTERVAL_KEY, 30).get(); + } + + @Override + public int getInitialInstanceInfoReplicationIntervalSeconds() { + return configInstance.getIntProperty( + namespace + INITIAL_REGISTRATION_REPLICATION_DELAY_KEY, 40).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getDnsPollIntervalSeconds() + */ + @Override + public int getEurekaServiceUrlPollIntervalSeconds() { + return configInstance.getIntProperty( + namespace + EUREKA_SERVER_URL_POLL_INTERVAL_KEY, 5 * 60 * 1000).get() / 1000; + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getProxyHost() + */ + @Override + public String getProxyHost() { + return configInstance.getStringProperty( + namespace + EUREKA_SERVER_PROXY_HOST_KEY, null).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getProxyPort() + */ + @Override + public String getProxyPort() { + return configInstance.getStringProperty( + namespace + EUREKA_SERVER_PROXY_PORT_KEY, null).get(); + } + + @Override + public String getProxyUserName() { + return configInstance.getStringProperty( + namespace + EUREKA_SERVER_PROXY_USERNAME_KEY, null).get(); + } + + @Override + public String getProxyPassword() { + return configInstance.getStringProperty( + namespace + EUREKA_SERVER_PROXY_PASSWORD_KEY, null).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#shouldGZipContent() + */ + @Override + public boolean shouldGZipContent() { + return configInstance.getBooleanProperty( + namespace + EUREKA_SERVER_GZIP_CONTENT_KEY, true).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getDSServerReadTimeout() + */ + @Override + public int getEurekaServerReadTimeoutSeconds() { + return configInstance.getIntProperty( + namespace + EUREKA_SERVER_READ_TIMEOUT_KEY, 8).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getDSServerConnectTimeout() + */ + @Override + public int getEurekaServerConnectTimeoutSeconds() { + return configInstance.getIntProperty( + namespace + EUREKA_SERVER_CONNECT_TIMEOUT_KEY, 5).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getBackupRegistryImpl() + */ + @Override + public String getBackupRegistryImpl() { + return configInstance.getStringProperty(namespace + BACKUP_REGISTRY_CLASSNAME_KEY, + null).get(); + } + + /* + * (non-Javadoc) + * + * @see + * com.netflix.discovery.EurekaClientConfig#getDSServerTotalMaxConnections() + */ + @Override + public int getEurekaServerTotalConnections() { + return configInstance.getIntProperty( + namespace + EUREKA_SERVER_MAX_CONNECTIONS_KEY, 200).get(); + } + + /* + * (non-Javadoc) + * + * @see + * com.netflix.discovery.EurekaClientConfig#getDSServerConnectionsPerHost() + */ + @Override + public int getEurekaServerTotalConnectionsPerHost() { + return configInstance.getIntProperty( + namespace + EUREKA_SERVER_MAX_CONNECTIONS_PER_HOST_KEY, 50).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getDSServerURLContext() + */ + @Override + public String getEurekaServerURLContext() { + return configInstance.getStringProperty( + namespace + EUREKA_SERVER_URL_CONTEXT_KEY, + configInstance.getStringProperty(namespace + EUREKA_SERVER_FALLBACK_URL_CONTEXT_KEY, null) + .get()).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getDSServerPort() + */ + @Override + public String getEurekaServerPort() { + return configInstance.getStringProperty( + namespace + EUREKA_SERVER_PORT_KEY, + configInstance.getStringProperty(namespace + EUREKA_SERVER_FALLBACK_PORT_KEY, null) + .get()).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getDSServerDomain() + */ + @Override + public String getEurekaServerDNSName() { + return configInstance.getStringProperty( + namespace + EUREKA_SERVER_DNS_NAME_KEY, + configInstance + .getStringProperty(namespace + EUREKA_SERVER_FALLBACK_DNS_NAME_KEY, null) + .get()).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#shouldUseDns() + */ + @Override + public boolean shouldUseDnsForFetchingServiceUrls() { + return configInstance.getBooleanProperty(namespace + SHOULD_USE_DNS_KEY, + false).get(); + } + + /* + * (non-Javadoc) + * + * @see + * com.netflix.discovery.EurekaClientConfig#getDiscoveryRegistrationEnabled + * () + */ + @Override + public boolean shouldRegisterWithEureka() { + return configInstance.getBooleanProperty( + namespace + REGISTRATION_ENABLED_KEY, true).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#shouldPreferSameZoneDS() + */ + @Override + public boolean shouldPreferSameZoneEureka() { + return configInstance.getBooleanProperty(namespace + SHOULD_PREFER_SAME_ZONE_SERVER_KEY, + true).get(); + } + + @Override + public boolean allowRedirects() { + return configInstance.getBooleanProperty(namespace + SHOULD_ALLOW_REDIRECTS_KEY, false).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#shouldLogDeltaDiff() + */ + @Override + public boolean shouldLogDeltaDiff() { + return configInstance.getBooleanProperty( + namespace + SHOULD_LOG_DELTA_DIFF_KEY, false).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#shouldDisableDelta() + */ + @Override + public boolean shouldDisableDelta() { + return configInstance.getBooleanProperty(namespace + SHOULD_DISABLE_DELTA_KEY, + false).get(); + } + + @Nullable + @Override + public String fetchRegistryForRemoteRegions() { + return configInstance.getStringProperty(namespace + SHOULD_FETCH_REMOTE_REGION_KEY, null).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getRegion() + */ + @Override + public String getRegion() { + DynamicStringProperty defaultEurekaRegion = configInstance.getStringProperty( + CLIENT_REGION_FALLBACK_KEY, Values.DEFAULT_CLIENT_REGION); + return configInstance.getStringProperty(namespace + CLIENT_REGION_KEY, defaultEurekaRegion.get()).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getAvailabilityZones() + */ + @Override + public String[] getAvailabilityZones(String region) { + return configInstance + .getStringProperty( + namespace + region + "." + CONFIG_AVAILABILITY_ZONE_PREFIX, + DEFAULT_ZONE).get().split(","); + } + + /* + * (non-Javadoc) + * + * @see + * com.netflix.discovery.EurekaClientConfig#getEurekaServerServiceUrls() + */ + @Override + public List getEurekaServerServiceUrls(String myZone) { + String serviceUrls = configInstance.getStringProperty( + namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + "." + myZone, null).get(); + if (serviceUrls == null || serviceUrls.isEmpty()) { + serviceUrls = configInstance.getStringProperty( + namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + ".default", null).get(); + + } + if (serviceUrls != null) { + return Arrays.asList(serviceUrls.split(",")); + } + + return new ArrayList(); + } + + /* + * (non-Javadoc) + * + * @see + * com.netflix.discovery.EurekaClientConfig#shouldFilterOnlyUpInstances() + */ + @Override + public boolean shouldFilterOnlyUpInstances() { + return configInstance.getBooleanProperty( + namespace + SHOULD_FILTER_ONLY_UP_INSTANCES_KEY, true).get(); + } + + /* + * (non-Javadoc) + * + * @see + * com.netflix.discovery.EurekaClientConfig#getEurekaConnectionIdleTimeout() + */ + @Override + public int getEurekaConnectionIdleTimeoutSeconds() { + return configInstance.getIntProperty( + namespace + EUREKA_SERVER_CONNECTION_IDLE_TIMEOUT_KEY, 30) + .get(); + } + + @Override + public boolean shouldFetchRegistry() { + return configInstance.getBooleanProperty( + namespace + FETCH_REGISTRY_ENABLED_KEY, true).get(); + } + + /* + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getRegistryRefreshSingleVipAddress() + */ + @Override + public String getRegistryRefreshSingleVipAddress() { + return configInstance.getStringProperty( + namespace + FETCH_SINGLE_VIP_ONLY_KEY, null).get(); + } + + /** + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getHeartbeatExecutorThreadPoolSize() + */ + @Override + public int getHeartbeatExecutorThreadPoolSize() { + return configInstance.getIntProperty( + namespace + HEARTBEAT_THREADPOOL_SIZE_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_SIZE).get(); + } + + @Override + public int getHeartbeatExecutorExponentialBackOffBound() { + return configInstance.getIntProperty( + namespace + HEARTBEAT_BACKOFF_BOUND_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_BACKOFF_BOUND).get(); + } + + /** + * (non-Javadoc) + * + * @see com.netflix.discovery.EurekaClientConfig#getCacheRefreshExecutorThreadPoolSize() + */ + @Override + public int getCacheRefreshExecutorThreadPoolSize() { + return configInstance.getIntProperty( + namespace + CACHEREFRESH_THREADPOOL_SIZE_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_SIZE).get(); + } + + @Override + public int getCacheRefreshExecutorExponentialBackOffBound() { + return configInstance.getIntProperty( + namespace + CACHEREFRESH_BACKOFF_BOUND_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_BACKOFF_BOUND).get(); + } + + @Override + public String getDollarReplacement() { + return configInstance.getStringProperty( + namespace + CONFIG_DOLLAR_REPLACEMENT_KEY, Values.CONFIG_DOLLAR_REPLACEMENT).get(); + } + + @Override + public String getEscapeCharReplacement() { + return configInstance.getStringProperty( + namespace + CONFIG_ESCAPE_CHAR_REPLACEMENT_KEY, Values.CONFIG_ESCAPE_CHAR_REPLACEMENT).get(); + } + + @Override + public boolean shouldOnDemandUpdateStatusChange() { + return configInstance.getBooleanProperty( + namespace + SHOULD_ONDEMAND_UPDATE_STATUS_KEY, true).get(); + } + + @Override + public String getEncoderName() { + return configInstance.getStringProperty( + namespace + CLIENT_ENCODER_NAME_KEY, null).get(); + } + + @Override + public String getDecoderName() { + return configInstance.getStringProperty( + namespace + CLIENT_DECODER_NAME_KEY, null).get(); + } + + @Override + public String getClientDataAccept() { + return configInstance.getStringProperty( + namespace + CLIENT_DATA_ACCEPT_KEY, EurekaAccept.full.name()).get(); + } + + @Override + public String getExperimental(String name) { + return configInstance.getStringProperty(namespace + CONFIG_EXPERIMENTAL_PREFIX + "." + name, null).get(); + } + + @Override + public EurekaTransportConfig getTransportConfig() { + return transportConfig; + } +} diff --git a/src/main/java/com/hazelcast/eureka/one/PropertyBasedEurekaClientConfigConstants.java b/src/main/java/com/hazelcast/eureka/one/PropertyBasedEurekaClientConfigConstants.java new file mode 100644 index 0000000..87b148b --- /dev/null +++ b/src/main/java/com/hazelcast/eureka/one/PropertyBasedEurekaClientConfigConstants.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2008-2017, 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.eureka.one; + +/** + * constants pertaining to property based client configs + * + *

copy of Eureka's non-public PropertyBasedClientConfigConstants

+ */ +final class PropertyBasedEurekaClientConfigConstants { + + static final String CLIENT_REGION_FALLBACK_KEY = "eureka.region"; + + // NOTE: all keys are before any prefixes are applied + static final String CLIENT_REGION_KEY = "region"; + + static final String REGISTRATION_ENABLED_KEY = "registration.enabled"; + static final String FETCH_REGISTRY_ENABLED_KEY = "shouldFetchRegistry"; + + static final String REGISTRY_REFRESH_INTERVAL_KEY = "client.refresh.interval"; + static final String REGISTRATION_REPLICATION_INTERVAL_KEY = "appinfo.replicate.interval"; + static final String INITIAL_REGISTRATION_REPLICATION_DELAY_KEY = "appinfo.initial.replicate.time"; + static final String HEARTBEAT_THREADPOOL_SIZE_KEY = "client.heartbeat.threadPoolSize"; + static final String HEARTBEAT_BACKOFF_BOUND_KEY = "client.heartbeat.exponentialBackOffBound"; + static final String CACHEREFRESH_THREADPOOL_SIZE_KEY = "client.cacheRefresh.threadPoolSize"; + static final String CACHEREFRESH_BACKOFF_BOUND_KEY = "client.cacheRefresh.exponentialBackOffBound"; + + static final String SHOULD_ONDEMAND_UPDATE_STATUS_KEY = "shouldOnDemandUpdateStatusChange"; + static final String SHOULD_DISABLE_DELTA_KEY = "disableDelta"; + static final String SHOULD_FETCH_REMOTE_REGION_KEY = "fetchRemoteRegionsRegistry"; + static final String SHOULD_FILTER_ONLY_UP_INSTANCES_KEY = "shouldFilterOnlyUpInstances"; + static final String FETCH_SINGLE_VIP_ONLY_KEY = "registryRefreshSingleVipAddress"; + static final String CLIENT_ENCODER_NAME_KEY = "encoderName"; + static final String CLIENT_DECODER_NAME_KEY = "decoderName"; + static final String CLIENT_DATA_ACCEPT_KEY = "clientDataAccept"; + + static final String BACKUP_REGISTRY_CLASSNAME_KEY = "backupregistry"; + + static final String SHOULD_PREFER_SAME_ZONE_SERVER_KEY = "preferSameZone"; + static final String SHOULD_ALLOW_REDIRECTS_KEY = "allowRedirects"; + static final String SHOULD_USE_DNS_KEY = "shouldUseDns"; + + static final String EUREKA_SERVER_URL_POLL_INTERVAL_KEY = "serviceUrlPollIntervalMs"; + static final String EUREKA_SERVER_URL_CONTEXT_KEY = "eurekaServer.context"; + static final String EUREKA_SERVER_FALLBACK_URL_CONTEXT_KEY = "context"; + static final String EUREKA_SERVER_PORT_KEY = "eurekaServer.port"; + static final String EUREKA_SERVER_FALLBACK_PORT_KEY = "port"; + static final String EUREKA_SERVER_DNS_NAME_KEY = "eurekaServer.domainName"; + static final String EUREKA_SERVER_FALLBACK_DNS_NAME_KEY = "domainName"; + + static final String EUREKA_SERVER_PROXY_HOST_KEY = "eurekaServer.proxyHost"; + static final String EUREKA_SERVER_PROXY_PORT_KEY = "eurekaServer.proxyPort"; + static final String EUREKA_SERVER_PROXY_USERNAME_KEY = "eurekaServer.proxyUserName"; + static final String EUREKA_SERVER_PROXY_PASSWORD_KEY = "eurekaServer.proxyPassword"; + + static final String EUREKA_SERVER_GZIP_CONTENT_KEY = "eurekaServer.gzipContent"; + static final String EUREKA_SERVER_READ_TIMEOUT_KEY = "eurekaServer.readTimeout"; + static final String EUREKA_SERVER_CONNECT_TIMEOUT_KEY = "eurekaServer.connectTimeout"; + static final String EUREKA_SERVER_MAX_CONNECTIONS_KEY = "eurekaServer.maxTotalConnections"; + static final String EUREKA_SERVER_MAX_CONNECTIONS_PER_HOST_KEY = "eurekaServer.maxConnectionsPerHost"; + // yeah the case on eurekaserver is different, backwards compatibility requirements :( + static final String EUREKA_SERVER_CONNECTION_IDLE_TIMEOUT_KEY = "eurekaserver.connectionIdleTimeoutInSeconds"; + + static final String SHOULD_LOG_DELTA_DIFF_KEY = "printDeltaFullDiff"; + + static final String CONFIG_DOLLAR_REPLACEMENT_KEY = "dollarReplacement"; + static final String CONFIG_ESCAPE_CHAR_REPLACEMENT_KEY = "escapeCharReplacement"; + + + // additional namespaces + static final String CONFIG_EXPERIMENTAL_PREFIX = "experimental"; + static final String CONFIG_AVAILABILITY_ZONE_PREFIX = "availabilityZones"; + static final String CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX = "serviceUrl"; + + // prevent instantiation + private PropertyBasedEurekaClientConfigConstants() { + } + + static class Values { + static final String CONFIG_DOLLAR_REPLACEMENT = "_-"; + static final String CONFIG_ESCAPE_CHAR_REPLACEMENT = "__"; + + static final String DEFAULT_CLIENT_REGION = "us-east-1"; + + static final int DEFAULT_EXECUTOR_THREAD_POOL_SIZE = 5; + static final int DEFAULT_EXECUTOR_THREAD_POOL_BACKOFF_BOUND = 10; + } +} diff --git a/src/test/java/com/hazelcast/eureka/one/EurekaOnePropertyBasedClientConfigTest.java b/src/test/java/com/hazelcast/eureka/one/EurekaOnePropertyBasedClientConfigTest.java new file mode 100644 index 0000000..ee13a8a --- /dev/null +++ b/src/test/java/com/hazelcast/eureka/one/EurekaOnePropertyBasedClientConfigTest.java @@ -0,0 +1,63 @@ +package com.hazelcast.eureka.one; + +import com.google.common.collect.Maps; +import com.hazelcast.eureka.one.EurekaOneDiscoveryStrategy.EurekaOneDiscoveryStrategyBuilder; +import com.hazelcast.spi.discovery.DiscoveryNode; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import com.netflix.discovery.shared.Application; +import com.netflix.discovery.util.InstanceInfoGenerator; +import org.apache.commons.lang.RandomStringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.List; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class EurekaOnePropertyBasedClientConfigTest extends AbstractEurekaOneDiscoveryStrategyTest { + + @Mock + private DiscoveryNode node; + private Map properties; + + @Override + protected void initializeStrategy() { + EurekaOneDiscoveryStrategyBuilder builder = new EurekaOneDiscoveryStrategyBuilder(); + + properties = Maps.newHashMap(); + properties.put("self-registration", Boolean.TRUE); + properties.put("namespace", "hazelcast"); + properties.put("use-classpath-eureka-client-props", Boolean.FALSE); + properties.put("name", "hazelcast-test"); + properties.put("vipAddress", "hazelcast-test-vip"); + properties.put("serviceUrl.default", "http://localhost:8080/eureka/v2/"); + + builder.setEurekaClient(null) + .setProperties(properties) + .setApplicationInfoManager(applicationInfoManager) + .setDiscoveryNode(node) + .setStatusChangeStrategy(new DefaultUpdater()); + strategy = builder.build(); + } + + @Test + public void checkPropertyBasedConfigurationWorking() { + EurekaClient client = strategy.getEurekaClient(); + + assertThat(client, notNullValue()); + assertThat(client.getEurekaClientConfig(), notNullValue()); + assertThat(client.getEurekaClientConfig(), instanceOf(PropertyBasedEurekaClientConfig.class)); + assertThat(client.getEurekaClientConfig().getEurekaServerServiceUrls("default").get(0), + is(properties.get("serviceUrl.default"))); + } +} \ No newline at end of file diff --git a/src/test/resources/hazelcast.xml b/src/test/resources/hazelcast.xml index 6b9bb9b..138a516 100644 --- a/src/test/resources/hazelcast.xml +++ b/src/test/resources/hazelcast.xml @@ -16,7 +16,7 @@ -->