// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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 org.apache.cloudstack.framework.config.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigDepotAdmin; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.ScopedConfigStorage; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.commons.lang.ObjectUtils; import org.apache.log4j.Logger; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; /** * ConfigDepotImpl implements the ConfigDepot and ConfigDepotAdmin interface. * Its functionalities include: * - Control how dynamic config values are cached and refreshed. * - Control how scoped config values are stored. * - Gather all of the Configurable interfaces and insert their config * variables into the config table. * - Hide the data source where configs are stored and retrieved. * * When dealing with this class, we must be very careful on cluster situations. * * TODO: * - Move the rest of the changes to the config table to here. * - Add the code to mark the rows in configuration table without * the corresponding keys to be null. * - Move all of the configurations to using ConfigDepot * - Completely eliminate Config.java * - Figure out the correct categories. * - Add a scope for management server, where if the scope is management server * then the override is retrieved from a properties file. Imagine adding a * new management server node and it is much more capable system than previous * management servers, you want the adjustments to thread pools etc to be * very different than other management serves. * - Add validation methods to ConfigKey<?>. If a validation class is declared * when constructing a ConfigKey then configuration server should use the * validation class to validate the value the admin input for the key. */ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin { private final static Logger s_logger = Logger.getLogger(ConfigDepotImpl.class); @Inject ConfigurationDao _configDao; List<Configurable> _configurables; List<ScopedConfigStorage> _scopedStorages; Set<Configurable> _configured = Collections.synchronizedSet(new HashSet<Configurable>()); HashMap<String, Pair<String, ConfigKey<?>>> _allKeys = new HashMap<String, Pair<String, ConfigKey<?>>>(1007); HashMap<ConfigKey.Scope, Set<ConfigKey<?>>> _scopeLevelConfigsMap = new HashMap<ConfigKey.Scope, Set<ConfigKey<?>>>(); public ConfigDepotImpl() { ConfigKey.init(this); _scopeLevelConfigsMap.put(ConfigKey.Scope.Zone, new HashSet<ConfigKey<?>>()); _scopeLevelConfigsMap.put(ConfigKey.Scope.Cluster, new HashSet<ConfigKey<?>>()); _scopeLevelConfigsMap.put(ConfigKey.Scope.StoragePool, new HashSet<ConfigKey<?>>()); _scopeLevelConfigsMap.put(ConfigKey.Scope.Account, new HashSet<ConfigKey<?>>()); _scopeLevelConfigsMap.put(ConfigKey.Scope.ImageStore, new HashSet<ConfigKey<?>>()); } @Override public ConfigKey<?> get(String key) { Pair<String, ConfigKey<?>> value = _allKeys.get(key); return value != null ? value.second() : null; } @PostConstruct @Override public void populateConfigurations() { Date date = new Date(); for (Configurable configurable : _configurables) { populateConfiguration(date, configurable); } } protected void populateConfiguration(Date date, Configurable configurable) { if (_configured.contains(configurable)) return; s_logger.debug("Retrieving keys from " + configurable.getClass().getSimpleName()); for (ConfigKey<?> key : configurable.getConfigKeys()) { Pair<String, ConfigKey<?>> previous = _allKeys.get(key.key()); if (previous != null && !previous.first().equals(configurable.getConfigComponentName())) { throw new CloudRuntimeException("Configurable " + configurable.getConfigComponentName() + " is adding a key that has been added before by " + previous.first() + ": " + key.toString()); } _allKeys.put(key.key(), new Pair<String, ConfigKey<?>>(configurable.getConfigComponentName(), key)); createOrupdateConfigObject(date, configurable.getConfigComponentName(), key, null); if ((key.scope() != null) && (key.scope() != ConfigKey.Scope.Global)) { Set<ConfigKey<?>> currentConfigs = _scopeLevelConfigsMap.get(key.scope()); currentConfigs.add(key); } } _configured.add(configurable); } private void createOrupdateConfigObject(Date date, String componentName, ConfigKey<?> key, String value) { ConfigurationVO vo = _configDao.findById(key.key()); if (vo == null) { vo = new ConfigurationVO(componentName, key); vo.setUpdated(date); if (value != null) { vo.setValue(value); } _configDao.persist(vo); } else { if (vo.isDynamic() != key.isDynamic() || !ObjectUtils.equals(vo.getDescription(), key.description()) || !ObjectUtils.equals(vo.getDefaultValue(), key.defaultValue()) || !ObjectUtils.equals(vo.getScope(), key.scope().toString()) || !ObjectUtils.equals(vo.getComponent(), componentName)) { vo.setDynamic(key.isDynamic()); vo.setDescription(key.description()); vo.setDefaultValue(key.defaultValue()); vo.setScope(key.scope().toString()); vo.setComponent(componentName); vo.setUpdated(date); _configDao.persist(vo); } } } @Override public void populateConfiguration(Configurable configurable) { populateConfiguration(new Date(), configurable); } @Override public List<String> getComponentsInDepot() { return new ArrayList<String>(); } public ConfigurationDao global() { return _configDao; } public ScopedConfigStorage scoped(ConfigKey<?> config) { for (ScopedConfigStorage storage : _scopedStorages) { if (storage.getScope() == config.scope()) { return storage; } } throw new CloudRuntimeException("Unable to find config storage for this scope: " + config.scope() + " for " + config.key()); } public List<ScopedConfigStorage> getScopedStorages() { return _scopedStorages; } @Inject public void setScopedStorages(List<ScopedConfigStorage> scopedStorages) { _scopedStorages = scopedStorages; } public List<Configurable> getConfigurables() { return _configurables; } @Inject public void setConfigurables(List<Configurable> configurables) { _configurables = configurables; } @Override public Set<ConfigKey<?>> getConfigListByScope(String scope) { return _scopeLevelConfigsMap.get(ConfigKey.Scope.valueOf(scope)); } @Override public <T> void set(ConfigKey<T> key, T value) { _configDao.update(key.key(), value.toString()); } @Override public <T> void createOrUpdateConfigObject(String componentName, ConfigKey<T> key, String value) { createOrupdateConfigObject(new Date(), componentName, key, value); } }