blob: ddc06b652ebc250f74d5d8f8dd48bbdf7a4fea06 [file] [log] [blame]
// 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>());
private 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);
createEmptyScopeLevelMappings();
}
/**
* Create an empty map of ConfigKey.Scope values, setting the _scopeLevelConfigsMap with the created map
* This map must contain all ConfigKey.Scope values, except the ConfigKey.Scope.Global.
*/
protected void createEmptyScopeLevelMappings() {
_scopeLevelConfigsMap = new HashMap<ConfigKey.Scope, Set<ConfigKey<?>>>();
_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<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.Domain, new HashSet<ConfigKey<?>>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.ManagementServer, 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 findScopedConfigStorage(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);
}
}