TestConfigSupplier.java
/*
* Copyright 2017 Providence Authors
*
* 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 net.morimekta.providence.config.testing;
import net.morimekta.providence.PMessage;
import net.morimekta.providence.config.impl.UpdatingConfigSupplier;
import net.morimekta.providence.config.parser.ConfigException;
import net.morimekta.providence.descriptor.PMessageDescriptor;
import net.morimekta.providence.serializer.JsonSerializer;
import net.morimekta.providence.serializer.JsonSerializerException;
import net.morimekta.providence.serializer.PrettySerializer;
import net.morimekta.providence.serializer.Serializer;
import net.morimekta.providence.serializer.pretty.PrettyException;
import javax.annotation.Nonnull;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Clock;
import java.util.Locale;
/**
* Config supplier meant for testing only. It is an updating config supplier, but that
* exposes the config update method itself.
*
* @param <M> The message type.
*/
public class TestConfigSupplier<M extends PMessage<M>> extends UpdatingConfigSupplier<M> {
/**
* Start with an initial config value.
*
* @param initialResource The initial config value.
* @param descriptor The message descriptor.
* @throws ConfigException If unable to load the config.
*/
public TestConfigSupplier(@Nonnull String initialResource, PMessageDescriptor<M> descriptor)
throws ConfigException {
this(loadInternal(initialResource, descriptor));
}
/**
* Start with an initial config value.
*
* @param initialConfig The initial config value.
*/
public TestConfigSupplier(@Nonnull M initialConfig) {
this(Clock.systemUTC(), initialConfig);
}
/**
* Start with an initial config value.
*
* @param clock The clock to use for timing.
* @param initialConfig The initial config value.
*/
public TestConfigSupplier(@Nonnull Clock clock, @Nonnull M initialConfig) {
super(clock);
this.descriptor = initialConfig.descriptor();
set(initialConfig);
}
/**
* Update the current config and trigger updates.
*
* @param newInstance The new config instance.
*/
public void testUpdate(@Nonnull M newInstance) {
set(newInstance);
}
/**
* Update the current config and trigger updates.
*
* @param resourceName The new config resource name.
* @throws ConfigException If the loaded config is not valid.
*/
public void testUpdate(@Nonnull String resourceName) throws ConfigException {
testUpdate(loadInternal(resourceName, descriptor));
}
@Override
public String toString() {
return getName() + "{" + descriptor.getQualifiedName() + "}";
}
@Override
public String getName() {
return "TestConfig";
}
private final PMessageDescriptor<M> descriptor;
private static <Message extends PMessage<Message>>
Message loadInternal(String resourceName, PMessageDescriptor<Message> descriptor) throws ConfigException {
int lastDot = resourceName.lastIndexOf(".");
if (lastDot < 1) {
throw new ConfigException("No file ending, or no resource file name: " + resourceName);
}
int lastSlash = resourceName.lastIndexOf("/");
String fileName = resourceName;
if (lastSlash >= 0) {
fileName = resourceName.substring(lastSlash + 1);
}
String suffix = resourceName.substring(lastDot)
.toLowerCase(Locale.US);
Serializer serializer;
switch (suffix) {
case ".jsn":
case ".json":
serializer = new JsonSerializer();
break;
case ".cfg":
case ".cnf":
case ".config":
serializer = new PrettySerializer().config();
break;
default:
throw new ConfigException("Unrecognized resource config type: %s (%s)",
suffix,
resourceName);
}
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
InputStream in = classLoader.getResourceAsStream(resourceName);
if (in == null) {
in = TestConfigSupplier.class.getResourceAsStream(resourceName);
if (in == null) {
throw new ConfigException("No such config resource: %s", resourceName);
}
}
try (InputStream bin = new BufferedInputStream(in)) {
return serializer.deserialize(bin, descriptor);
} catch (ConfigException e) {
throw e.setFile(fileName);
} catch (PrettyException se) {
if (se.getLine() != null) {
throw new ConfigException(se).setFile(fileName);
}
throw new ConfigException(se, se.getMessage()).setFile(fileName);
} catch (JsonSerializerException se) {
if (se.getLine() != null) {
throw new ConfigException(se).setFile(fileName);
}
throw new ConfigException(se, se.getMessage()).setFile(fileName);
} catch (IOException se) {
throw new ConfigException(se, se.getMessage()).setFile(fileName);
}
}
}