/*
* Copyright 2013 NGDATA nv
* Copyright 2007 Outerthought bvba and Schaubroeck nv
*
* 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 org.lilyproject.runtime.configuration;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lilyproject.runtime.conf.ConfImpl;
import org.lilyproject.runtime.conf.XmlConfBuilder;
import org.lilyproject.util.io.IOUtils;
public abstract class ConfSource {
private final String location;
private Map<String, CachedConfig> confs = new HashMap<String, CachedConfig>();
protected static final String CONFIG_FILE_EXT = ".xml";
private final Log log = LogFactory.getLog(getClass());
public ConfSource(String location) {
this.location = location;
}
/**
* Returns all the available configuration paths.
*/
public Collection<String> getPaths() {
// Only return paths of valid configurations
Set<String> paths = new HashSet<String>();
for (Map.Entry<String, CachedConfig> entry : confs.entrySet()) {
if (entry.getValue().state == ConfigState.OK) {
paths.add(entry.getKey());
}
}
return paths;
}
/**
* Returns the configuration at a certain path.
*/
public CachedConfig get(String path) {
return confs.get(path);
}
public void refresh() {
// Search all config files on disk
List<ConfigPath> configPaths = getConfigFiles();
// Delete configs from cache which don't exist on disk anymore
Iterator<String> currentEntriesIt = confs.keySet().iterator();
while (currentEntriesIt.hasNext()) {
String path = currentEntriesIt.next();
boolean found = false;
for (ConfigPath configPath : configPaths) {
if (configPath.path.equals(path)) {
found = true;
break;
}
}
if (!found) {
currentEntriesIt.remove();
if (log.isDebugEnabled()) {
log.debug("Configuration: detected removed config " + path + " in " + location);
}
}
}
// Add/update configs
for (ConfigPath configPath : configPaths) {
CachedConfig cachedConfig = confs.get(configPath.path);
if (cachedConfig == null || cachedConfig.lastModified != configPath.file.lastModified()) {
if (log.isDebugEnabled()) {
log.debug("Configuration: detected updated or added config " + configPath.path + " in " + location);
}
long lastModified = configPath.file.lastModified();
ConfImpl conf = parseConfiguration(configPath.file);
cachedConfig = new CachedConfig();
cachedConfig.lastModified = lastModified;
cachedConfig.conf = conf;
cachedConfig.state = conf == null ? ConfigState.ERROR : ConfigState.OK;
confs.put(configPath.path, cachedConfig);
}
}
}
protected static class ConfigPath {
String path;
ConfigFile file;
public ConfigPath(String path, ConfigFile file) {
this.path = path;
this.file = file;
}
}
public static interface ConfigFile {
InputStream getInputStream() throws IOException;
String getPath();
long lastModified();
}
public static class CachedConfig {
long lastModified;
ConfImpl conf;
ConfigState state;
public long getLastModified() {
return lastModified;
}
public ConfImpl getConfiguration() {
return conf;
}
}
enum ConfigState { OK, ERROR }
protected abstract List<ConfigPath> getConfigFiles();
protected static boolean acceptFileName(boolean isDirectory, String name) {
if (isDirectory) {
return !name.startsWith(".") && !name.equals("CVS");
} else {
return !name.startsWith(".") && name.endsWith(CONFIG_FILE_EXT);
}
}
private ConfImpl parseConfiguration(ConfigFile file) {
ConfImpl config = null;
InputStream is = null;
try {
is = file.getInputStream();
config = XmlConfBuilder.build(is, file.getPath());
} catch (Throwable e) {
log.error("Error reading configuration file " + file.getPath(), e);
} finally {
IOUtils.closeQuietly(is);
}
return config;
}
}