net.sf.aguacate.configuration.impl.ConfigurationBridgeImpl Maven / Gradle / Ivy
package net.sf.aguacate.configuration.impl;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.sf.aguacate.configuration.Configuration;
import net.sf.aguacate.configuration.ConfigurationBridge;
import net.sf.aguacate.configuration.compiler.ConfigurationCompiler;
import net.sf.aguacate.util.codec.bridge.CodecCoupling;
import net.sf.aguacate.util.filesystem.EventHandler;
import net.sf.aguacate.util.filesystem.FileSystemObserver;
import net.sf.aguacate.util.resource.ResourceLocator;
import net.sf.aguacate.util.resource.impl.ResourceLocatorClassImpl;
import net.sf.aguacate.util.resource.impl.ResourceLocatorFileImpl;
public class ConfigurationBridgeImpl implements ConfigurationBridge, EventHandler {
private static final Logger LOGGER = LogManager.getLogger(ConfigurationBridgeImpl.class);
private static final Logger LOGGER2 = LogManager.getLogger("aguacate.file.configuration");
private static final String SUFFIX = ".json";
private static final int SUFFIX_LENGTH = 5;
private static final String ENVIRONMENT = "DIRECTORY_CONFIGURATION";
private static final File DIRECTORY;
private static final Map SPECS;
private final ResourceLocator locator;
private final ConfigurationCompiler compiler;
private Map cache;
static {
assert SUFFIX_LENGTH == SUFFIX.length();
String temp = System.getProperty(ENVIRONMENT);
if (temp == null || temp.isEmpty()) {
temp = System.getenv(ENVIRONMENT);
if (temp == null || temp.isEmpty()) {
LOGGER.info("No " + ENVIRONMENT + " defined, using default");
DIRECTORY = null;
} else {
LOGGER.info("using " + ENVIRONMENT + " (env): {}", temp);
DIRECTORY = new File(temp);
}
} else {
LOGGER.info("using " + ENVIRONMENT + " (prop): {}", temp);
DIRECTORY = new File(temp);
}
Map spec = new HashMap<>();
spec.put("kind", "configuration");
spec.put("format", "1.0");
SPECS = spec;
}
public ConfigurationBridgeImpl(ConfigurationCompiler compiler) {
if (DIRECTORY == null) {
this.locator = new ResourceLocatorClassImpl(ConfigurationBridge.class);
} else {
this.locator = new ResourceLocatorFileImpl(DIRECTORY);
FileSystemObserver.watch(DIRECTORY.toPath(), this);
}
this.compiler = compiler;
this.cache = new HashMap<>();
}
@Override
public Configuration load(String entity) {
return get(entity);
}
Configuration get(String entity) {
Configuration info = cache.get(entity);
if (info == null) {
synchronized (this) {
info = cache.get(entity);
if (info == null) {
String resource = entity.concat(SUFFIX);
info = load0(resource);
if (info != null) {
LOGGER.trace("original: {}", cache);
Map temp = new HashMap<>(cache);
temp.put(entity, info);
LOGGER.trace("new: {}", temp);
cache = temp;
}
}
}
}
return info;
}
Configuration load0(String file) {
LOGGER2.info("Loading configuration: {}", file);
try {
Configuration info;
InputStream inputStream = locator.open(file);
if (inputStream == null) {
LOGGER.warn("Not Found: {}", file);
info = null;
} else {
Map data;
try {
data = checkSpecs(CodecCoupling.jsonCodecBridge()
.decodeMap(new InputStreamReader(inputStream, StandardCharsets.UTF_8)));
} finally {
try {
inputStream.close();
} catch (IOException e) {
LOGGER.warn("when closing a resource", e);
}
}
LOGGER.trace("decoded: {}", data);
info = compiler.compile(data);
}
return info;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
Map checkSpecs(Map data) {
@SuppressWarnings("unchecked")
Map specs = (Map) data.get("specs");
if (specs == null) {
throw new IllegalArgumentException("no specs");
} else {
if (SPECS.equals(specs)) {
return data;
} else {
throw new IllegalArgumentException(
"unsupported specs: ".concat(CodecCoupling.jsonCodecBridge().encode(specs)));
}
}
}
@Override
public ResourceLocator getLocator() {
return locator;
}
@Override
public void onDelete(Path directory, Path deleted) {
String file = deleted.toString();
if (file.endsWith(SUFFIX)) {
LOGGER2.warn("Removing configuration definition: {}", deleted);
String name = removeSufix(file);
synchronized (this) {
if (cache.containsKey(name)) {
Map temp = new HashMap<>(cache);
temp.remove(name);
cache = temp;
}
}
} else {
LOGGER.debug("ignore deleted file: {}", deleted);
}
}
@Override
public void onUpdate(Path directory, Path updated) {
String file = updated.toString();
if (file.endsWith(SUFFIX)) {
LOGGER2.info("Change detected on configuration: {}", updated);
String name = removeSufix(file);
synchronized (this) {
if (cache.containsKey(name)) {
Map temp = new HashMap<>(cache);
temp.put(name, load0(file));
cache = temp;
}
}
} else {
LOGGER2.warn("Ignored : {}", updated);
LOGGER.debug("ignore updated file: {}", updated);
}
}
String removeSufix(String name) {
return name.substring(0, name.length() - SUFFIX_LENGTH);
}
}