/*
* 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.spi.discovery.impl;
import com.hazelcast.config.DiscoveryConfig;
import com.hazelcast.config.DiscoveryStrategyConfig;
import com.hazelcast.config.InvalidConfigurationException;
import com.hazelcast.config.properties.PropertyDefinition;
import com.hazelcast.config.properties.ValidationException;
import com.hazelcast.config.properties.ValueValidator;
import com.hazelcast.core.HazelcastException;
import com.hazelcast.core.TypeConverter;
import com.hazelcast.logging.ILogger;
import com.hazelcast.spi.discovery.DiscoveryNode;
import com.hazelcast.spi.discovery.DiscoveryStrategy;
import com.hazelcast.spi.discovery.DiscoveryStrategyFactory;
import com.hazelcast.spi.discovery.NodeFilter;
import com.hazelcast.spi.discovery.integration.DiscoveryService;
import com.hazelcast.spi.discovery.integration.DiscoveryServiceSettings;
import com.hazelcast.util.ServiceLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DefaultDiscoveryService
implements DiscoveryService {
private static final String SERVICE_LOADER_TAG = DiscoveryStrategyFactory.class.getCanonicalName();
private final DiscoveryNode discoveryNode;
private final ILogger logger;
private final Iterable<DiscoveryStrategy> discoveryStrategies;
private final NodeFilter nodeFilter;
public DefaultDiscoveryService(DiscoveryServiceSettings settings) {
this.discoveryNode = settings.getDiscoveryNode();
this.logger = settings.getLogger();
this.nodeFilter = getNodeFilter(settings);
this.discoveryStrategies = loadDiscoveryStrategies(settings);
}
@Override
public void start() {
for (DiscoveryStrategy discoveryStrategy : discoveryStrategies) {
discoveryStrategy.start();
}
}
@Override
public Iterable<DiscoveryNode> discoverNodes() {
Set<DiscoveryNode> discoveryNodes = new HashSet<DiscoveryNode>();
for (DiscoveryStrategy discoveryStrategy : discoveryStrategies) {
Iterable<DiscoveryNode> candidates = discoveryStrategy.discoverNodes();
if (candidates != null) {
for (DiscoveryNode candidate : candidates) {
if (validateCandidate(candidate)) {
discoveryNodes.add(candidate);
}
}
}
}
return discoveryNodes;
}
@Override
public Map<String, Object> discoverLocalMetadata() {
Map<String, Object> metadata = new HashMap<String, Object>();
for (DiscoveryStrategy discoveryStrategy : discoveryStrategies) {
metadata.putAll(discoveryStrategy.discoverLocalMetadata());
}
return metadata;
}
@Override
public void destroy() {
for (DiscoveryStrategy discoveryStrategy : discoveryStrategies) {
discoveryStrategy.destroy();
}
}
public Iterable<DiscoveryStrategy> getDiscoveryStrategies() {
return discoveryStrategies;
}
private NodeFilter getNodeFilter(DiscoveryServiceSettings settings) {
DiscoveryConfig discoveryConfig = settings.getDiscoveryConfig();
ClassLoader configClassLoader = settings.getConfigClassLoader();
if (discoveryConfig.getNodeFilter() != null) {
return discoveryConfig.getNodeFilter();
}
if (discoveryConfig.getNodeFilterClass() != null) {
try {
ClassLoader cl = configClassLoader;
if (cl == null) {
cl = DefaultDiscoveryService.class.getClassLoader();
}
String className = discoveryConfig.getNodeFilterClass();
return (NodeFilter) cl.loadClass(className).newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to configure discovery node filter", e);
}
}
return null;
}
private boolean validateCandidate(DiscoveryNode candidate) {
return nodeFilter == null || nodeFilter.test(candidate);
}
private Iterable<DiscoveryStrategy> loadDiscoveryStrategies(DiscoveryServiceSettings settings) {
DiscoveryConfig discoveryConfig = settings.getDiscoveryConfig();
ClassLoader configClassLoader = settings.getConfigClassLoader();
try {
Collection<DiscoveryStrategyConfig> discoveryStrategyConfigs = new ArrayList<DiscoveryStrategyConfig>(
discoveryConfig.getDiscoveryStrategyConfigs());
Iterator<DiscoveryStrategyFactory> iterator = ServiceLoader
.iterator(DiscoveryStrategyFactory.class, SERVICE_LOADER_TAG, configClassLoader);
// Collect possible factories
List<DiscoveryStrategyFactory> factories = new ArrayList<DiscoveryStrategyFactory>();
while (iterator.hasNext()) {
factories.add(iterator.next());
}
for (DiscoveryStrategyConfig config : discoveryStrategyConfigs) {
DiscoveryStrategyFactory factory = config.getDiscoveryStrategyFactory();
if (factory != null) {
factories.add(factory);
}
}
List<DiscoveryStrategy> discoveryStrategies = new ArrayList<DiscoveryStrategy>();
for (DiscoveryStrategyFactory factory : factories) {
DiscoveryStrategy discoveryStrategy = buildDiscoveryStrategy(factory, discoveryStrategyConfigs);
if (discoveryStrategy != null) {
discoveryStrategies.add(discoveryStrategy);
}
}
return discoveryStrategies;
} catch (Exception e) {
if (e instanceof ValidationException) {
throw new InvalidConfigurationException("Invalid configuration", e);
} else {
throw new RuntimeException("Failed to configure discovery strategies", e);
}
}
}
private Map<String, Comparable> buildProperties(DiscoveryStrategyFactory factory, DiscoveryStrategyConfig config,
String className) {
Collection<PropertyDefinition> propertyDefinitions = factory.getConfigurationProperties();
if (propertyDefinitions == null) {
return Collections.emptyMap();
}
Map<String, Comparable> properties = config.getProperties();
Map<String, Comparable> mappedProperties = new HashMap<String, Comparable>();
for (PropertyDefinition propertyDefinition : propertyDefinitions) {
String propertyKey = propertyDefinition.key();
Comparable value = properties.get(propertyKey);
if (value == null) {
if (!propertyDefinition.optional()) {
throw new HazelcastException(
"Missing property '" + propertyKey + "' on discovery strategy '" + className + "' configuration");
}
continue;
}
TypeConverter typeConverter = propertyDefinition.typeConverter();
Comparable mappedValue = typeConverter.convert(value);
ValueValidator validator = propertyDefinition.validator();
if (validator != null) {
validator.validate(mappedValue);
}
mappedProperties.put(propertyKey, mappedValue);
}
return mappedProperties;
}
private DiscoveryStrategy buildDiscoveryStrategy(DiscoveryStrategyFactory factory,
Collection<DiscoveryStrategyConfig> discoveryStrategyConfigs) {
Class<? extends DiscoveryStrategy> discoveryStrategyType = factory.getDiscoveryStrategyType();
String className = discoveryStrategyType.getName();
for (DiscoveryStrategyConfig config : discoveryStrategyConfigs) {
String factoryClassName = getFactoryClassName(config);
if (className.equals(factoryClassName)) {
Map<String, Comparable> properties = buildProperties(factory, config, className);
return factory.newDiscoveryStrategy(discoveryNode, logger, properties);
}
}
return null;
}
private String getFactoryClassName(DiscoveryStrategyConfig config) {
if (config.getDiscoveryStrategyFactory() != null) {
DiscoveryStrategyFactory factory = config.getDiscoveryStrategyFactory();
return factory.getDiscoveryStrategyType().getName();
}
return config.getClassName();
}
}