io.inugami.configuration.services.resolver.ConfigurationResolver Maven / Gradle / Ivy
/* --------------------------------------------------------------------
* Inugami
* --------------------------------------------------------------------
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.inugami.configuration.services.resolver;
import io.inugami.api.constants.JvmKeyValues;
import io.inugami.api.exceptions.Asserts;
import io.inugami.api.exceptions.FatalException;
import io.inugami.api.exceptions.TechnicalException;
import io.inugami.api.loggers.Loggers;
import io.inugami.api.models.Gav;
import io.inugami.api.models.plugins.ManifestInfo;
import io.inugami.configuration.models.EventConfig;
import io.inugami.configuration.models.app.ApplicationConfig;
import io.inugami.configuration.models.plugins.Dependency;
import io.inugami.configuration.models.plugins.EventsFileModel;
import io.inugami.configuration.models.plugins.PluginConfiguration;
import io.inugami.configuration.models.plugins.components.config.Components;
import io.inugami.configuration.services.PluginConfigurationLoader;
import io.inugami.configuration.services.resolver.strategies.ClassLoaderPluginConfigStrategy;
import io.inugami.configuration.services.resolver.strategies.HomePluginConfigStrategy;
import io.inugami.configuration.services.resolver.strategies.JvmPluginConfigStrategy;
import io.inugami.configuration.services.resolver.strategies.PluginConfigResolverStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URL;
import java.util.*;
import static java.util.stream.Collectors.toList;
/**
* ConfigurationResolver
*
* @author patrick_guillerm
* @since 26 déc. 2016
*/
@SuppressWarnings({"java:S3655", "java:S2139"})
public class ConfigurationResolver {
// =========================================================================
// ATTRIBUTES
// =========================================================================
public static final File HOME_PATH = initHomePath();
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationResolver.class);
private static final PluginConfigurationLoader PLUGIN_LOADER = new PluginConfigurationLoader();
private static final ManifestResolver MANIFEST_RESOLVER = new ManifestResolver();
private static final ClassLoaderPluginConfigStrategy classLoaderStrategy = new ClassLoaderPluginConfigStrategy(PLUGIN_LOADER);
//@formatter:off
private static final PluginConfigResolverStrategy[] pluginStrategies = {
new JvmPluginConfigStrategy(PLUGIN_LOADER),
new HomePluginConfigStrategy(PLUGIN_LOADER, HOME_PATH),
classLoaderStrategy
};
//@formatter:on
// =========================================================================
// INITIALIZE
// =========================================================================
private static File initHomePath() {
File result = null;
final String jvmValueDefine = System.getProperty(JvmKeyValues.JVM_HOME_PATH.getKey());
if (jvmValueDefine == null) {
final String userHome = System.getProperty("user.home");
Asserts.assertNotNull("Can't get user home information!", userHome);
result = new File(userHome + File.separator + "." + JvmKeyValues.DEFAULT_APPLICATION_NAME);
} else {
result = new File(System.getProperty(JvmKeyValues.JVM_HOME_PATH.getKey()));
}
Asserts.assertNotNull(JvmKeyValues.DEFAULT_APPLICATION_NAME + " HOME isn't define!", result);
if (!result.exists()) {
result.mkdirs();
}
if (!result.canRead()) {
throw new FatalException("can't read in user home : {0}", result.getAbsolutePath());
}
if (!result.canWrite()) {
throw new FatalException("can't write in user home : {0}", result.getAbsolutePath());
}
return result;
}
// =========================================================================
// METHODS
// =========================================================================
public ApplicationConfig loadApplicationConfig(final URL url) {
Optional result = Optional.empty();
try {
result = PLUGIN_LOADER.loadApplicationConfig(url);
} catch (final TechnicalException e) {
throw new FatalException("Can't load application configuration", e);
}
if (!result.isPresent()) {
throw new FatalException("Can't load application configuration");
}
return result.get();
}
public ApplicationConfig loadApplicationConfig(final File file) {
try {
final Optional result;
result = PLUGIN_LOADER.loadApplicationConfig(file);
if (!result.isPresent()) {
throw new FatalException("Can't load application configuration : {0}", file.getAbsolutePath());
}
return result.get();
} catch (final TechnicalException e) {
Loggers.DEBUG.error(e.getMessage(), e);
throw new FatalException("Can't load application configuration : {0} : {1}", file.getAbsolutePath(),
e.getMessage());
}
}
public Optional> resolvePluginsConfigurations() throws TechnicalException {
Optional> result = Optional.empty();
final List configs = processResolvePluginsConfigurations();
if (configs != null) {
result = Optional.of(resolveDependencies(configs));
}
return result;
}
public Optional> resolvePluginEventConfig(final PluginConfiguration config) throws TechnicalException {
Optional> result = Optional.empty();
final List eventsConfigs = processResolvePluginEventConfig(config);
if (eventsConfigs != null) {
result = Optional.of(eventsConfigs);
}
return result;
}
// =========================================================================
// PROCESS
// =========================================================================
protected List processResolvePluginsConfigurations() throws ConfigurationResolverException {
final List errors = new ArrayList<>();
final Map configs = new HashMap<>();
final List componentsConfigs = classLoaderStrategy.loadComponents();
for (final PluginConfigResolverStrategy strategy : pluginStrategies) {
Optional> optConfig = Optional.empty();
LOGGER.info("resolve from strategy :{}", strategy.getClass().getSimpleName());
try {
optConfig = strategy.resolve();
} catch (final ConfigurationResolverException e) {
errors.add(e);
}
if (optConfig.isPresent()) {
final List plugins = optConfig.get();
for (final PluginConfiguration plugin : plugins) {
addPluginToConfig(configs, plugin);
addComponents(plugin, componentsConfigs);
}
}
}
assertsNoErrors(errors);
return convertMapToList(configs);
}
protected List processResolvePluginEventConfig(final PluginConfiguration config) throws TechnicalException {
Asserts.assertNotNull("plugin configuration mustn't be null", config);
List result = null;
if ((config.getEventsFiles() != null) && !config.getEventsFiles().isEmpty()) {
result = new ArrayList<>();
for (final EventsFileModel eventFile : config.getEventsFiles()) {
final EventConfig eventConfig = resolveEventFile(config, eventFile);
LOGGER.debug("found event file : {}", eventConfig.getConfigFile());
result.add(eventConfig);
}
}
return result;
}
private EventConfig resolveEventFile(final PluginConfiguration config,
final EventsFileModel eventFile) throws TechnicalException {
Asserts.assertNotNull("plugin configuration mustn't be null!", config);
Asserts.assertNotNull("event file mustn't be null!", eventFile);
EventConfig result = null;
for (final PluginConfigResolverStrategy strategy : pluginStrategies) {
final Optional eventConfigOpt = strategy.resolveEventFile(config, eventFile);
if (eventConfigOpt.isPresent()) {
result = eventConfigOpt.get();
break;
}
}
if (result == null) {
// @formatter:off
throw new ConfigurationResolverDependecyUnresolvedException(
"can't found event file \"{0}\" define in plugin {1}", eventFile.getName(),
config.getGav().getHash());
// @formatter:on
}
return result;
}
// =========================================================================
// ADD configs
// =========================================================================
private void addPluginToConfig(final Map configs, final PluginConfiguration plugin) {
if (configs.containsKey(plugin.getGav())) {
if (plugin.getFrontConfig().isPresent()) {
final PluginConfiguration resolvedPlugin = configs.get(plugin.getGav());
resolvedPlugin.setFrontConfig(plugin.getFrontConfig().get());
}
} else {
Loggers.PLUGINS.info("plugin found : {}", plugin.getGav().getHash());
configs.put(plugin.getGav(), plugin);
}
}
private void addComponents(final PluginConfiguration plugin, final List componentsConfigs) {
for (final Components compo : componentsConfigs) {
if (compo.isSameGav(plugin.getGav())) {
plugin.setComponents(compo);
break;
}
}
}
// =========================================================================
// RESOLVE DEPENDENCIES
// =========================================================================
@SuppressWarnings({"java:S3776"})
private List resolveDependencies(final List list) throws ConfigurationResolverDependecyUnresolvedException {
LOGGER.info("check plugins dependencies...");
final List result = new ArrayList<>();
final List withDependencies = new ArrayList<>();
list.forEach(config -> {
if ((config.getDependencies() == null) || config.getDependencies().isEmpty()) {
result.add(config);
} else {
withDependencies.add(config);
}
});
LOGGER.info("number plugins with dependencies : {}", withDependencies.size());
while (!withDependencies.isEmpty()) {
for (int index = 0; index < withDependencies.size(); index++) {
final PluginConfiguration item = withDependencies.get(index);
boolean allDependInResolved = true;
for (final Dependency depend : item.getDependencies()) {
allDependInResolved = foundDependency(result, depend);
if (matchDependency(item, depend)) {
throwUnresolved("extension can't depend him self ! ({0})", item);
}
if (!allDependInResolved && !foundDependency(withDependencies, depend)) {
throwUnresolved("can't resolve dependency :({0})", item);
}
}
if (allDependInResolved) {
result.add(item);
withDependencies.remove(item);
}
}
}
return result;
}
private boolean foundDependency(final List values, final Dependency item) {
boolean foundInResolved = false;
for (final PluginConfiguration resolved : values) {
if (matchDependency(resolved, item)) {
foundInResolved = true;
break;
}
}
return foundInResolved;
}
private boolean matchDependency(final PluginConfiguration item, final Dependency depend) {
final boolean match = item.getGav().equalsWithoutVersion(depend);
if (match && !item.getGav().getVersion().equals(depend.getVersion())) {
Loggers.XLLOG.warn("plugin dependency error : {} -> {}", item.getGav(), depend);
}
return match;
}
// =========================================================================
// MANIFEST RESOLVER
// =========================================================================
public ManifestInfo resolvePluginManifest(final PluginConfiguration config) {
return MANIFEST_RESOLVER.resolvePluginManifest(config);
}
// =========================================================================
// TOOLS
// =========================================================================
private List convertMapToList(final Map configs) {
Asserts.assertNotNull(configs);
return configs.values().stream().collect(toList());
}
/**
* Allow to asserts that no errors occurs
*
* @param errors the errors
* @throws ConfigurationResolverException the configuration resolver
* exception
*/
private void assertsNoErrors(final List errors) throws ConfigurationResolverException {
if (!errors.isEmpty()) {
final StringBuilder msg = new StringBuilder("some errors occurs in resolving plugins configurations:\n");
errors.forEach(e -> appendMessageAndLog(msg, e));
throw new ConfigurationResolverException(msg.toString());
}
}
private void appendMessageAndLog(final StringBuilder msg, final ConfigurationResolverException e) {
msg.append(e.getMessage()).append('\n');
LOGGER.error(e.getMessage(), e);
}
private void throwUnresolved(final String message,
final PluginConfiguration dependency) throws ConfigurationResolverDependecyUnresolvedException {
throw new ConfigurationResolverDependecyUnresolvedException(message, dependency.getGav());
}
@SuppressWarnings({"java:S110"})
private class ConfigurationResolverDependecyUnresolvedException extends FatalException {
private static final long serialVersionUID = 4732065150403156551L;
public ConfigurationResolverDependecyUnresolvedException(final String message, final Object... values) {
super(message, values);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy