
com.hazelcast.internal.config.ConfigUtils Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2024, 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.internal.config;
import com.hazelcast.config.Config;
import com.hazelcast.config.ConfigPatternMatcher;
import com.hazelcast.config.InvalidConfigurationException;
import com.hazelcast.config.NamedConfig;
import com.hazelcast.internal.util.Preconditions;
import com.hazelcast.internal.util.StringUtil;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import com.hazelcast.partition.strategy.StringPartitioningStrategy;
import javax.annotation.Nonnull;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.function.BiConsumer;
import static com.hazelcast.partition.strategy.StringPartitioningStrategy.getBaseName;
import static java.lang.String.format;
/**
* Utility class to access configuration.
*/
public final class ConfigUtils {
private static final ILogger LOGGER = Logger.getLogger(Config.class);
private static final BiConsumer DEFAULT_NAME_SETTER = NamedConfig::setName;
private ConfigUtils() {
}
public static T lookupByPattern(ConfigPatternMatcher configPatternMatcher, Map configPatterns,
String itemName) {
T candidate = configPatterns.get(itemName);
if (candidate != null) {
return candidate;
}
String configPatternKey = configPatternMatcher.matches(configPatterns.keySet(), itemName);
if (configPatternKey != null) {
return configPatterns.get(configPatternKey);
}
if (!"default".equals(itemName) && !itemName.startsWith("hz:")) {
LOGGER.finest("No configuration found for " + itemName + ", using default config!");
}
return null;
}
/**
* Returns a config for the given name, creating one
* if necessary and adding it to the collection of known configurations.
*
* The configuration is found by matching the configuration name
* pattern to the provided {@code name} without the partition qualifier
* (the part of the name after {@code '@'}).
* If no configuration matches, it will create one by cloning the
* {@code "default"} configuration and add it to the configuration
* collection.
*
* This method is intended to easily and fluently create and add
* configurations more specific than the default configuration without
* explicitly adding it by invoking addXConfig(..)
*
* Because it adds new configurations if they are not already present,
* this method is intended to be used before this config is used to
* create a hazelcast instance. Afterwards, newly added configurations
* may be ignored.
*
* @param name name of the config
* @return the configuration
* @throws com.hazelcast.config.InvalidConfigurationException if ambiguous configurations are found
* @see StringPartitioningStrategy#getBaseName(java.lang.String)
* @see Config#setConfigPatternMatcher(ConfigPatternMatcher)
* @see Config#getConfigPatternMatcher()
*/
public static T getConfig(ConfigPatternMatcher configPatternMatcher,
Map configs, String name,
Class clazz) {
return getConfig(configPatternMatcher, configs, name, clazz, (BiConsumer) DEFAULT_NAME_SETTER);
}
/**
* Similar to {@link ConfigUtils#getConfig(ConfigPatternMatcher, Map, String, Class)}
* This method is introduced specifically for solving problem of EventJournalConfig
* use {@link ConfigUtils#getConfig(ConfigPatternMatcher, Map, String, Class)} along with {@link NamedConfig}
* where possible
*/
public static T getConfig(ConfigPatternMatcher configPatternMatcher,
Map configs, String name,
Class clazz, BiConsumer nameSetter) {
name = getBaseName(name);
T config = lookupByPattern(configPatternMatcher, configs, name);
if (config != null) {
return config;
}
T defConfig = configs.get("default");
try {
if (defConfig == null) {
defConfig = (T) constructReflectively(clazz);
nameSetter.accept(defConfig, "default");
configs.put("default", defConfig);
}
config = cloneClass(clazz, defConfig);
nameSetter.accept(config, name);
configs.put(name, config);
return config;
} catch (NoSuchMethodException | InstantiationException
| IllegalAccessException | InvocationTargetException e) {
LOGGER.severe("Could not create class " + clazz.getName());
assert false;
return null;
}
}
@SuppressWarnings("unchecked")
private static T cloneClass(Class> clazz, T defConfig) throws NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Constructor> copyConstructor = clazz.getDeclaredConstructor(clazz);
copyConstructor.setAccessible(true);
return (T) copyConstructor.newInstance(defConfig);
}
/**
* If {@code configs} contains an exact match for {@code name}, returns
* the matching config. Otherwise, creates a new config with the given
* name, adds it to {@code configs} and returns it.
*/
public static T getByNameOrNew(Map configs, String name,
Class clazz) {
T config = configs.get(name);
if (config != null) {
return config;
} else {
try {
config = constructReflectively(clazz);
config.setName(name);
configs.put(name, config);
return config;
} catch (NoSuchMethodException | InstantiationException
| IllegalAccessException | InvocationTargetException e) {
LOGGER.severe("Could not create class " + clazz.getName());
assert false;
return null;
}
}
}
@Nonnull
private static T constructReflectively(Class clazz)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
T config;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
config = (T) constructor.newInstance();
return config;
}
public static InvalidConfigurationException createAmbiguousConfigurationException(
String itemName, String candidate, String duplicate) {
return new InvalidConfigurationException(
format("Found ambiguous configurations for item\"%s\": \"%s\" vs. \"%s\"%n"
+ "Please specify your configuration.", itemName, candidate, duplicate));
}
public static boolean matches(String configName, String configName2) {
return configName != null
&& configName2 != null
&& configName.replace("-", "").equals(configName2.replace("-", ""));
}
@Nonnull
public static String resolveResourceId(String providedId, URL url) {
if (!StringUtil.isNullOrEmpty(providedId)) {
return providedId;
}
return urlToFileName(url);
}
private static String urlToFileName(URL url) {
String filename = new File(url.getPath()).getName();
return Preconditions.checkHasText(filename, "URL has no path: " + url);
}
}