Updated to latest config lib and changed how reference config files are loaded.
* Config lib 4f3a91f * All reference files named reference.conf, all will be loaded * Usage of ConfigFactor.load as default way * Extensions use same config as ActorSystem.settings.config
This commit is contained in:
parent
8ab25a23ac
commit
b56201ab7f
72 changed files with 2361 additions and 977 deletions
|
|
@ -7,7 +7,6 @@ import org.junit.Test;
|
||||||
|
|
||||||
import com.typesafe.config.ConfigFactory;
|
import com.typesafe.config.ConfigFactory;
|
||||||
import com.typesafe.config.Config;
|
import com.typesafe.config.Config;
|
||||||
import com.typesafe.config.ConfigParseOptions;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
|
@ -26,14 +25,14 @@ public class JavaExtension {
|
||||||
}
|
}
|
||||||
|
|
||||||
static class TestExtension implements Extension {
|
static class TestExtension implements Extension {
|
||||||
public final ActorSystemImpl system;
|
public final ActorSystemImpl system;
|
||||||
public TestExtension(ActorSystemImpl i) {
|
|
||||||
system = i;
|
public TestExtension(ActorSystemImpl i) {
|
||||||
}
|
system = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Config c = ConfigFactory.parseString("akka.extensions = [ \"akka.actor.JavaExtension$Provider\" ]",
|
private Config c = ConfigFactory.parseString("akka.extensions = [ \"akka.actor.JavaExtension$Provider\" ]");
|
||||||
ConfigParseOptions.defaults());
|
|
||||||
|
|
||||||
private ActorSystem system = ActorSystem.create("JavaExtension", c);
|
private ActorSystem system = ActorSystem.create("JavaExtension", c);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import org.scalatest.WordSpec
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import akka.dispatch._
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class DispatchersSpec extends AkkaSpec {
|
class DispatchersSpec extends AkkaSpec {
|
||||||
|
|
@ -41,7 +40,7 @@ class DispatchersSpec extends AkkaSpec {
|
||||||
throughput = 17
|
throughput = 17
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""", ConfigParseOptions.defaults)
|
""")
|
||||||
|
|
||||||
lazy val allDispatchers: Map[String, Option[MessageDispatcher]] = {
|
lazy val allDispatchers: Map[String, Option[MessageDispatcher]] = {
|
||||||
validTypes.map(t ⇒ (t, from(ConfigFactory.parseMap(Map(tipe -> t).asJava).withFallback(defaultDispatcherConfig)))).toMap
|
validTypes.map(t ⇒ (t, from(ConfigFactory.parseMap(Map(tipe -> t).asJava).withFallback(defaultDispatcherConfig)))).toMap
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,12 @@ package akka.config
|
||||||
|
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import akka.util.duration._
|
import akka.util.duration._
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class ConfigSpec extends AkkaSpec(ConfigFactory.parseResource(classOf[ConfigSpec], "/akka-actor-reference.conf", ConfigParseOptions.defaults)) {
|
class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference) {
|
||||||
|
|
||||||
"The default configuration file (i.e. akka-actor-reference.conf)" must {
|
"The default configuration file (i.e. akka-actor-reference.conf)" must {
|
||||||
"contain all configuration properties for akka-actor that are used in code with their correct defaults" in {
|
"contain all configuration properties for akka-actor that are used in code with their correct defaults" in {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import akka.testkit.AkkaSpec
|
||||||
import akka.util.duration._
|
import akka.util.duration._
|
||||||
import akka.actor.{ Actor, ActorRef, ActorSystemImpl }
|
import akka.actor.{ Actor, ActorRef, ActorSystemImpl }
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
|
|
||||||
|
|
@ -19,7 +18,7 @@ object EventStreamSpec {
|
||||||
loglevel = INFO
|
loglevel = INFO
|
||||||
event-handlers = ["akka.event.EventStreamSpec$MyLog", "%s"]
|
event-handlers = ["akka.event.EventStreamSpec$MyLog", "%s"]
|
||||||
}
|
}
|
||||||
""".format(Logging.StandardOutLoggerName), ConfigParseOptions.defaults)
|
""".format(Logging.StandardOutLoggerName))
|
||||||
|
|
||||||
case class M(i: Int)
|
case class M(i: Int)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ class Report(
|
||||||
sb.append("Akka version: ").append(system.settings.ConfigVersion)
|
sb.append("Akka version: ").append(system.settings.ConfigVersion)
|
||||||
sb.append("\n")
|
sb.append("\n")
|
||||||
sb.append("Akka config:")
|
sb.append("Akka config:")
|
||||||
for ((key, value) ← system.settings.config.toObject) {
|
for ((key, value) ← system.settings.config.toValue) {
|
||||||
sb.append("\n ").append(key).append("=").append(value)
|
sb.append("\n ").append(key).append("=").append(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import akka.actor.{ ActorSystem, ActorSystemImpl }
|
||||||
import java.io.{ ObjectInputStream, ByteArrayInputStream, ByteArrayOutputStream, ObjectOutputStream }
|
import java.io.{ ObjectInputStream, ByteArrayInputStream, ByteArrayOutputStream, ObjectOutputStream }
|
||||||
import akka.actor.DeadLetterActorRef
|
import akka.actor.DeadLetterActorRef
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
|
|
||||||
object SerializeSpec {
|
object SerializeSpec {
|
||||||
|
|
||||||
|
|
@ -32,7 +31,7 @@ object SerializeSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""", ConfigParseOptions.defaults)
|
""")
|
||||||
|
|
||||||
@BeanInfo
|
@BeanInfo
|
||||||
case class Address(no: String, street: String, city: String, zip: String) { def this() = this("", "", "", "") }
|
case class Address(no: String, street: String, city: String, zip: String) { def this() = this("", "", "", "") }
|
||||||
|
|
|
||||||
|
|
@ -6,51 +6,82 @@ package com.typesafe.config;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents an immutable map from config paths to config values. It
|
* An immutable map from config paths to config values.
|
||||||
* also contains some static methods for creating configs.
|
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* Contrast with {@link ConfigObject} which is a map from config <em>keys</em>,
|
||||||
|
* rather than paths, to config values. A {@code Config} contains a tree of
|
||||||
|
* {@code ConfigObject}, and {@link Config#root()} returns the tree's root
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
* Throughout the API, there is a distinction between "keys" and "paths". A key
|
* Throughout the API, there is a distinction between "keys" and "paths". A key
|
||||||
* is a key in a JSON object; it's just a string that's the key in a map. A
|
* is a key in a JSON object; it's just a string that's the key in a map. A
|
||||||
* "path" is a parseable expression with a syntax and it refers to a series of
|
* "path" is a parseable expression with a syntax and it refers to a series of
|
||||||
* keys. Path expressions are described in the spec for "HOCON", which can be
|
* keys. Path expressions are described in the <a
|
||||||
* found at https://github.com/havocp/config/blob/master/HOCON.md; in brief, a
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">spec for
|
||||||
* path is period-separated so "a.b.c" looks for key c in object b in object a
|
* Human-Optimized Config Object Notation</a>. In brief, a path is
|
||||||
* in the root object. Sometimes double quotes are needed around special
|
* period-separated so "a.b.c" looks for key c in object b in object a in the
|
||||||
* characters in path expressions.
|
* root object. Sometimes double quotes are needed around special characters in
|
||||||
|
* path expressions.
|
||||||
*
|
*
|
||||||
* The API for a Config is in terms of path expressions, while the API for a
|
* <p>
|
||||||
* ConfigObject is in terms of keys. Conceptually, Config is a one-level map
|
* The API for a {@code Config} is in terms of path expressions, while the API
|
||||||
* from paths to values, while a ConfigObject is a tree of maps from keys to
|
* for a {@code ConfigObject} is in terms of keys. Conceptually, {@code Config}
|
||||||
* values.
|
* is a one-level map from <em>paths</em> to values, while a
|
||||||
|
* {@code ConfigObject} is a tree of nested maps from <em>keys</em> to values.
|
||||||
*
|
*
|
||||||
* Another difference between Config and ConfigObject is that conceptually,
|
* <p>
|
||||||
* ConfigValue with valueType() of ConfigValueType.NULL exist in a ConfigObject,
|
* Another difference between {@code Config} and {@code ConfigObject} is that
|
||||||
* while a Config treats null values as if they were missing.
|
* conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType()
|
||||||
|
* valueType()} of {@link ConfigValueType#NULL NULL} exist in a
|
||||||
|
* {@code ConfigObject}, while a {@code Config} treats null values as if they
|
||||||
|
* were missing.
|
||||||
*
|
*
|
||||||
* Config is an immutable object and thus safe to use from multiple threads.
|
* <p>
|
||||||
|
* {@code Config} is an immutable object and thus safe to use from multiple
|
||||||
|
* threads. There's never a need for "defensive copies."
|
||||||
*
|
*
|
||||||
* The "getters" on a Config all work in the same way. They never return null,
|
* <p>
|
||||||
* nor do they return a ConfigValue with valueType() of ConfigValueType.NULL.
|
* The "getters" on a {@code Config} all work in the same way. They never return
|
||||||
* Instead, they throw ConfigException.Missing if the value is completely absent
|
* null, nor do they return a {@code ConfigValue} with
|
||||||
* or set to null. If the value is set to null, a subtype of
|
* {@link ConfigValue#valueType() valueType()} of {@link ConfigValueType#NULL
|
||||||
* ConfigException.Missing called ConfigException.Null will be thrown.
|
* NULL}. Instead, they throw {@link ConfigException.Missing} if the value is
|
||||||
* ConfigException.WrongType will be thrown anytime you ask for a type and the
|
* completely absent or set to null. If the value is set to null, a subtype of
|
||||||
* value has an incompatible type. Reasonable type conversions are performed for
|
* {@code ConfigException.Missing} called {@link ConfigException.Null} will be
|
||||||
* you though.
|
* thrown. {@link ConfigException.WrongType} will be thrown anytime you ask for
|
||||||
|
* a type and the value has an incompatible type. Reasonable type conversions
|
||||||
|
* are performed for you though.
|
||||||
*
|
*
|
||||||
* If you want to iterate over the contents of a Config, you have to get its
|
* <p>
|
||||||
* ConfigObject with toObject, and then iterate over the ConfigObject.
|
* If you want to iterate over the contents of a {@code Config}, you have to get
|
||||||
|
* its {@code ConfigObject} with {@link #root()}, and then iterate over the
|
||||||
|
* {@code ConfigObject}.
|
||||||
*
|
*
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement {@code Config}</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
*/
|
*/
|
||||||
public interface Config extends ConfigMergeable {
|
public interface Config extends ConfigMergeable {
|
||||||
/**
|
/**
|
||||||
* Gets the config as a tree of ConfigObject. This is a constant-time
|
* Gets the {@code Config} as a tree of {@link ConfigObject}. This is a
|
||||||
* operation (it is not proportional to the number of values in the Config).
|
* constant-time operation (it is not proportional to the number of values
|
||||||
|
* in the {@code Config}).
|
||||||
*
|
*
|
||||||
* @return
|
* @return the root object in the configuration
|
||||||
*/
|
*/
|
||||||
ConfigObject toObject();
|
ConfigObject root();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the origin of the {@code Config}, which may be a file, or a file
|
||||||
|
* with a line number, or just a descriptive phrase.
|
||||||
|
*
|
||||||
|
* @return the origin of the {@code Config} for use in error messages
|
||||||
|
*/
|
||||||
ConfigOrigin origin();
|
ConfigOrigin origin();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -60,15 +91,157 @@ public interface Config extends ConfigMergeable {
|
||||||
ConfigObject toValue();
|
ConfigObject toValue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a value is present and non-null at the given path. This
|
* Returns a replacement config with all substitutions (the
|
||||||
* differs in two ways from ConfigObject.containsKey(): it looks for a path
|
* <code>${foo.bar}</code> syntax, see <a
|
||||||
* expression, not a key; and it returns false for null values, while
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">the
|
||||||
* containsKey() returns true indicating that the object contains a null
|
* spec</a>) resolved. Substitutions are looked up using this
|
||||||
* value for the key.
|
* <code>Config</code> as the root object, that is, a substitution
|
||||||
|
* <code>${foo.bar}</code> will be replaced with the result of
|
||||||
|
* <code>getValue("foo.bar")</code>.
|
||||||
*
|
*
|
||||||
* If a path exists according to hasPath(), then getValue() will never throw
|
* <p>
|
||||||
* an exception. However, the typed getters, such as getInt(), will still
|
* This method uses {@link ConfigResolveOptions#defaults()}, there is
|
||||||
* throw if the value is not convertible to the requested type.
|
* another variant {@link Config#resolve(ConfigResolveOptions)} which lets
|
||||||
|
* you specify non-default options.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A given {@link Config} must be resolved before using it to retrieve
|
||||||
|
* config values, but ideally should be resolved one time for your entire
|
||||||
|
* stack of fallbacks (see {@link Config#withFallback}). Otherwise, some
|
||||||
|
* substitutions that could have resolved with all fallbacks available may
|
||||||
|
* not resolve, which will be a user-visible oddity.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <code>resolve()</code> should be invoked on root config objects, rather
|
||||||
|
* than on a subtree (a subtree is the result of something like
|
||||||
|
* <code>config.getConfig("foo")</code>). The problem with
|
||||||
|
* <code>resolve()</code> on a subtree is that substitutions are relative to
|
||||||
|
* the root of the config and the subtree will have no way to get values
|
||||||
|
* from the root. For example, if you did
|
||||||
|
* <code>config.getConfig("foo").resolve()</code> on the below config file,
|
||||||
|
* it would not work:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* common-value = 10
|
||||||
|
* foo {
|
||||||
|
* whatever = ${common-value}
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return an immutable object with substitutions resolved
|
||||||
|
* @throws ConfigException.UnresolvedSubstitution
|
||||||
|
* if any substitutions refer to nonexistent paths
|
||||||
|
* @throws ConfigException
|
||||||
|
* some other config exception if there are other problems
|
||||||
|
*/
|
||||||
|
Config resolve();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link Config#resolve()} but allows you to specify non-default
|
||||||
|
* options.
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
* resolve options
|
||||||
|
* @return the resolved <code>Config</code>
|
||||||
|
*/
|
||||||
|
Config resolve(ConfigResolveOptions options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates this config against a reference config, throwing an exception
|
||||||
|
* if it is invalid. The purpose of this method is to "fail early" with a
|
||||||
|
* comprehensive list of problems; in general, anything this method can find
|
||||||
|
* would be detected later when trying to use the config, but it's often
|
||||||
|
* more user-friendly to fail right away when loading the config.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Using this method is always optional, since you can "fail late" instead.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* You must restrict validation to paths you "own" (those whose meaning are
|
||||||
|
* defined by your code module). If you validate globally, you may trigger
|
||||||
|
* errors about paths that happen to be in the config but have nothing to do
|
||||||
|
* with your module. It's best to allow the modules owning those paths to
|
||||||
|
* validate them. Also, if every module validates only its own stuff, there
|
||||||
|
* isn't as much redundant work being done.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If no paths are specified in <code>checkValid()</code>'s parameter list,
|
||||||
|
* validation is for the entire config.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If you specify paths that are not in the reference config, those paths
|
||||||
|
* are ignored. (There's nothing to validate.)
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Here's what validation involves:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>All paths found in the reference config must be present in this
|
||||||
|
* config or an exception will be thrown.
|
||||||
|
* <li>
|
||||||
|
* Some changes in type from the reference config to this config will cause
|
||||||
|
* an exception to be thrown. Not all potential type problems are detected,
|
||||||
|
* in particular it's assumed that strings are compatible with everything
|
||||||
|
* except objects and lists. This is because string types are often "really"
|
||||||
|
* some other type (system properties always start out as strings, or a
|
||||||
|
* string like "5ms" could be used with {@link #getMilliseconds}). Also,
|
||||||
|
* it's allowed to set any type to null or override null with any type.
|
||||||
|
* <li>
|
||||||
|
* Any unresolved substitutions in this config will cause a validation
|
||||||
|
* failure; both the reference config and this config should be resolved
|
||||||
|
* before validation. If the reference config is unresolved, it's a bug in
|
||||||
|
* the caller of this method.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If you want to allow a certain setting to have a flexible type (or
|
||||||
|
* otherwise want validation to be looser for some settings), you could
|
||||||
|
* either remove the problematic setting from the reference config provided
|
||||||
|
* to this method, or you could intercept the validation exception and
|
||||||
|
* screen out certain problems. Of course, this will only work if all other
|
||||||
|
* callers of this method are careful to restrict validation to their own
|
||||||
|
* paths, as they should be.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If validation fails, the thrown exception contains a list of all problems
|
||||||
|
* found. See {@link ConfigException.ValidationFailed#problems}. The
|
||||||
|
* exception's <code>getMessage()</code> will have all the problems
|
||||||
|
* concatenated into one huge string, as well.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Again, <code>checkValid()</code> can't guess every domain-specific way a
|
||||||
|
* setting can be invalid, so some problems may arise later when attempting
|
||||||
|
* to use the config. <code>checkValid()</code> is limited to reporting
|
||||||
|
* generic, but common, problems such as missing settings and blatant type
|
||||||
|
* incompatibilities.
|
||||||
|
*
|
||||||
|
* @param reference
|
||||||
|
* a reference configuration
|
||||||
|
* @param restrictToPaths
|
||||||
|
* only validate values underneath these paths that your code
|
||||||
|
* module owns and understands
|
||||||
|
* @throws ConfigException.ValidationFailed
|
||||||
|
* if there are any validation issues
|
||||||
|
* @throws ConfigException.NotResolved
|
||||||
|
* if this config is not resolved
|
||||||
|
* @throws ConfigException.BugOrBroken
|
||||||
|
* if the reference config is unresolved or caller otherwise
|
||||||
|
* misuses the API
|
||||||
|
*/
|
||||||
|
void checkValid(Config reference, String... restrictToPaths);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a value is present and non-null at the given path. This
|
||||||
|
* differs in two ways from {@code Map.containsKey()} as implemented by
|
||||||
|
* {@link ConfigObject}: it looks for a path expression, not a key; and it
|
||||||
|
* returns false for null values, while {@code containsKey()} returns true
|
||||||
|
* indicating that the object contains a null value for the key.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If a path exists according to {@link #hasPath(String)}, then
|
||||||
|
* {@link #getValue(String)} will never throw an exception. However, the
|
||||||
|
* typed getters, such as {@link #getInt(String)}, will still throw if the
|
||||||
|
* value is not convertible to the requested type.
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* the path expression
|
* the path expression
|
||||||
|
|
@ -78,12 +251,19 @@ public interface Config extends ConfigMergeable {
|
||||||
*/
|
*/
|
||||||
boolean hasPath(String path);
|
boolean hasPath(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the {@code Config}'s root object contains no key-value
|
||||||
|
* pairs.
|
||||||
|
*
|
||||||
|
* @return true if the configuration is empty
|
||||||
|
*/
|
||||||
boolean isEmpty();
|
boolean isEmpty();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the boolean value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -93,7 +273,8 @@ public interface Config extends ConfigMergeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the numeric value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -103,17 +284,20 @@ public interface Config extends ConfigMergeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the 32-bit integer value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
* if value is not convertible to an int
|
* if value is not convertible to an int (for example it is out
|
||||||
|
* of range, or it's a boolean value)
|
||||||
*/
|
*/
|
||||||
int getInt(String path);
|
int getInt(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the 64-bit long value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -123,7 +307,8 @@ public interface Config extends ConfigMergeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the floating-point value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -133,7 +318,8 @@ public interface Config extends ConfigMergeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the string value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -143,7 +329,8 @@ public interface Config extends ConfigMergeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the {@link ConfigObject} value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -153,7 +340,8 @@ public interface Config extends ConfigMergeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the nested {@code Config} value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -162,9 +350,13 @@ public interface Config extends ConfigMergeable {
|
||||||
Config getConfig(String path);
|
Config getConfig(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value at the path as an unwrapped Java boxed value (Boolean,
|
* Gets the value at the path as an unwrapped Java boxed value (
|
||||||
* Integer, Long, etc.)
|
* {@link java.lang.Boolean Boolean}, {@link java.lang.Integer Integer}, and
|
||||||
|
* so on - see {@link ConfigValue#unwrapped()}).
|
||||||
*
|
*
|
||||||
|
* @param path
|
||||||
|
* path expression
|
||||||
|
* @return the unwrapped value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
*/
|
*/
|
||||||
|
|
@ -172,35 +364,47 @@ public interface Config extends ConfigMergeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value at the given path, unless the value is a null value or
|
* Gets the value at the given path, unless the value is a null value or
|
||||||
* missing, in which case it throws just like the other getters. Use get()
|
* missing, in which case it throws just like the other getters. Use
|
||||||
* from the Map interface if you want an unprocessed value.
|
* {@code get()} from the {@link java.util.Map Map} interface if you want an
|
||||||
|
* unprocessed value.
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* path expression
|
||||||
|
* @return the value at the requested path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
*/
|
*/
|
||||||
ConfigValue getValue(String path);
|
ConfigValue getValue(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get value as a size in bytes (parses special strings like "128M"). The
|
* Gets a value as a size in bytes (parses special strings like "128M"). If
|
||||||
* size units are interpreted as for memory, not as for disk space, so they
|
* the value is already a number, then it's left alone; if it's a string,
|
||||||
* are in powers of two.
|
* it's parsed understanding unit suffixes such as "128K", as documented in
|
||||||
|
* the <a href="https://github.com/havocp/config/blob/master/HOCON.md">the
|
||||||
|
* spec</a>.
|
||||||
*
|
*
|
||||||
|
* @param path
|
||||||
|
* path expression
|
||||||
|
* @return the value at the requested path, in bytes
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
* if value is not convertible to Long or String
|
* if value is not convertible to Long or String
|
||||||
* @throws ConfigException.BadValue
|
* @throws ConfigException.BadValue
|
||||||
* if value cannot be parsed as a memory size
|
* if value cannot be parsed as a size in bytes
|
||||||
*/
|
*/
|
||||||
Long getMemorySizeInBytes(String path);
|
Long getBytes(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get value as a duration in milliseconds. If the value is already a
|
* Get value as a duration in milliseconds. If the value is already a
|
||||||
* number, then it's left alone; if it's a string, it's parsed understanding
|
* number, then it's left alone; if it's a string, it's parsed understanding
|
||||||
* units suffixes like "10m" or "5ns"
|
* units suffixes like "10m" or "5ns" as documented in the <a
|
||||||
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">the
|
||||||
|
* spec</a>.
|
||||||
*
|
*
|
||||||
|
* @param path
|
||||||
|
* path expression
|
||||||
|
* @return the duration value at the requested path, in milliseconds
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -213,8 +417,12 @@ public interface Config extends ConfigMergeable {
|
||||||
/**
|
/**
|
||||||
* Get value as a duration in nanoseconds. If the value is already a number
|
* Get value as a duration in nanoseconds. If the value is already a number
|
||||||
* it's taken as milliseconds and converted to nanoseconds. If it's a
|
* it's taken as milliseconds and converted to nanoseconds. If it's a
|
||||||
* string, it's parsed understanding unit suffixes.
|
* string, it's parsed understanding unit suffixes, as for
|
||||||
|
* {@link #getMilliseconds(String)}.
|
||||||
*
|
*
|
||||||
|
* @param path
|
||||||
|
* path expression
|
||||||
|
* @return the duration value at the requested path, in nanoseconds
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
@ -225,13 +433,13 @@ public interface Config extends ConfigMergeable {
|
||||||
Long getNanoseconds(String path);
|
Long getNanoseconds(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list value (with any element type) as a ConfigList, which
|
* Gets a list value (with any element type) as a {@link ConfigList}, which
|
||||||
* implements java.util.List<ConfigValue>. Throws if the path is unset or
|
* implements {@code java.util.List<ConfigValue>}. Throws if the path is
|
||||||
* null.
|
* unset or null.
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* the path to the list value.
|
* the path to the list value.
|
||||||
* @return the ConfigList at the path
|
* @return the {@link ConfigList} at the path
|
||||||
* @throws ConfigException.Missing
|
* @throws ConfigException.Missing
|
||||||
* if value is absent or null
|
* if value is absent or null
|
||||||
* @throws ConfigException.WrongType
|
* @throws ConfigException.WrongType
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,19 @@
|
||||||
*/
|
*/
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All exceptions thrown by the library are subclasses of ConfigException.
|
* All exceptions thrown by the library are subclasses of ConfigException.
|
||||||
*/
|
*/
|
||||||
public class ConfigException extends RuntimeException {
|
public class ConfigException extends RuntimeException {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
final private ConfigOrigin origin;
|
||||||
|
|
||||||
protected ConfigException(ConfigOrigin origin, String message,
|
protected ConfigException(ConfigOrigin origin, String message,
|
||||||
Throwable cause) {
|
Throwable cause) {
|
||||||
super(origin.description() + ": " + message, cause);
|
super(origin.description() + ": " + message, cause);
|
||||||
|
this.origin = origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ConfigException(ConfigOrigin origin, String message) {
|
protected ConfigException(ConfigOrigin origin, String message) {
|
||||||
|
|
@ -20,12 +24,26 @@ public class ConfigException extends RuntimeException {
|
||||||
|
|
||||||
protected ConfigException(String message, Throwable cause) {
|
protected ConfigException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
this.origin = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ConfigException(String message) {
|
protected ConfigException(String message) {
|
||||||
this(message, null);
|
this(message, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an "origin" (such as a filename and line number) for the
|
||||||
|
* exception, or null if none is available. If there's no sensible origin
|
||||||
|
* for a given exception, or the kind of exception doesn't meaningfully
|
||||||
|
* relate to a particular origin file, this returns null. Never assume this
|
||||||
|
* will return non-null, it can always return null.
|
||||||
|
*
|
||||||
|
* @return origin of the problem, or null if unknown/inapplicable
|
||||||
|
*/
|
||||||
|
public ConfigOrigin origin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception indicating that the type of a value does not match the type you
|
* Exception indicating that the type of a value does not match the type you
|
||||||
* requested.
|
* requested.
|
||||||
|
|
@ -163,10 +181,11 @@ public class ConfigException extends RuntimeException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception indicating that there's a bug in something or the runtime
|
* Exception indicating that there's a bug in something (possibly the
|
||||||
* environment is broken. This exception should never be handled; instead,
|
* library itself) or the runtime environment is broken. This exception
|
||||||
* something should be fixed to keep the exception from occurring.
|
* should never be handled; instead, something should be fixed to keep the
|
||||||
*
|
* exception from occurring. This exception can be thrown by any method in
|
||||||
|
* the library.
|
||||||
*/
|
*/
|
||||||
public static class BugOrBroken extends ConfigException {
|
public static class BugOrBroken extends ConfigException {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
@ -212,12 +231,29 @@ public class ConfigException extends RuntimeException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception indicating that a substitution did not resolve to anything.
|
||||||
|
* Thrown by {@link Config#resolve}.
|
||||||
|
*/
|
||||||
|
public static class UnresolvedSubstitution extends Parse {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UnresolvedSubstitution(ConfigOrigin origin, String expression, Throwable cause) {
|
||||||
|
super(origin, "Could not resolve substitution to a value: " + expression, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnresolvedSubstitution(ConfigOrigin origin, String expression) {
|
||||||
|
this(origin, expression, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception indicating that you tried to use a function that requires
|
* Exception indicating that you tried to use a function that requires
|
||||||
* substitutions to be resolved, but substitutions have not been resolved.
|
* substitutions to be resolved, but substitutions have not been resolved
|
||||||
* This is always a bug in either application code or the library; it's
|
* (that is, {@link Config#resolve} was not called). This is always a bug in
|
||||||
* wrong to write a handler for this exception because you should be able to
|
* either application code or the library; it's wrong to write a handler for
|
||||||
* fix the code to avoid it.
|
* this exception because you should be able to fix the code to avoid it by
|
||||||
|
* adding calls to {@link Config#resolve}.
|
||||||
*/
|
*/
|
||||||
public static class NotResolved extends BugOrBroken {
|
public static class NotResolved extends BugOrBroken {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
@ -230,4 +266,59 @@ public class ConfigException extends RuntimeException {
|
||||||
this(message, null);
|
this(message, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ValidationProblem {
|
||||||
|
|
||||||
|
final private String path;
|
||||||
|
final private ConfigOrigin origin;
|
||||||
|
final private String problem;
|
||||||
|
|
||||||
|
public ValidationProblem(String path, ConfigOrigin origin, String problem) {
|
||||||
|
this.path = path;
|
||||||
|
this.origin = origin;
|
||||||
|
this.problem = problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String path() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigOrigin origin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String problem() {
|
||||||
|
return problem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ValidationFailed extends ConfigException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
final private Iterable<ValidationProblem> problems;
|
||||||
|
|
||||||
|
public ValidationFailed(Iterable<ValidationProblem> problems) {
|
||||||
|
super(makeMessage(problems), null);
|
||||||
|
this.problems = problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<ValidationProblem> problems() {
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String makeMessage(Iterable<ValidationProblem> problems) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (ValidationProblem p : problems) {
|
||||||
|
sb.append(p.origin().description());
|
||||||
|
sb.append(": ");
|
||||||
|
sb.append(p.path());
|
||||||
|
sb.append(": ");
|
||||||
|
sb.append(p.problem());
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.setLength(sb.length() - 2); // chop comma and space
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,203 +13,430 @@ import com.typesafe.config.impl.ConfigImpl;
|
||||||
import com.typesafe.config.impl.Parseable;
|
import com.typesafe.config.impl.Parseable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains static methods for creating Config objects.
|
* Contains static methods for creating {@link Config} instances.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* See also {@link ConfigValueFactory} which contains static methods for
|
||||||
|
* converting Java values into a {@link ConfigObject}. You can then convert a
|
||||||
|
* {@code ConfigObject} into a {@code Config} with {@link ConfigObject#toConfig}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
* The static methods with "load" in the name do some sort of higher-level
|
* The static methods with "load" in the name do some sort of higher-level
|
||||||
* operation potentially parsing multiple resources and resolving substitutions,
|
* operation potentially parsing multiple resources and resolving substitutions,
|
||||||
* while the ones with "parse" in the name just create a ConfigValue from a
|
* while the ones with "parse" in the name just create a {@link ConfigValue}
|
||||||
* resource and nothing else.
|
* from a resource and nothing else.
|
||||||
*/
|
*/
|
||||||
public final class ConfigFactory {
|
public final class ConfigFactory {
|
||||||
|
private ConfigFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a configuration for the given root path in a "standard" way.
|
* Loads an application's configuration from the given classpath resource or
|
||||||
* Oversimplified, if your root path is foo.bar then this will load files
|
* classpath resource basename, sandwiches it between default reference
|
||||||
* from the classpath: foo-bar.conf, foo-bar.json, foo-bar.properties,
|
* config and default overrides, and then resolves it. The classpath
|
||||||
* foo-bar-reference.conf, foo-bar-reference.json,
|
* resource is "raw" (it should have no "/" prefix, and is not made relative
|
||||||
* foo-bar-reference.properties. It will override all those files with any
|
* to any package, so it's like {@link ClassLoader#getResource} not
|
||||||
* system properties that begin with "foo.bar.", as well.
|
* {@link Class#getResource}).
|
||||||
*
|
|
||||||
* The root path should be a path expression, usually just a single short
|
|
||||||
* word, that scopes the package being configured; typically it's the
|
|
||||||
* package name or something similar. System properties overriding values in
|
|
||||||
* the configuration will have to be prefixed with the root path. The root
|
|
||||||
* path may have periods in it if you like but other punctuation or
|
|
||||||
* whitespace will probably cause you headaches. Example root paths: "akka",
|
|
||||||
* "sbt", "jsoup", "heroku", "mongo", etc.
|
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* The loaded object will already be resolved (substitutions have already
|
* The loaded object will already be resolved (substitutions have already
|
||||||
* been processed). As a result, if you add more fallbacks then they won't
|
* been processed). As a result, if you add more fallbacks then they won't
|
||||||
* be seen by substitutions. Substitutions are the "${foo.bar}" syntax. If
|
* be seen by substitutions. Substitutions are the "${foo.bar}" syntax. If
|
||||||
* you want to parse additional files or something then you need to use
|
* you want to parse additional files or something then you need to use
|
||||||
* loadWithoutResolving().
|
* {@link #load(Config)}.
|
||||||
*
|
*
|
||||||
* @param rootPath
|
* @param resourceBasename
|
||||||
* the configuration "domain"
|
* name (optionally without extension) of a resource on classpath
|
||||||
* @return configuration object for the requested root path
|
* @return configuration for an application
|
||||||
*/
|
*/
|
||||||
public static ConfigRoot load(String rootPath) {
|
public static Config load(String resourceBasename) {
|
||||||
return loadWithoutResolving(rootPath).resolve();
|
return load(resourceBasename, ConfigParseOptions.defaults(),
|
||||||
}
|
ConfigResolveOptions.defaults());
|
||||||
|
|
||||||
public static ConfigRoot load(String rootPath,
|
|
||||||
ConfigParseOptions parseOptions, ConfigResolveOptions resolveOptions) {
|
|
||||||
return loadWithoutResolving(rootPath, parseOptions).resolve(
|
|
||||||
resolveOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like load() but does not resolve the object, so you can go ahead and add
|
* Like {@link #load(String)} but allows you to specify parse and resolve
|
||||||
* more fallbacks and stuff and have them seen by substitutions when you do
|
* options.
|
||||||
* call {@link ConfigRoot.resolve()}.
|
|
||||||
*
|
*
|
||||||
* @param rootPath
|
* <p>
|
||||||
* @return
|
* To be aware of: using
|
||||||
|
* {@link ConfigResolveOptions#setUseSystemProperties(boolean)
|
||||||
|
* setUseSystemProperties(false)} with this method has no meaningful effect,
|
||||||
|
* because the system properties are merged into the config as overrides
|
||||||
|
* anyway. <code>setUseSystemProperties</code> affects whether to fall back
|
||||||
|
* to system properties when they are not found in the config, but with
|
||||||
|
* <code>load()</code>, they will be in the config.
|
||||||
|
*
|
||||||
|
* @param resourceBasename
|
||||||
|
* the classpath resource name with optional extension
|
||||||
|
* @param parseOptions
|
||||||
|
* options to use when parsing the resource
|
||||||
|
* @param resolveOptions
|
||||||
|
* options to use when resolving the stack
|
||||||
|
* @return configuration for an application
|
||||||
*/
|
*/
|
||||||
public static ConfigRoot loadWithoutResolving(String rootPath) {
|
public static Config load(String resourceBasename, ConfigParseOptions parseOptions,
|
||||||
return loadWithoutResolving(rootPath, ConfigParseOptions.defaults());
|
ConfigResolveOptions resolveOptions) {
|
||||||
|
Config appConfig = ConfigFactory.parseResourcesAnySyntax(ConfigFactory.class, "/"
|
||||||
|
+ resourceBasename, parseOptions);
|
||||||
|
return load(appConfig, resolveOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigRoot loadWithoutResolving(String rootPath,
|
/**
|
||||||
ConfigParseOptions options) {
|
* Assembles a standard configuration using a custom <code>Config</code>
|
||||||
ConfigRoot system = systemPropertiesRoot(rootPath);
|
* object rather than loading "application.conf". The <code>Config</code>
|
||||||
|
* object will be sandwiched between the default reference config and
|
||||||
Config mainFiles = parseResourcesForPath(rootPath, options);
|
* default overrides and then resolved.
|
||||||
Config referenceFiles = parseResourcesForPath(rootPath + ".reference",
|
*
|
||||||
options);
|
* @param config
|
||||||
|
* the application's portion of the configuration
|
||||||
return system.withFallback(mainFiles).withFallback(referenceFiles);
|
* @return resolved configuration with overrides and fallbacks added
|
||||||
|
*/
|
||||||
|
public static Config load(Config config) {
|
||||||
|
return load(config, ConfigResolveOptions.defaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigRoot emptyRoot(String rootPath) {
|
/**
|
||||||
return emptyRoot(rootPath, null);
|
* Like {@link #load(Config)} but allows you to specify
|
||||||
|
* {@link ConfigResolveOptions}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To be aware of: using
|
||||||
|
* {@link ConfigResolveOptions#setUseSystemProperties(boolean)
|
||||||
|
* setUseSystemProperties(false)} with this method has no meaningful effect,
|
||||||
|
* because the system properties are merged into the config as overrides
|
||||||
|
* anyway. <code>setUseSystemProperties</code> affects whether to fall back
|
||||||
|
* to system properties when they are not found in the config, but with
|
||||||
|
* <code>load()</code>, they will be in the config.
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* the application's portion of the configuration
|
||||||
|
* @param resolveOptions
|
||||||
|
* options for resolving the assembled config stack
|
||||||
|
* @return resolved configuration with overrides and fallbacks added
|
||||||
|
*/
|
||||||
|
public static Config load(Config config, ConfigResolveOptions resolveOptions) {
|
||||||
|
return defaultOverrides().withFallback(config).withFallback(defaultReference())
|
||||||
|
.resolve(resolveOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class DefaultConfigHolder {
|
||||||
|
static final Config defaultConfig = load("application");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a default configuration, equivalent to {@link #load(String)
|
||||||
|
* load("application")}. This configuration should be used by libraries and
|
||||||
|
* frameworks unless an application provides a different one.
|
||||||
|
* <p>
|
||||||
|
* This method may return a cached singleton.
|
||||||
|
*
|
||||||
|
* @return configuration for an application
|
||||||
|
*/
|
||||||
|
public static Config load() {
|
||||||
|
return DefaultConfigHolder.defaultConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the default reference configuration, which is currently created
|
||||||
|
* by merging all resources "reference.conf" found on the classpath and
|
||||||
|
* overriding the result with system properties. The returned reference
|
||||||
|
* configuration will already have substitutions resolved.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Libraries and frameworks should ship with a "reference.conf" in their
|
||||||
|
* jar.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The {@link #load()} methods merge this configuration for you
|
||||||
|
* automatically.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Future versions may look for reference configuration in more places. It
|
||||||
|
* is not guaranteed that this method <em>only</em> looks at
|
||||||
|
* "reference.conf".
|
||||||
|
*
|
||||||
|
* @return the default reference config
|
||||||
|
*/
|
||||||
|
public static Config defaultReference() {
|
||||||
|
return ConfigImpl.defaultReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the default override configuration, which currently consists of
|
||||||
|
* system properties. The returned override configuration will already have
|
||||||
|
* substitutions resolved.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The {@link #load()} methods merge this configuration for you
|
||||||
|
* automatically.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Future versions may get overrides in more places. It is not guaranteed
|
||||||
|
* that this method <em>only</em> uses system properties.
|
||||||
|
*
|
||||||
|
* @return the default override configuration
|
||||||
|
*/
|
||||||
|
public static Config defaultOverrides() {
|
||||||
|
return systemProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an empty configuration. See also {@link #empty(String)} to create an
|
||||||
|
* empty configuration with a description, which may improve user-visible
|
||||||
|
* error messages.
|
||||||
|
*
|
||||||
|
* @return an empty configuration
|
||||||
|
*/
|
||||||
public static Config empty() {
|
public static Config empty() {
|
||||||
return empty(null);
|
return empty(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigRoot emptyRoot(String rootPath, String originDescription) {
|
/**
|
||||||
return ConfigImpl.emptyRoot(rootPath, originDescription);
|
* Gets an empty configuration with a description to be used to create a
|
||||||
}
|
* {@link ConfigOrigin} for this <code>Config</code>. The description should
|
||||||
|
* be very short and say what the configuration is, like "default settings"
|
||||||
|
* or "foo settings" or something. (Presumably you will merge some actual
|
||||||
|
* settings into this empty config using {@link Config#withFallback}, making
|
||||||
|
* the description more useful.)
|
||||||
|
*
|
||||||
|
* @param originDescription
|
||||||
|
* description of the config
|
||||||
|
* @return an empty configuration
|
||||||
|
*/
|
||||||
public static Config empty(String originDescription) {
|
public static Config empty(String originDescription) {
|
||||||
return ConfigImpl.emptyConfig(originDescription);
|
return ConfigImpl.emptyConfig(originDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigRoot systemPropertiesRoot(String rootPath) {
|
/**
|
||||||
return ConfigImpl.systemPropertiesRoot(rootPath);
|
* Gets a <code>Config</code> containing the system properties from
|
||||||
}
|
* {@link java.lang.System#getProperties()}, parsed and converted as with
|
||||||
|
* {@link #parseProperties}. This method can return a global immutable
|
||||||
|
* singleton, so it's preferred over parsing system properties yourself.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@link #load} will include the system properties as overrides already, as
|
||||||
|
* will {@link #defaultReference} and {@link #defaultOverrides}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Because this returns a singleton, it will not notice changes to system
|
||||||
|
* properties made after the first time this method is called.
|
||||||
|
*
|
||||||
|
* @return system properties parsed into a <code>Config</code>
|
||||||
|
*/
|
||||||
public static Config systemProperties() {
|
public static Config systemProperties() {
|
||||||
return ConfigImpl.systemPropertiesAsConfig();
|
return ConfigImpl.systemPropertiesAsConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a <code>Config</code> containing the system's environment variables.
|
||||||
|
* This method can return a global immutable singleton.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Environment variables are used as fallbacks when resolving substitutions
|
||||||
|
* whether or not this object is included in the config being resolved, so
|
||||||
|
* you probably don't need to use this method for most purposes. It can be a
|
||||||
|
* nicer API for accessing environment variables than raw
|
||||||
|
* {@link java.lang.System#getenv(String)} though, since you can use methods
|
||||||
|
* such as {@link Config#getInt}.
|
||||||
|
*
|
||||||
|
* @return system environment variables parsed into a <code>Config</code>
|
||||||
|
*/
|
||||||
public static Config systemEnvironment() {
|
public static Config systemEnvironment() {
|
||||||
return ConfigImpl.envVariablesAsConfig();
|
return ConfigImpl.envVariablesAsConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a Java Properties object to a ConfigObject using the rules
|
* Converts a Java {@link java.util.Properties} object to a
|
||||||
* documented in https://github.com/havocp/config/blob/master/HOCON.md The
|
* {@link ConfigObject} using the rules documented in the <a
|
||||||
* keys in the Properties object are split on the period character '.' and
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON
|
||||||
* treated as paths. The values will all end up as string values. If you
|
* spec</a>. The keys in the <code>Properties</code> object are split on the
|
||||||
* have both "a=foo" and "a.b=bar" in your properties file, so "a" is both
|
* period character '.' and treated as paths. The values will all end up as
|
||||||
* the object containing "b" and the string "foo", then the string value is
|
* string values. If you have both "a=foo" and "a.b=bar" in your properties
|
||||||
* dropped.
|
* file, so "a" is both the object containing "b" and the string "foo", then
|
||||||
|
* the string value is dropped.
|
||||||
*
|
*
|
||||||
* If you want to get System.getProperties() as a ConfigObject, it's better
|
* <p>
|
||||||
* to use the systemProperties() or systemPropertiesRoot() methods. Those
|
* If you want to have <code>System.getProperties()</code> as a
|
||||||
* methods are able to use a cached global singleton ConfigObject for the
|
* ConfigObject, it's better to use the {@link #systemProperties()} method
|
||||||
* system properties.
|
* which returns a cached global singleton.
|
||||||
*
|
*
|
||||||
* @param properties
|
* @param properties
|
||||||
* a Java Properties object
|
* a Java Properties object
|
||||||
* @param options
|
* @param options
|
||||||
* @return
|
* @return the parsed configuration
|
||||||
*/
|
*/
|
||||||
public static Config parseProperties(Properties properties,
|
public static Config parseProperties(Properties properties,
|
||||||
ConfigParseOptions options) {
|
ConfigParseOptions options) {
|
||||||
return Parseable.newProperties(properties, options).parse().toConfig();
|
return Parseable.newProperties(properties, options).parse().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config parseProperties(Properties properties) {
|
||||||
|
return parseProperties(properties, ConfigParseOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
public static Config parseReader(Reader reader, ConfigParseOptions options) {
|
public static Config parseReader(Reader reader, ConfigParseOptions options) {
|
||||||
return Parseable.newReader(reader, options).parse().toConfig();
|
return Parseable.newReader(reader, options).parse().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config parseReader(Reader reader) {
|
||||||
|
return parseReader(reader, ConfigParseOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
public static Config parseURL(URL url, ConfigParseOptions options) {
|
public static Config parseURL(URL url, ConfigParseOptions options) {
|
||||||
return Parseable.newURL(url, options).parse().toConfig();
|
return Parseable.newURL(url, options).parse().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config parseURL(URL url) {
|
||||||
|
return parseURL(url, ConfigParseOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
public static Config parseFile(File file, ConfigParseOptions options) {
|
public static Config parseFile(File file, ConfigParseOptions options) {
|
||||||
return Parseable.newFile(file, options).parse().toConfig();
|
return Parseable.newFile(file, options).parse().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config parseFile(File file) {
|
||||||
|
return parseFile(file, ConfigParseOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a file. If the fileBasename already ends in a known extension,
|
* Parses a file with a flexible extension. If the <code>fileBasename</code>
|
||||||
* just parses it according to that extension. If the fileBasename does not
|
* already ends in a known extension, this method parses it according to
|
||||||
* end in an extension, then parse all known extensions and merge whatever
|
* that extension (the file's syntax must match its extension). If the
|
||||||
* is found. If options force a specific syntax, only parse files with an
|
* <code>fileBasename</code> does not end in an extension, it parses files
|
||||||
* extension matching that syntax. If options.getAllowMissing() is true,
|
* with all known extensions and merges whatever is found.
|
||||||
* then no files have to exist; if false, then at least one file has to
|
*
|
||||||
* exist.
|
* <p>
|
||||||
|
* In the current implementation, the extension ".conf" forces
|
||||||
|
* {@link ConfigSyntax#CONF}, ".json" forces {@link ConfigSyntax#JSON}, and
|
||||||
|
* ".properties" forces {@link ConfigSyntax#PROPERTIES}. When merging files,
|
||||||
|
* ".conf" falls back to ".json" falls back to ".properties".
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Future versions of the implementation may add additional syntaxes or
|
||||||
|
* additional extensions. However, the ordering (fallback priority) of the
|
||||||
|
* three current extensions will remain the same.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If <code>options</code> forces a specific syntax, this method only parses
|
||||||
|
* files with an extension matching that syntax.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If {@link ConfigParseOptions#getAllowMissing options.getAllowMissing()}
|
||||||
|
* is true, then no files have to exist; if false, then at least one file
|
||||||
|
* has to exist.
|
||||||
*
|
*
|
||||||
* @param fileBasename
|
* @param fileBasename
|
||||||
|
* a filename with or without extension
|
||||||
* @param options
|
* @param options
|
||||||
* @return
|
* parse options
|
||||||
|
* @return the parsed configuration
|
||||||
*/
|
*/
|
||||||
public static Config parseFileAnySyntax(File fileBasename,
|
public static Config parseFileAnySyntax(File fileBasename,
|
||||||
ConfigParseOptions options) {
|
ConfigParseOptions options) {
|
||||||
return ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig();
|
return ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Config parseResource(Class<?> klass, String resource,
|
public static Config parseFileAnySyntax(File fileBasename) {
|
||||||
ConfigParseOptions options) {
|
return parseFileAnySyntax(fileBasename, ConfigParseOptions.defaults());
|
||||||
return Parseable.newResource(klass, resource, options).parse()
|
|
||||||
.toConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same behavior as parseFileAnySyntax() but for classpath resources
|
* Parses all resources on the classpath with the given name and merges them
|
||||||
* instead.
|
* into a single <code>Config</code>.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the resource name does not begin with a "/", it will have the supplied
|
||||||
|
* class's package added to it, in the same way as
|
||||||
|
* {@link java.lang.Class#getResource}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Duplicate resources with the same name are merged such that ones returned
|
||||||
|
* earlier from {@link ClassLoader#getResources} fall back to (have higher
|
||||||
|
* priority than) the ones returned later. This implies that resources
|
||||||
|
* earlier in the classpath override those later in the classpath when they
|
||||||
|
* configure the same setting. However, in practice real applications may
|
||||||
|
* not be consistent about classpath ordering, so be careful. It may be best
|
||||||
|
* to avoid assuming too much.
|
||||||
*
|
*
|
||||||
* @param klass
|
* @param klass
|
||||||
* @param resourceBasename
|
* <code>klass.getClassLoader()</code> will be used to load
|
||||||
|
* resources, and non-absolute resource names will have this
|
||||||
|
* class's package added
|
||||||
|
* @param resource
|
||||||
|
* resource to look up, relative to <code>klass</code>'s package
|
||||||
|
* or absolute starting with a "/"
|
||||||
* @param options
|
* @param options
|
||||||
* @return
|
* parse options
|
||||||
|
* @return the parsed configuration
|
||||||
*/
|
*/
|
||||||
public static Config parseResourceAnySyntax(Class<?> klass, String resourceBasename,
|
public static Config parseResources(Class<?> klass, String resource,
|
||||||
ConfigParseOptions options) {
|
ConfigParseOptions options) {
|
||||||
return ConfigImpl.parseResourceAnySyntax(klass, resourceBasename,
|
return Parseable.newResources(klass, resource, options).parse()
|
||||||
|
.toConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config parseResources(Class<?> klass, String resource) {
|
||||||
|
return parseResources(klass, resource, ConfigParseOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses classpath resources with a flexible extension. In general, this
|
||||||
|
* method has the same behavior as
|
||||||
|
* {@link #parseFileAnySyntax(File,ConfigParseOptions)} but for classpath
|
||||||
|
* resources instead, as in {@link #parseResources}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* There is a thorny problem with this method, which is that
|
||||||
|
* {@link java.lang.ClassLoader#getResources} must be called separately for
|
||||||
|
* each possible extension. The implementation ends up with separate lists
|
||||||
|
* of resources called "basename.conf" and "basename.json" for example. As a
|
||||||
|
* result, the ideal ordering between two files with different extensions is
|
||||||
|
* unknown; there is no way to figure out how to merge the two lists in
|
||||||
|
* classpath order. To keep it simple, the lists are simply concatenated,
|
||||||
|
* with the same syntax priorities as
|
||||||
|
* {@link #parseFileAnySyntax(File,ConfigParseOptions) parseFileAnySyntax()}
|
||||||
|
* - all ".conf" resources are ahead of all ".json" resources which are
|
||||||
|
* ahead of all ".properties" resources.
|
||||||
|
*
|
||||||
|
* @param klass
|
||||||
|
* class which determines the <code>ClassLoader</code> and the
|
||||||
|
* package for relative resource names
|
||||||
|
* @param resourceBasename
|
||||||
|
* a resource name as in {@link java.lang.Class#getResource},
|
||||||
|
* with or without extension
|
||||||
|
* @param options
|
||||||
|
* parse options
|
||||||
|
* @return the parsed configuration
|
||||||
|
*/
|
||||||
|
public static Config parseResourcesAnySyntax(Class<?> klass, String resourceBasename,
|
||||||
|
ConfigParseOptions options) {
|
||||||
|
return ConfigImpl.parseResourcesAnySyntax(klass, resourceBasename,
|
||||||
options).toConfig();
|
options).toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config parseResourcesAnySyntax(Class<?> klass, String resourceBasename) {
|
||||||
|
return parseResourcesAnySyntax(klass, resourceBasename, ConfigParseOptions.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
public static Config parseString(String s, ConfigParseOptions options) {
|
public static Config parseString(String s, ConfigParseOptions options) {
|
||||||
return Parseable.newString(s, options).parse().toConfig();
|
return Parseable.newString(s, options).parse().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static Config parseString(String s) {
|
||||||
* Parses classpath resources corresponding to this path expression.
|
return parseString(s, ConfigParseOptions.defaults());
|
||||||
* Essentially if the path is "foo.bar" then the resources are
|
|
||||||
* "/foo-bar.conf", "/foo-bar.json", and "/foo-bar.properties". If more than
|
|
||||||
* one of those exists, they are merged.
|
|
||||||
*
|
|
||||||
* @param path
|
|
||||||
* @param options
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static Config parseResourcesForPath(String rootPath,
|
|
||||||
ConfigParseOptions options) {
|
|
||||||
// null originDescription is allowed in parseResourcesForPath
|
|
||||||
return ConfigImpl.parseResourcesForPath(rootPath, options).toConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to ConfigValueFactory.fromMap(), but the keys in the map are path
|
* Creates a {@code Config} based on a {@link java.util.Map} from paths to
|
||||||
* expressions, rather than keys; and correspondingly it returns a Config
|
* plain Java values. Similar to
|
||||||
* instead of a ConfigObject. This is more convenient if you are writing
|
* {@link ConfigValueFactory#fromMap(Map,String)}, except the keys in the
|
||||||
* literal maps in code, and less convenient if you are getting your maps
|
* map are path expressions, rather than keys; and correspondingly it
|
||||||
* from some data source such as a parser.
|
* returns a {@code Config} instead of a {@code ConfigObject}. This is more
|
||||||
|
* convenient if you are writing literal maps in code, and less convenient
|
||||||
|
* if you are getting your maps from some data source such as a parser.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* An exception will be thrown (and it is a bug in the caller of the method)
|
* An exception will be thrown (and it is a bug in the caller of the method)
|
||||||
* if a path is both an object and a value, for example if you had both
|
* if a path is both an object and a value, for example if you had both
|
||||||
* "a=foo" and "a.b=bar", then "a" is both the string "foo" and the parent
|
* "a=foo" and "a.b=bar", then "a" is both the string "foo" and the parent
|
||||||
|
|
@ -221,7 +448,7 @@ public final class ConfigFactory {
|
||||||
* description of what this map represents, like a filename, or
|
* description of what this map represents, like a filename, or
|
||||||
* "default settings" (origin description is used in error
|
* "default settings" (origin description is used in error
|
||||||
* messages)
|
* messages)
|
||||||
* @return
|
* @return the map converted to a {@code Config}
|
||||||
*/
|
*/
|
||||||
public static Config parseMap(Map<String, ? extends Object> values,
|
public static Config parseMap(Map<String, ? extends Object> values,
|
||||||
String originDescription) {
|
String originDescription) {
|
||||||
|
|
@ -229,11 +456,11 @@ public final class ConfigFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the other overload of parseMap() for details, this one just uses a
|
* See the other overload of {@link #parseMap(Map, String)} for details,
|
||||||
* default origin description.
|
* this one just uses a default origin description.
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values
|
||||||
* @return
|
* @return the map converted to a {@code Config}
|
||||||
*/
|
*/
|
||||||
public static Config parseMap(Map<String, ? extends Object> values) {
|
public static Config parseMap(Map<String, ? extends Object> values) {
|
||||||
return parseMap(values, null);
|
return parseMap(values, null);
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ package com.typesafe.config;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ConfigIncludeContext is passed to a ConfigIncluder. This interface is not
|
* Context provided to a {@link ConfigIncluder}; this interface is only useful
|
||||||
* intended for apps to implement.
|
* inside a {@code ConfigIncluder} implementation, and is not intended for apps
|
||||||
|
* to implement.
|
||||||
*/
|
*/
|
||||||
public interface ConfigIncludeContext {
|
public interface ConfigIncludeContext {
|
||||||
/**
|
/**
|
||||||
|
|
@ -15,7 +16,7 @@ public interface ConfigIncludeContext {
|
||||||
* null if it can't meaningfully create a relative name. The returned
|
* null if it can't meaningfully create a relative name. The returned
|
||||||
* parseable may not exist; this function is not required to do any IO, just
|
* parseable may not exist; this function is not required to do any IO, just
|
||||||
* compute what the name would be.
|
* compute what the name would be.
|
||||||
*
|
*
|
||||||
* The passed-in filename has to be a complete name (with extension), not
|
* The passed-in filename has to be a complete name (with extension), not
|
||||||
* just a basename. (Include statements in config files are allowed to give
|
* just a basename. (Include statements in config files are allowed to give
|
||||||
* just a basename.)
|
* just a basename.)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface you have to implement to customize "include" statements in config
|
* Implement this interface and provide an instance to
|
||||||
* files.
|
* {@link ConfigParseOptions#setIncluder ConfigParseOptions.setIncluder()} to
|
||||||
|
* customize handling of {@code include} statements in config files.
|
||||||
*/
|
*/
|
||||||
public interface ConfigIncluder {
|
public interface ConfigIncluder {
|
||||||
/**
|
/**
|
||||||
|
|
@ -29,7 +30,7 @@ public interface ConfigIncluder {
|
||||||
* Parses another item to be included. The returned object typically would
|
* Parses another item to be included. The returned object typically would
|
||||||
* not have substitutions resolved. You can throw a ConfigException here to
|
* not have substitutions resolved. You can throw a ConfigException here to
|
||||||
* abort parsing, or return an empty object, but may not return null.
|
* abort parsing, or return an empty object, but may not return null.
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* some info about the include context
|
* some info about the include context
|
||||||
* @param what
|
* @param what
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,30 @@ package com.typesafe.config;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list (aka array) value corresponding to ConfigValueType.LIST or JSON's
|
* Subtype of {@link ConfigValue} representing a list value, as in JSON's
|
||||||
* "[1,2,3]" value. Implements java.util.List<ConfigValue> so you can use it
|
* {@code [1,2,3]} syntax.
|
||||||
* like a regular Java list.
|
*
|
||||||
|
* <p>
|
||||||
|
* {@code ConfigList} implements {@code java.util.List<ConfigValue>} so you can
|
||||||
|
* use it like a regular Java list. Or call {@link #unwrapped()} to unwrap the
|
||||||
|
* list elements into plain Java values.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Like all {@link ConfigValue} subtypes, {@code ConfigList} is immutable. This
|
||||||
|
* makes it threadsafe and you never have to create "defensive copies." The
|
||||||
|
* mutator methods from {@link java.util.List} all throw
|
||||||
|
* {@link java.lang.UnsupportedOperationException}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The {@link ConfigValue#valueType} method on a list returns
|
||||||
|
* {@link ConfigValueType#LIST}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement {@code ConfigList}</em>; it should only be implemented
|
||||||
|
* by the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface ConfigList extends List<ConfigValue>, ConfigValue {
|
public interface ConfigList extends List<ConfigValue>, ConfigValue {
|
||||||
|
|
|
||||||
|
|
@ -4,33 +4,48 @@
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a marker for types that can be merged as a fallback into a Config or
|
* Marker for types whose instances can be merged, that is {@link Config} and
|
||||||
* a ConfigValue. Both Config and ConfigValue are mergeable.
|
* {@link ConfigValue}. Instances of {@code Config} and {@code ConfigValue} can
|
||||||
|
* be combined into a single new instance using the
|
||||||
|
* {@link ConfigMergeable#withFallback withFallback()} method.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement this interface</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
*/
|
*/
|
||||||
public interface ConfigMergeable {
|
public interface ConfigMergeable {
|
||||||
/**
|
/**
|
||||||
* Converts the mergeable to a ConfigValue to be merged.
|
* Converts this instance to a {@link ConfigValue}. If called on a
|
||||||
|
* {@code ConfigValue} it returns {@code this}, if called on a
|
||||||
|
* {@link Config} it's equivalent to {@link Config#root()}.
|
||||||
*
|
*
|
||||||
* @return
|
* @return this instance as a {@code ConfigValue}
|
||||||
*/
|
*/
|
||||||
ConfigValue toValue();
|
ConfigValue toValue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new value computed by merging this value with another, with
|
* Returns a new value computed by merging this value with another, with
|
||||||
* keys in this value "winning" over the other one. Only ConfigObject and
|
* keys in this value "winning" over the other one. Only
|
||||||
* Config instances do anything in this method (they need to merge the
|
* {@link ConfigObject} and {@link Config} instances do anything in this
|
||||||
* fallback keys into themselves). All other values just return the original
|
* method (they need to merge the fallback keys into themselves). All other
|
||||||
* value, since they automatically override any fallback.
|
* values just return the original value, since they automatically override
|
||||||
*
|
* any fallback.
|
||||||
* The semantics of merging are described in
|
*
|
||||||
* https://github.com/havocp/config/blob/master/HOCON.md
|
* <p>
|
||||||
*
|
* The semantics of merging are described in the <a
|
||||||
* Note that objects do not merge "across" non-objects; if you do
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">spec for
|
||||||
|
* HOCON</a>.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that objects do not merge "across" non-objects; if you write
|
||||||
* <code>object.withFallback(nonObject).withFallback(otherObject)</code>,
|
* <code>object.withFallback(nonObject).withFallback(otherObject)</code>,
|
||||||
* then <code>otherObject</code> will simply be ignored. This is an
|
* then <code>otherObject</code> will simply be ignored. This is an
|
||||||
* intentional part of how merging works. Both non-objects, and any object
|
* intentional part of how merging works. Both non-objects, and any object
|
||||||
* which has fallen back to a non-object, block subsequent fallbacks.
|
* which has fallen back to a non-object, block subsequent fallbacks.
|
||||||
*
|
*
|
||||||
* @param other
|
* @param other
|
||||||
* an object whose keys should be used if the keys are not
|
* an object whose keys should be used if the keys are not
|
||||||
* present in this one
|
* present in this one
|
||||||
|
|
|
||||||
|
|
@ -6,54 +6,67 @@ package com.typesafe.config;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ConfigObject is a read-only configuration object, which may have nested
|
* Subtype of {@link ConfigValue} representing an object (dictionary, map)
|
||||||
* child objects. Implementations of ConfigObject should be immutable (at least
|
* value, as in JSON's <code>{ "a" : 42 }</code> syntax.
|
||||||
* from the perspective of anyone using this interface) and thus thread-safe.
|
|
||||||
*
|
*
|
||||||
* In most cases you want to use the Config interface rather than this one. Call
|
* <p>
|
||||||
* toConfig() to convert a ConfigObject to a config.
|
* {@code ConfigObject} implements {@code java.util.Map<String, ConfigValue>} so
|
||||||
|
* you can use it like a regular Java map. Or call {@link #unwrapped()} to
|
||||||
|
* unwrap the map to a map with plain Java values rather than
|
||||||
|
* {@code ConfigValue}.
|
||||||
*
|
*
|
||||||
* The API for a ConfigObject is in terms of keys, while the API for a Config is
|
* <p>
|
||||||
* in terms of path expressions. Conceptually, ConfigObject is a tree of maps
|
* Like all {@link ConfigValue} subtypes, {@code ConfigObject} is immutable.
|
||||||
* from keys to values, while a ConfigObject is a one-level map from paths to
|
* This makes it threadsafe and you never have to create "defensive copies." The
|
||||||
* values.
|
* mutator methods from {@link java.util.Map} all throw
|
||||||
|
* {@link java.lang.UnsupportedOperationException}.
|
||||||
*
|
*
|
||||||
* Throughout the API, there is a distinction between "keys" and "paths". A key
|
* <p>
|
||||||
* is a key in a JSON object; it's just a string that's the key in a map. A
|
* The {@link ConfigValue#valueType} method on an object returns
|
||||||
* "path" is a parseable expression with a syntax and it refers to a series of
|
* {@link ConfigValueType#OBJECT}.
|
||||||
* keys. A path is used to traverse nested ConfigObject by looking up each key
|
|
||||||
* in the path. Path expressions are described in the spec for "HOCON", which
|
|
||||||
* can be found at https://github.com/havocp/config/blob/master/HOCON.md; in
|
|
||||||
* brief, a path is period-separated so "a.b.c" looks for key c in object b in
|
|
||||||
* object a in the root object. Sometimes double quotes are needed around
|
|
||||||
* special characters in path expressions.
|
|
||||||
*
|
*
|
||||||
* ConfigObject implements java.util.Map<String,ConfigValue> and all methods
|
* <p>
|
||||||
* work with keys, not path expressions.
|
* In most cases you want to use the {@link Config} interface rather than this
|
||||||
|
* one. Call {@link #toConfig()} to convert a {@code ConfigObject} to a
|
||||||
|
* {@code Config}.
|
||||||
*
|
*
|
||||||
* While ConfigObject implements the standard Java Map interface, the mutator
|
* <p>
|
||||||
* methods all throw UnsupportedOperationException. This Map is immutable.
|
* The API for a {@code ConfigObject} is in terms of keys, while the API for a
|
||||||
|
* {@link Config} is in terms of path expressions. Conceptually,
|
||||||
|
* {@code ConfigObject} is a tree of maps from keys to values, while a
|
||||||
|
* {@code ConfigObject} is a one-level map from paths to values.
|
||||||
*
|
*
|
||||||
* The Map may contain null values, which will have ConfigValue.valueType() ==
|
* <p>
|
||||||
* ConfigValueType.NULL. If get() returns Java's null then the key was not
|
* A {@code ConfigObject} may contain null values, which will have
|
||||||
* present in the parsed file (or wherever this value tree came from). If get()
|
* {@link ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If
|
||||||
* returns a ConfigValue with type ConfigValueType.NULL then the key was set to
|
* {@code get()} returns Java's null then the key was not present in the parsed
|
||||||
* null explicitly.
|
* file (or wherever this value tree came from). If {@code get()} returns a
|
||||||
|
* {@link ConfigValue} with type {@code ConfigValueType#NULL} then the key was
|
||||||
|
* set to null explicitly in the config file.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement {@code ConfigObject}</em>; it should only be implemented
|
||||||
|
* by the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
*/
|
*/
|
||||||
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this object to a Config instance, enabling you to use path
|
* Converts this object to a {@link Config} instance, enabling you to use
|
||||||
* expressions to find values in the object. This is a constant-time
|
* path expressions to find values in the object. This is a constant-time
|
||||||
* operation (it is not proportional to the size of the object).
|
* operation (it is not proportional to the size of the object).
|
||||||
*
|
*
|
||||||
* @return
|
* @return a {@link Config} with this object as its root
|
||||||
*/
|
*/
|
||||||
Config toConfig();
|
Config toConfig();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively unwraps the object, returning a map from String to whatever
|
* Recursively unwraps the object, returning a map from String to whatever
|
||||||
* plain Java values are unwrapped from the object's values.
|
* plain Java values are unwrapped from the object's values.
|
||||||
|
*
|
||||||
|
* @return a {@link java.util.Map} containing plain Java objects
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
Map<String, Object> unwrapped();
|
Map<String, Object> unwrapped();
|
||||||
|
|
@ -62,10 +75,15 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
|
||||||
ConfigObject withFallback(ConfigMergeable other);
|
ConfigObject withFallback(ConfigMergeable other);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a ConfigValue at the given key, or returns null if there is no
|
* Gets a {@link ConfigValue} at the given key, or returns null if there is
|
||||||
* value. The returned ConfigValue may have ConfigValueType.NULL or any
|
* no value. The returned {@link ConfigValue} may have
|
||||||
* other type, and the passed-in key must be a key in this object, rather
|
* {@link ConfigValueType#NULL} or any other type, and the passed-in key
|
||||||
* than a path expression.
|
* must be a key in this object, rather than a path expression.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* key to look up
|
||||||
|
*
|
||||||
|
* @return the value at the key or null if none
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
ConfigValue get(Object key);
|
ConfigValue get(Object key);
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,67 @@
|
||||||
*/
|
*/
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConfigOrigin is used to track the origin (such as filename and line number)
|
* Represents the origin (such as filename and line number) of a
|
||||||
* of a ConfigValue or other object. The origin is used in error messages.
|
* {@link ConfigValue} for use in error messages. Obtain the origin of a value
|
||||||
|
* with {@link ConfigValue#origin}. Exceptions may have an origin, see
|
||||||
|
* {@link ConfigException#origin}, but be careful because
|
||||||
|
* <code>ConfigException.origin()</code> may return null.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It's best to use this interface only for debugging; its accuracy is
|
||||||
|
* "best effort" rather than guaranteed, and a potentially-noticeable amount of
|
||||||
|
* memory could probably be saved if origins were not kept around, so in the
|
||||||
|
* future there might be some option to discard origins.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement this interface</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
*/
|
*/
|
||||||
public interface ConfigOrigin {
|
public interface ConfigOrigin {
|
||||||
|
/**
|
||||||
|
* Returns a string describing the origin of a value or exception. This will
|
||||||
|
* never return null.
|
||||||
|
*
|
||||||
|
* @return string describing the origin
|
||||||
|
*/
|
||||||
public String description();
|
public String description();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a filename describing the origin. This will return null if the
|
||||||
|
* origin was not a file.
|
||||||
|
*
|
||||||
|
* @return filename of the origin or null
|
||||||
|
*/
|
||||||
|
public String filename();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a URL describing the origin. This will return null if the origin
|
||||||
|
* has no meaningful URL.
|
||||||
|
*
|
||||||
|
* @return url of the origin or null
|
||||||
|
*/
|
||||||
|
public URL url();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a classpath resource name describing the origin. This will return
|
||||||
|
* null if the origin was not a classpath resource.
|
||||||
|
*
|
||||||
|
* @return resource name of the origin or null
|
||||||
|
*/
|
||||||
|
public String resource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a line number where the value or exception originated. This will
|
||||||
|
* return -1 if there's no meaningful line number.
|
||||||
|
*
|
||||||
|
* @return line number or -1 if none is available
|
||||||
|
*/
|
||||||
|
public int lineNumber();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,22 @@
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of options related to parsing.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This object is immutable, so the "setters" return a new object.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Here is an example of creating a custom {@code ConfigParseOptions}:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigParseOptions options = ConfigParseOptions.defaults()
|
||||||
|
* .setSyntax(ConfigSyntax.JSON)
|
||||||
|
* .setAllowMissing(false)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
public final class ConfigParseOptions {
|
public final class ConfigParseOptions {
|
||||||
final ConfigSyntax syntax;
|
final ConfigSyntax syntax;
|
||||||
final String originDescription;
|
final String originDescription;
|
||||||
|
|
@ -24,10 +40,11 @@ public final class ConfigParseOptions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the file format. If set to null, try to guess from any available
|
* Set the file format. If set to null, try to guess from any available
|
||||||
* filename extension; if guessing fails, assume ConfigSyntax.CONF.
|
* filename extension; if guessing fails, assume {@link ConfigSyntax#CONF}.
|
||||||
*
|
*
|
||||||
* @param syntax
|
* @param syntax
|
||||||
* @return
|
* a syntax or {@code null} for best guess
|
||||||
|
* @return options with the syntax set
|
||||||
*/
|
*/
|
||||||
public ConfigParseOptions setSyntax(ConfigSyntax syntax) {
|
public ConfigParseOptions setSyntax(ConfigSyntax syntax) {
|
||||||
if (this.syntax == syntax)
|
if (this.syntax == syntax)
|
||||||
|
|
@ -45,10 +62,11 @@ public final class ConfigParseOptions {
|
||||||
* Set a description for the thing being parsed. In most cases this will be
|
* Set a description for the thing being parsed. In most cases this will be
|
||||||
* set up for you to something like the filename, but if you provide just an
|
* set up for you to something like the filename, but if you provide just an
|
||||||
* input stream you might want to improve on it. Set to null to allow the
|
* input stream you might want to improve on it. Set to null to allow the
|
||||||
* library to come up with something automatically.
|
* library to come up with something automatically. This description is the
|
||||||
|
* basis for the {@link ConfigOrigin} of the parsed values.
|
||||||
*
|
*
|
||||||
* @param originDescription
|
* @param originDescription
|
||||||
* @return
|
* @return options with the origin description set
|
||||||
*/
|
*/
|
||||||
public ConfigParseOptions setOriginDescription(String originDescription) {
|
public ConfigParseOptions setOriginDescription(String originDescription) {
|
||||||
if (this.originDescription == originDescription)
|
if (this.originDescription == originDescription)
|
||||||
|
|
@ -79,7 +97,7 @@ public final class ConfigParseOptions {
|
||||||
* case.
|
* case.
|
||||||
*
|
*
|
||||||
* @param allowMissing
|
* @param allowMissing
|
||||||
* @return
|
* @return options with the "allow missing" flag set
|
||||||
*/
|
*/
|
||||||
public ConfigParseOptions setAllowMissing(boolean allowMissing) {
|
public ConfigParseOptions setAllowMissing(boolean allowMissing) {
|
||||||
if (this.allowMissing == allowMissing)
|
if (this.allowMissing == allowMissing)
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,40 @@
|
||||||
*/
|
*/
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
/** An opaque handle to something that can be parsed. */
|
/**
|
||||||
|
* An opaque handle to something that can be parsed, obtained from
|
||||||
|
* {@link ConfigIncludeContext}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement this interface</em>; it should only be implemented by
|
||||||
|
* the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
|
*/
|
||||||
public interface ConfigParseable {
|
public interface ConfigParseable {
|
||||||
/**
|
/**
|
||||||
* Parse whatever it is.
|
* Parse whatever it is. The options should come from
|
||||||
*
|
* {@link ConfigParseable#options options()} but you could tweak them if you
|
||||||
|
* like.
|
||||||
|
*
|
||||||
* @param options
|
* @param options
|
||||||
* parse options, should be based on the ones from options()
|
* parse options, should be based on the ones from
|
||||||
|
* {@link ConfigParseable#options options()}
|
||||||
*/
|
*/
|
||||||
ConfigObject parse(ConfigParseOptions options);
|
ConfigObject parse(ConfigParseOptions options);
|
||||||
|
|
||||||
/** Possibly return a URL representing the resource; this may return null. */
|
/**
|
||||||
URL url();
|
* Returns a {@link ConfigOrigin} describing the origin of the parseable
|
||||||
|
* item.
|
||||||
|
*/
|
||||||
|
ConfigOrigin origin();
|
||||||
|
|
||||||
/** Get the initial options, which can be modified then passed to parse(). */
|
/**
|
||||||
|
* Get the initial options, which can be modified then passed to parse().
|
||||||
|
* These options will have the right description, includer, and other
|
||||||
|
* parameters already set up.
|
||||||
|
*/
|
||||||
ConfigParseOptions options();
|
ConfigParseOptions options();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,28 @@
|
||||||
*/
|
*/
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of options related to resolving substitutions. Substitutions use the
|
||||||
|
* <code>${foo.bar}</code> syntax and are documented in the <a
|
||||||
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON</a> spec.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This object is immutable, so the "setters" return a new object.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Here is an example of creating a custom {@code ConfigResolveOptions}:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigResolveOptions options = ConfigResolveOptions.defaults()
|
||||||
|
* .setUseSystemProperties(false)
|
||||||
|
* .setUseSystemEnvironment(false)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* In addition to {@link ConfigResolveOptions#defaults}, there's a prebuilt
|
||||||
|
* {@link ConfigResolveOptions#noSystem} which avoids looking at any system
|
||||||
|
* properties or environment variables.
|
||||||
|
*/
|
||||||
public final class ConfigResolveOptions {
|
public final class ConfigResolveOptions {
|
||||||
private final boolean useSystemProperties;
|
private final boolean useSystemProperties;
|
||||||
private final boolean useSystemEnvironment;
|
private final boolean useSystemEnvironment;
|
||||||
|
|
@ -13,26 +35,68 @@ public final class ConfigResolveOptions {
|
||||||
this.useSystemEnvironment = useSystemEnvironment;
|
this.useSystemEnvironment = useSystemEnvironment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default resolve options.
|
||||||
|
*
|
||||||
|
* @return the default resolve options
|
||||||
|
*/
|
||||||
public static ConfigResolveOptions defaults() {
|
public static ConfigResolveOptions defaults() {
|
||||||
return new ConfigResolveOptions(true, true);
|
return new ConfigResolveOptions(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns resolve options that disable any reference to "system" data
|
||||||
|
* (system properties or environment variables).
|
||||||
|
*
|
||||||
|
* @return the resolve options with system properties and env variables
|
||||||
|
* disabled
|
||||||
|
*/
|
||||||
public static ConfigResolveOptions noSystem() {
|
public static ConfigResolveOptions noSystem() {
|
||||||
return new ConfigResolveOptions(false, false);
|
return defaults().setUseSystemEnvironment(false).setUseSystemProperties(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with use of Java system properties set to the given
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to resolve substitutions falling back to Java system
|
||||||
|
* properties.
|
||||||
|
* @return options with requested setting for use of system properties
|
||||||
|
*/
|
||||||
public ConfigResolveOptions setUseSystemProperties(boolean value) {
|
public ConfigResolveOptions setUseSystemProperties(boolean value) {
|
||||||
return new ConfigResolveOptions(value, useSystemEnvironment);
|
return new ConfigResolveOptions(value, useSystemEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options with use of environment variables set to the given value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true to resolve substitutions falling back to environment
|
||||||
|
* variables.
|
||||||
|
* @return options with requested setting for use of environment variables
|
||||||
|
*/
|
||||||
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
|
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
|
||||||
return new ConfigResolveOptions(useSystemProperties, value);
|
return new ConfigResolveOptions(useSystemProperties, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options enable use of system properties. This method
|
||||||
|
* is mostly used by the config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return true if system properties should be used
|
||||||
|
*/
|
||||||
public boolean getUseSystemProperties() {
|
public boolean getUseSystemProperties() {
|
||||||
return useSystemProperties;
|
return useSystemProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the options enable use of system environment variables.
|
||||||
|
* This method is mostly used by the config lib internally, not by
|
||||||
|
* applications.
|
||||||
|
*
|
||||||
|
* @return true if environment variables should be used
|
||||||
|
*/
|
||||||
public boolean getUseSystemEnvironment() {
|
public boolean getUseSystemEnvironment() {
|
||||||
return useSystemEnvironment;
|
return useSystemEnvironment;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
|
|
||||||
*/
|
|
||||||
package com.typesafe.config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A root object. The only special thing about a root object is that you can
|
|
||||||
* resolve substitutions against it. So it can have a resolve() method that
|
|
||||||
* doesn't require you to pass in an object to resolve against.
|
|
||||||
*/
|
|
||||||
public interface ConfigRoot extends Config {
|
|
||||||
/**
|
|
||||||
* Returns a replacement root object with all substitutions (the
|
|
||||||
* "${foo.bar}" syntax) resolved. Substitutions are looked up in this root
|
|
||||||
* object. A configuration value tree must be resolved before you can use
|
|
||||||
* it. This method uses ConfigResolveOptions.defaults().
|
|
||||||
*
|
|
||||||
* @return an immutable object with substitutions resolved
|
|
||||||
*/
|
|
||||||
ConfigRoot resolve();
|
|
||||||
|
|
||||||
ConfigRoot resolve(ConfigResolveOptions options);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ConfigRoot withFallback(ConfigMergeable fallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the global app name that this root represents.
|
|
||||||
*
|
|
||||||
* @return the app's root config path
|
|
||||||
*/
|
|
||||||
String rootPath();
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,30 @@
|
||||||
*/
|
*/
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The syntax of a character stream, <a href="http://json.org">JSON</a>, <a
|
||||||
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON</a> aka
|
||||||
|
* ".conf", or <a href=
|
||||||
|
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
||||||
|
* >Java properties</a>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
public enum ConfigSyntax {
|
public enum ConfigSyntax {
|
||||||
JSON, CONF, PROPERTIES;
|
/**
|
||||||
|
* Pedantically strict <a href="http://json.org">JSON</a> format; no
|
||||||
|
* comments, no unexpected commas, no duplicate keys in the same object.
|
||||||
|
*/
|
||||||
|
JSON,
|
||||||
|
/**
|
||||||
|
* The JSON-superset <a
|
||||||
|
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON</a>
|
||||||
|
* format.
|
||||||
|
*/
|
||||||
|
CONF,
|
||||||
|
/**
|
||||||
|
* Standard <a href=
|
||||||
|
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
||||||
|
* >Java properties</a> format.
|
||||||
|
*/
|
||||||
|
PROPERTIES;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,32 +4,57 @@
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface implemented by any configuration value. From the perspective of
|
* An immutable value, following the <a href="http://json.org">JSON</a> type
|
||||||
* users of this interface, the object is immutable. It is therefore safe to use
|
* schema.
|
||||||
* from multiple threads.
|
*
|
||||||
|
* <p>
|
||||||
|
* Because this object is immutable, it is safe to use from multiple threads and
|
||||||
|
* there's no need for "defensive copies."
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <em>Do not implement {@code ConfigValue}</em>; it should only be implemented
|
||||||
|
* by the config library. Arbitrary implementations will not work because the
|
||||||
|
* library internals assume a specific concrete implementation. Also, this
|
||||||
|
* interface is likely to grow new methods over time, so third-party
|
||||||
|
* implementations will break.
|
||||||
*/
|
*/
|
||||||
public interface ConfigValue extends ConfigMergeable {
|
public interface ConfigValue extends ConfigMergeable {
|
||||||
/**
|
/**
|
||||||
* The origin of the value, for debugging and error messages.
|
* The origin of the value (file, line number, etc.), for debugging and
|
||||||
|
* error messages.
|
||||||
*
|
*
|
||||||
* @return where the value came from
|
* @return where the value came from
|
||||||
*/
|
*/
|
||||||
ConfigOrigin origin();
|
ConfigOrigin origin();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the value; matches the JSON type schema.
|
* The {@link ConfigValueType} of the value; matches the JSON type schema.
|
||||||
*
|
*
|
||||||
* @return value's type
|
* @return value's type
|
||||||
*/
|
*/
|
||||||
ConfigValueType valueType();
|
ConfigValueType valueType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the config value as a plain Java boxed value, should be a String,
|
* Returns the value as a plain Java boxed value, that is, a {@code String},
|
||||||
* Number, etc. matching the valueType() of the ConfigValue. If the value is
|
* {@code Number}, {@code Boolean}, {@code Map<String,Object>},
|
||||||
* a ConfigObject or ConfigList, it is recursively unwrapped.
|
* {@code List<Object>}, or {@code null}, matching the {@link #valueType()}
|
||||||
|
* of this {@code ConfigValue}. If the value is a {@link ConfigObject} or
|
||||||
|
* {@link ConfigList}, it is recursively unwrapped.
|
||||||
*/
|
*/
|
||||||
Object unwrapped();
|
Object unwrapped();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the config value as a HOCON string. This method is primarily
|
||||||
|
* intended for debugging, so it tries to add helpful comments and
|
||||||
|
* whitespace. If the config value has not been resolved (see
|
||||||
|
* {@link Config#resolve}), it's possible that it can't be rendered as valid
|
||||||
|
* HOCON. In that case the rendering should still be useful for debugging
|
||||||
|
* but you might not be able to parse it.
|
||||||
|
*
|
||||||
|
* @return the rendered value
|
||||||
|
*/
|
||||||
|
String render();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ConfigValue withFallback(ConfigMergeable other);
|
ConfigValue withFallback(ConfigMergeable other);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
*/
|
*/
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.typesafe.config.impl.ConfigImpl;
|
import com.typesafe.config.impl.ConfigImpl;
|
||||||
|
|
@ -14,6 +13,9 @@ import com.typesafe.config.impl.ConfigImpl;
|
||||||
* data structures.
|
* data structures.
|
||||||
*/
|
*/
|
||||||
public final class ConfigValueFactory {
|
public final class ConfigValueFactory {
|
||||||
|
private ConfigValueFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a ConfigValue from a plain Java boxed value, which may be a
|
* Creates a ConfigValue from a plain Java boxed value, which may be a
|
||||||
* Boolean, Number, String, Map, Iterable, or null. A Map must be a Map from
|
* Boolean, Number, String, Map, Iterable, or null. A Map must be a Map from
|
||||||
|
|
@ -23,23 +25,27 @@ public final class ConfigValueFactory {
|
||||||
* the Iterable is not an ordered collection, results could be strange,
|
* the Iterable is not an ordered collection, results could be strange,
|
||||||
* since ConfigList is ordered.
|
* since ConfigList is ordered.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* In a Map passed to fromAnyRef(), the map's keys are plain keys, not path
|
* In a Map passed to fromAnyRef(), the map's keys are plain keys, not path
|
||||||
* expressions. So if your Map has a key "foo.bar" then you will get one
|
* expressions. So if your Map has a key "foo.bar" then you will get one
|
||||||
* object with a key called "foo.bar", rather than an object with a key
|
* object with a key called "foo.bar", rather than an object with a key
|
||||||
* "foo" containing another object with a key "bar".
|
* "foo" containing another object with a key "bar".
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* The originDescription will be used to set the origin() field on the
|
* The originDescription will be used to set the origin() field on the
|
||||||
* ConfigValue. It should normally be the name of the file the values came
|
* ConfigValue. It should normally be the name of the file the values came
|
||||||
* from, or something short describing the value such as "default settings".
|
* from, or something short describing the value such as "default settings".
|
||||||
* The originDescription is prefixed to error messages so users can tell
|
* The originDescription is prefixed to error messages so users can tell
|
||||||
* where problematic values are coming from.
|
* where problematic values are coming from.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Supplying the result of ConfigValue.unwrapped() to this function is
|
* Supplying the result of ConfigValue.unwrapped() to this function is
|
||||||
* guaranteed to work and should give you back a ConfigValue that matches
|
* guaranteed to work and should give you back a ConfigValue that matches
|
||||||
* the one you unwrapped. The re-wrapped ConfigValue will lose some
|
* the one you unwrapped. The re-wrapped ConfigValue will lose some
|
||||||
* information that was present in the original such as its origin, but it
|
* information that was present in the original such as its origin, but it
|
||||||
* will have matching values.
|
* will have matching values.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This function throws if you supply a value that cannot be converted to a
|
* This function throws if you supply a value that cannot be converted to a
|
||||||
* ConfigValue, but supplying such a value is a bug in your program, so you
|
* ConfigValue, but supplying such a value is a bug in your program, so you
|
||||||
* should never handle the exception. Just fix your program (or report a bug
|
* should never handle the exception. Just fix your program (or report a bug
|
||||||
|
|
@ -57,23 +63,25 @@ public final class ConfigValueFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||||
* wrapper that only works on Map and returns ConfigObject rather than
|
* wrapper that only works on {@link java.util.Map} and returns
|
||||||
* ConfigValue.
|
* {@link ConfigObject} rather than {@link ConfigValue}.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* If your Map has a key "foo.bar" then you will get one object with a key
|
* If your Map has a key "foo.bar" then you will get one object with a key
|
||||||
* called "foo.bar", rather than an object with a key "foo" containing
|
* called "foo.bar", rather than an object with a key "foo" containing
|
||||||
* another object with a key "bar". The keys in the map are keys; not path
|
* another object with a key "bar". The keys in the map are keys; not path
|
||||||
* expressions. That is, the Map corresponds exactly to a single
|
* expressions. That is, the Map corresponds exactly to a single
|
||||||
* ConfigObject. The keys will not be parsed or modified, and the values are
|
* {@code ConfigObject}. The keys will not be parsed or modified, and the
|
||||||
* wrapped in ConfigValue. To get nested ConfigObject, some of the values in
|
* values are wrapped in ConfigValue. To get nested {@code ConfigObject},
|
||||||
* the map would have to be more maps.
|
* some of the values in the map would have to be more maps.
|
||||||
*
|
*
|
||||||
* There is a separate fromPathMap() that interprets the keys in the map as
|
* <p>
|
||||||
* path expressions.
|
* See also {@link ConfigFactory#parseMap(Map,String)} which interprets the
|
||||||
|
* keys in the map as path expressions.
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values
|
||||||
* @param originDescription
|
* @param originDescription
|
||||||
* @return
|
* @return a new {@link ConfigObject} value
|
||||||
*/
|
*/
|
||||||
public static ConfigObject fromMap(Map<String, ? extends Object> values,
|
public static ConfigObject fromMap(Map<String, ? extends Object> values,
|
||||||
String originDescription) {
|
String originDescription) {
|
||||||
|
|
@ -82,12 +90,12 @@ public final class ConfigValueFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the fromAnyRef() documentation for details. This is a typesafe
|
* See the fromAnyRef() documentation for details. This is a typesafe
|
||||||
* wrapper that only works on Iterable and returns ConfigList rather than
|
* wrapper that only works on {@link java.util.Iterable} and returns
|
||||||
* ConfigValue.
|
* {@link ConfigList} rather than {@link ConfigValue}.
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values
|
||||||
* @param originDescription
|
* @param originDescription
|
||||||
* @return
|
* @return a new {@link ConfigList} value
|
||||||
*/
|
*/
|
||||||
public static ConfigList fromIterable(Iterable<? extends Object> values,
|
public static ConfigList fromIterable(Iterable<? extends Object> values,
|
||||||
String originDescription) {
|
String originDescription) {
|
||||||
|
|
@ -95,35 +103,39 @@ public final class ConfigValueFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the other overload of fromAnyRef() for details, this one just uses a
|
* See the other overload {@link #fromAnyRef(Object,String)} for details,
|
||||||
* default origin description.
|
* this one just uses a default origin description.
|
||||||
*
|
*
|
||||||
* @param object
|
* @param object
|
||||||
* @return
|
* @return a new {@link ConfigValue}
|
||||||
*/
|
*/
|
||||||
public static ConfigValue fromAnyRef(Object object) {
|
public static ConfigValue fromAnyRef(Object object) {
|
||||||
return fromAnyRef(object, null);
|
return fromAnyRef(object, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the other overload of fromMap() for details, this one just uses a
|
* See the other overload {@link #fromMap(Map,String)} for details, this one
|
||||||
* default origin description.
|
* just uses a default origin description.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* See also {@link ConfigFactory#parseMap(Map)} which interprets the keys in
|
||||||
|
* the map as path expressions.
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values
|
||||||
* @return
|
* @return a new {@link ConfigObject}
|
||||||
*/
|
*/
|
||||||
public static ConfigObject fromMap(Map<String, ? extends Object> values) {
|
public static ConfigObject fromMap(Map<String, ? extends Object> values) {
|
||||||
return fromMap(values, null);
|
return fromMap(values, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the other overload of fromIterable() for details, this one just uses
|
* See the other overload of {@link #fromIterable(Iterable, String)} for
|
||||||
* a default origin description.
|
* details, this one just uses a default origin description.
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values
|
||||||
* @return
|
* @return a new {@link ConfigList}
|
||||||
*/
|
*/
|
||||||
public static ConfigList fromIterable(Collection<? extends Object> values) {
|
public static ConfigList fromIterable(Iterable<? extends Object> values) {
|
||||||
return fromIterable(values, null);
|
return fromIterable(values, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of a configuration value. Value types follow the JSON type schema.
|
* The type of a configuration value (following the <a
|
||||||
|
* href="http://json.org">JSON</a> type schema).
|
||||||
*/
|
*/
|
||||||
public enum ConfigValueType {
|
public enum ConfigValueType {
|
||||||
OBJECT, LIST, NUMBER, BOOLEAN, NULL, STRING
|
OBJECT, LIST, NUMBER, BOOLEAN, NULL, STRING
|
||||||
|
|
|
||||||
|
|
@ -61,19 +61,23 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||||
* (just returns null if path not found). Does however resolve the path, if
|
* (just returns null if path not found). Does however resolve the path, if
|
||||||
* resolver != null.
|
* resolver != null.
|
||||||
*/
|
*/
|
||||||
protected ConfigValue peekPath(Path path, SubstitutionResolver resolver,
|
protected AbstractConfigValue peekPath(Path path, SubstitutionResolver resolver,
|
||||||
int depth, ConfigResolveOptions options) {
|
int depth, ConfigResolveOptions options) {
|
||||||
return peekPath(this, path, resolver, depth, options);
|
return peekPath(this, path, resolver, depth, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigValue peekPath(AbstractConfigObject self, Path path,
|
AbstractConfigValue peekPath(Path path) {
|
||||||
|
return peekPath(this, path, null, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractConfigValue peekPath(AbstractConfigObject self, Path path,
|
||||||
SubstitutionResolver resolver, int depth,
|
SubstitutionResolver resolver, int depth,
|
||||||
ConfigResolveOptions options) {
|
ConfigResolveOptions options) {
|
||||||
String key = path.first();
|
String key = path.first();
|
||||||
Path next = path.remainder();
|
Path next = path.remainder();
|
||||||
|
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
ConfigValue v = self.peek(key, resolver, depth, options);
|
AbstractConfigValue v = self.peek(key, resolver, depth, options);
|
||||||
return v;
|
return v;
|
||||||
} else {
|
} else {
|
||||||
// it's important to ONLY resolve substitutions here, not
|
// it's important to ONLY resolve substitutions here, not
|
||||||
|
|
@ -164,18 +168,13 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||||
if (stack.isEmpty())
|
if (stack.isEmpty())
|
||||||
throw new ConfigException.BugOrBroken(
|
throw new ConfigException.BugOrBroken(
|
||||||
"can't merge origins on empty list");
|
"can't merge origins on empty list");
|
||||||
final String prefix = "merge of ";
|
List<ConfigOrigin> origins = new ArrayList<ConfigOrigin>();
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
ConfigOrigin firstOrigin = null;
|
ConfigOrigin firstOrigin = null;
|
||||||
int numMerged = 0;
|
int numMerged = 0;
|
||||||
for (AbstractConfigValue v : stack) {
|
for (AbstractConfigValue v : stack) {
|
||||||
if (firstOrigin == null)
|
if (firstOrigin == null)
|
||||||
firstOrigin = v.origin();
|
firstOrigin = v.origin();
|
||||||
|
|
||||||
String desc = v.origin().description();
|
|
||||||
if (desc.startsWith(prefix))
|
|
||||||
desc = desc.substring(prefix.length());
|
|
||||||
|
|
||||||
if (v instanceof AbstractConfigObject
|
if (v instanceof AbstractConfigObject
|
||||||
&& ((AbstractConfigObject) v).resolveStatus() == ResolveStatus.RESOLVED
|
&& ((AbstractConfigObject) v).resolveStatus() == ResolveStatus.RESOLVED
|
||||||
&& ((ConfigObject) v).isEmpty()) {
|
&& ((ConfigObject) v).isEmpty()) {
|
||||||
|
|
@ -183,22 +182,17 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||||
// config in the description, since they are
|
// config in the description, since they are
|
||||||
// likely to be "implementation details"
|
// likely to be "implementation details"
|
||||||
} else {
|
} else {
|
||||||
sb.append(desc);
|
origins.add(v.origin());
|
||||||
sb.append(",");
|
|
||||||
numMerged += 1;
|
numMerged += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (numMerged > 0) {
|
|
||||||
sb.setLength(sb.length() - 1); // chop comma
|
if (numMerged == 0) {
|
||||||
if (numMerged > 1) {
|
// the configs were all empty, so just use the first one
|
||||||
return new SimpleConfigOrigin(prefix + sb.toString());
|
origins.add(firstOrigin);
|
||||||
} else {
|
|
||||||
return new SimpleConfigOrigin(sb.toString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// the configs were all empty.
|
|
||||||
return firstOrigin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return SimpleConfigOrigin.mergeOrigins(origins);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ConfigOrigin mergeOrigins(AbstractConfigObject... stack) {
|
static ConfigOrigin mergeOrigins(AbstractConfigObject... stack) {
|
||||||
|
|
@ -210,6 +204,8 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||||
Map<String, AbstractConfigValue> changes = null;
|
Map<String, AbstractConfigValue> changes = null;
|
||||||
for (String k : keySet()) {
|
for (String k : keySet()) {
|
||||||
AbstractConfigValue v = peek(k);
|
AbstractConfigValue v = peek(k);
|
||||||
|
// "modified" may be null, which means remove the child;
|
||||||
|
// to do that we put null in the "changes" map.
|
||||||
AbstractConfigValue modified = modifier.modifyChild(v);
|
AbstractConfigValue modified = modifier.modifyChild(v);
|
||||||
if (modified != v) {
|
if (modified != v) {
|
||||||
if (changes == null)
|
if (changes == null)
|
||||||
|
|
@ -223,7 +219,12 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||||
Map<String, AbstractConfigValue> modified = new HashMap<String, AbstractConfigValue>();
|
Map<String, AbstractConfigValue> modified = new HashMap<String, AbstractConfigValue>();
|
||||||
for (String k : keySet()) {
|
for (String k : keySet()) {
|
||||||
if (changes.containsKey(k)) {
|
if (changes.containsKey(k)) {
|
||||||
modified.put(k, changes.get(k));
|
AbstractConfigValue newValue = changes.get(k);
|
||||||
|
if (newValue != null) {
|
||||||
|
modified.put(k, newValue);
|
||||||
|
} else {
|
||||||
|
// remove this child; don't put it in the new map.
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
modified.put(k, peek(k));
|
modified.put(k, peek(k));
|
||||||
}
|
}
|
||||||
|
|
@ -271,20 +272,36 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||||
StringBuilder sb = new StringBuilder();
|
if (isEmpty()) {
|
||||||
sb.append(valueType().name());
|
sb.append("{}");
|
||||||
sb.append("(");
|
} else {
|
||||||
for (String k : keySet()) {
|
sb.append("{");
|
||||||
sb.append(k);
|
if (formatted)
|
||||||
sb.append("->");
|
sb.append('\n');
|
||||||
sb.append(peek(k).toString());
|
for (String k : keySet()) {
|
||||||
sb.append(",");
|
AbstractConfigValue v = peek(k);
|
||||||
|
if (formatted) {
|
||||||
|
indent(sb, indent + 1);
|
||||||
|
sb.append("# ");
|
||||||
|
sb.append(v.origin().description());
|
||||||
|
sb.append("\n");
|
||||||
|
indent(sb, indent + 1);
|
||||||
|
}
|
||||||
|
v.render(sb, indent + 1, k, formatted);
|
||||||
|
sb.append(",");
|
||||||
|
if (formatted)
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
// chop comma or newline
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
if (formatted) {
|
||||||
|
sb.setLength(sb.length() - 1); // also chop comma
|
||||||
|
sb.append("\n"); // put a newline back
|
||||||
|
indent(sb, indent);
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
}
|
}
|
||||||
if (!keySet().isEmpty())
|
|
||||||
sb.setLength(sb.length() - 1); // chop comma
|
|
||||||
sb.append(")");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean mapEquals(Map<String, ConfigValue> a,
|
private static boolean mapEquals(Map<String, ConfigValue> a,
|
||||||
|
|
|
||||||
|
|
@ -162,8 +162,39 @@ abstract class AbstractConfigValue implements ConfigValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public final String toString() {
|
||||||
return valueType().name() + "(" + unwrapped() + ")";
|
StringBuilder sb = new StringBuilder();
|
||||||
|
render(sb, 0, null /* atKey */, false /* formatted */);
|
||||||
|
return getClass().getSimpleName() + "(" + sb.toString() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void indent(StringBuilder sb, int indent) {
|
||||||
|
int remaining = indent;
|
||||||
|
while (remaining > 0) {
|
||||||
|
sb.append(" ");
|
||||||
|
--remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void render(StringBuilder sb, int indent, String atKey, boolean formatted) {
|
||||||
|
if (atKey != null) {
|
||||||
|
sb.append(ConfigUtil.renderJsonString(atKey));
|
||||||
|
sb.append(" : ");
|
||||||
|
}
|
||||||
|
render(sb, indent, formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||||
|
Object u = unwrapped();
|
||||||
|
sb.append(u.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String render() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
render(sb, 0, null, true /* formatted */);
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// toString() is a debugging-oriented string but this is defined
|
// toString() is a debugging-oriented string but this is defined
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ package com.typesafe.config.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.typesafe.config.ConfigException;
|
import com.typesafe.config.ConfigException;
|
||||||
|
|
@ -76,10 +77,12 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
|
||||||
AbstractConfigValue merged = null;
|
AbstractConfigValue merged = null;
|
||||||
for (AbstractConfigValue v : stack) {
|
for (AbstractConfigValue v : stack) {
|
||||||
AbstractConfigValue resolved = resolver.resolve(v, depth, options);
|
AbstractConfigValue resolved = resolver.resolve(v, depth, options);
|
||||||
if (merged == null)
|
if (resolved != null) {
|
||||||
merged = resolved;
|
if (merged == null)
|
||||||
else
|
merged = resolved;
|
||||||
merged = merged.withFallback(resolved);
|
else
|
||||||
|
merged = merged.withFallback(resolved);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return merged;
|
return merged;
|
||||||
|
|
@ -160,16 +163,58 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
protected void render(StringBuilder sb, int indent, String atKey, boolean formatted) {
|
||||||
StringBuilder sb = new StringBuilder();
|
render(stack, sb, indent, atKey, formatted);
|
||||||
sb.append("DELAYED_MERGE");
|
}
|
||||||
sb.append("(");
|
|
||||||
for (Object s : stack) {
|
// static method also used by ConfigDelayedMergeObject.
|
||||||
sb.append(s.toString());
|
static void render(List<AbstractConfigValue> stack, StringBuilder sb, int indent, String atKey,
|
||||||
sb.append(",");
|
boolean formatted) {
|
||||||
|
if (formatted) {
|
||||||
|
sb.append("# unresolved merge of " + stack.size() + " values follows (\n");
|
||||||
|
if (atKey == null) {
|
||||||
|
indent(sb, indent);
|
||||||
|
sb.append("# this unresolved merge will not be parseable because it's at the root of the object\n");
|
||||||
|
sb.append("# the HOCON format has no way to list multiple root objects in a single file\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AbstractConfigValue> reversed = new ArrayList<AbstractConfigValue>();
|
||||||
|
reversed.addAll(stack);
|
||||||
|
Collections.reverse(reversed);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (AbstractConfigValue v : reversed) {
|
||||||
|
if (formatted) {
|
||||||
|
indent(sb, indent);
|
||||||
|
if (atKey != null) {
|
||||||
|
sb.append("# unmerged value " + i + " for key "
|
||||||
|
+ ConfigUtil.renderJsonString(atKey) + " from ");
|
||||||
|
} else {
|
||||||
|
sb.append("# unmerged value " + i + " from ");
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
sb.append(v.origin().description());
|
||||||
|
sb.append("\n");
|
||||||
|
indent(sb, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atKey != null) {
|
||||||
|
sb.append(ConfigUtil.renderJsonString(atKey));
|
||||||
|
sb.append(" : ");
|
||||||
|
}
|
||||||
|
v.render(sb, indent, formatted);
|
||||||
|
sb.append(",");
|
||||||
|
if (formatted)
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
// chop comma or newline
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
if (formatted) {
|
||||||
|
sb.setLength(sb.length() - 1); // also chop comma
|
||||||
|
sb.append("\n"); // put a newline back
|
||||||
|
indent(sb, indent);
|
||||||
|
sb.append("# ) end of unresolved merge\n");
|
||||||
}
|
}
|
||||||
sb.setLength(sb.length() - 1); // chop comma
|
|
||||||
sb.append(")");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,17 +139,8 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
protected void render(StringBuilder sb, int indent, String atKey, boolean formatted) {
|
||||||
StringBuilder sb = new StringBuilder();
|
ConfigDelayedMerge.render(stack, sb, indent, atKey, formatted);
|
||||||
sb.append("DELAYED_MERGE_OBJECT");
|
|
||||||
sb.append("(");
|
|
||||||
for (Object s : stack) {
|
|
||||||
sb.append(s.toString());
|
|
||||||
sb.append(",");
|
|
||||||
}
|
|
||||||
sb.setLength(sb.length() - 1); // chop comma
|
|
||||||
sb.append(")");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigException notResolved() {
|
private static ConfigException notResolved() {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import com.typesafe.config.ConfigObject;
|
||||||
import com.typesafe.config.ConfigOrigin;
|
import com.typesafe.config.ConfigOrigin;
|
||||||
import com.typesafe.config.ConfigParseOptions;
|
import com.typesafe.config.ConfigParseOptions;
|
||||||
import com.typesafe.config.ConfigParseable;
|
import com.typesafe.config.ConfigParseable;
|
||||||
import com.typesafe.config.ConfigRoot;
|
|
||||||
import com.typesafe.config.ConfigSyntax;
|
import com.typesafe.config.ConfigSyntax;
|
||||||
import com.typesafe.config.ConfigValue;
|
import com.typesafe.config.ConfigValue;
|
||||||
|
|
||||||
|
|
@ -45,8 +44,7 @@ public class ConfigImpl {
|
||||||
obj = p.parse(p.options().setAllowMissing(
|
obj = p.parse(p.options().setAllowMissing(
|
||||||
options.getAllowMissing()));
|
options.getAllowMissing()));
|
||||||
} else {
|
} else {
|
||||||
obj = SimpleConfigObject.emptyMissing(new SimpleConfigOrigin(
|
obj = SimpleConfigObject.emptyMissing(SimpleConfigOrigin.newSimple(name));
|
||||||
name));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ConfigParseable confHandle = source.nameToParseable(name + ".conf");
|
ConfigParseable confHandle = source.nameToParseable(name + ".conf");
|
||||||
|
|
@ -56,13 +54,13 @@ public class ConfigImpl {
|
||||||
|
|
||||||
if (!options.getAllowMissing() && confHandle == null
|
if (!options.getAllowMissing() && confHandle == null
|
||||||
&& jsonHandle == null && propsHandle == null) {
|
&& jsonHandle == null && propsHandle == null) {
|
||||||
throw new ConfigException.IO(new SimpleConfigOrigin(name),
|
throw new ConfigException.IO(SimpleConfigOrigin.newSimple(name),
|
||||||
"No config files {.conf,.json,.properties} found");
|
"No config files {.conf,.json,.properties} found");
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigSyntax syntax = options.getSyntax();
|
ConfigSyntax syntax = options.getSyntax();
|
||||||
|
|
||||||
obj = SimpleConfigObject.empty(new SimpleConfigOrigin(name));
|
obj = SimpleConfigObject.empty(SimpleConfigOrigin.newSimple(name));
|
||||||
if (confHandle != null
|
if (confHandle != null
|
||||||
&& (syntax == null || syntax == ConfigSyntax.CONF)) {
|
&& (syntax == null || syntax == ConfigSyntax.CONF)) {
|
||||||
obj = confHandle.parse(confHandle.options()
|
obj = confHandle.parse(confHandle.options()
|
||||||
|
|
@ -89,39 +87,13 @@ public class ConfigImpl {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String makeResourceBasename(Path path) {
|
|
||||||
StringBuilder sb = new StringBuilder("/");
|
|
||||||
String next = path.first();
|
|
||||||
Path remaining = path.remainder();
|
|
||||||
while (next != null) {
|
|
||||||
sb.append(next);
|
|
||||||
sb.append('-');
|
|
||||||
|
|
||||||
if (remaining == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
next = remaining.first();
|
|
||||||
remaining = remaining.remainder();
|
|
||||||
}
|
|
||||||
sb.setLength(sb.length() - 1); // chop extra hyphen
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||||
public static ConfigObject parseResourcesForPath(String expression,
|
public static ConfigObject parseResourcesAnySyntax(final Class<?> klass,
|
||||||
final ConfigParseOptions baseOptions) {
|
|
||||||
Path path = Parser.parsePath(expression);
|
|
||||||
String basename = makeResourceBasename(path);
|
|
||||||
return parseResourceAnySyntax(ConfigImpl.class, basename, baseOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
|
||||||
public static ConfigObject parseResourceAnySyntax(final Class<?> klass,
|
|
||||||
String resourceBasename, final ConfigParseOptions baseOptions) {
|
String resourceBasename, final ConfigParseOptions baseOptions) {
|
||||||
NameSource source = new NameSource() {
|
NameSource source = new NameSource() {
|
||||||
@Override
|
@Override
|
||||||
public ConfigParseable nameToParseable(String name) {
|
public ConfigParseable nameToParseable(String name) {
|
||||||
return Parseable.newResource(klass, name, baseOptions);
|
return Parseable.newResources(klass, name, baseOptions);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return fromBasename(source, resourceBasename, baseOptions);
|
return fromBasename(source, resourceBasename, baseOptions);
|
||||||
|
|
@ -139,16 +111,9 @@ public class ConfigImpl {
|
||||||
return fromBasename(source, basename.getPath(), baseOptions);
|
return fromBasename(source, basename.getPath(), baseOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
|
||||||
public static ConfigRoot emptyRoot(String rootPath, String originDescription) {
|
|
||||||
String desc = originDescription != null ? originDescription : rootPath;
|
|
||||||
return emptyObject(desc).toConfig().asRoot(
|
|
||||||
Path.newPath(rootPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
static AbstractConfigObject emptyObject(String originDescription) {
|
static AbstractConfigObject emptyObject(String originDescription) {
|
||||||
ConfigOrigin origin = originDescription != null ? new SimpleConfigOrigin(
|
ConfigOrigin origin = originDescription != null ? SimpleConfigOrigin
|
||||||
originDescription) : null;
|
.newSimple(originDescription) : null;
|
||||||
return emptyObject(origin);
|
return emptyObject(origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,8 +127,8 @@ public class ConfigImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// default origin for values created with fromAnyRef and no origin specified
|
// default origin for values created with fromAnyRef and no origin specified
|
||||||
final private static ConfigOrigin defaultValueOrigin = new SimpleConfigOrigin(
|
final private static ConfigOrigin defaultValueOrigin = SimpleConfigOrigin
|
||||||
"hardcoded value");
|
.newSimple("hardcoded value");
|
||||||
final private static ConfigBoolean defaultTrueValue = new ConfigBoolean(
|
final private static ConfigBoolean defaultTrueValue = new ConfigBoolean(
|
||||||
defaultValueOrigin, true);
|
defaultValueOrigin, true);
|
||||||
final private static ConfigBoolean defaultFalseValue = new ConfigBoolean(
|
final private static ConfigBoolean defaultFalseValue = new ConfigBoolean(
|
||||||
|
|
@ -196,7 +161,7 @@ public class ConfigImpl {
|
||||||
if (originDescription == null)
|
if (originDescription == null)
|
||||||
return defaultValueOrigin;
|
return defaultValueOrigin;
|
||||||
else
|
else
|
||||||
return new SimpleConfigOrigin(originDescription);
|
return SimpleConfigOrigin.newSimple(originDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||||
|
|
@ -290,17 +255,6 @@ public class ConfigImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
|
||||||
public static ConfigRoot systemPropertiesRoot(String rootPath) {
|
|
||||||
Path path = Parser.parsePath(rootPath);
|
|
||||||
try {
|
|
||||||
return systemPropertiesAsConfigObject().toConfig().getConfig(rootPath)
|
|
||||||
.asRoot(path);
|
|
||||||
} catch (ConfigException.Missing e) {
|
|
||||||
return emptyObject("system properties").toConfig().asRoot(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SimpleIncluder implements ConfigIncluder {
|
private static class SimpleIncluder implements ConfigIncluder {
|
||||||
|
|
||||||
private ConfigIncluder fallback;
|
private ConfigIncluder fallback;
|
||||||
|
|
@ -346,29 +300,26 @@ public class ConfigImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigIncluder defaultIncluder = null;
|
private static class DefaultIncluderHolder {
|
||||||
|
static final ConfigIncluder defaultIncluder = new SimpleIncluder(null);
|
||||||
synchronized static ConfigIncluder defaultIncluder() {
|
|
||||||
if (defaultIncluder == null) {
|
|
||||||
defaultIncluder = new SimpleIncluder(null);
|
|
||||||
}
|
|
||||||
return defaultIncluder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AbstractConfigObject systemProperties = null;
|
static ConfigIncluder defaultIncluder() {
|
||||||
|
return DefaultIncluderHolder.defaultIncluder;
|
||||||
synchronized static AbstractConfigObject systemPropertiesAsConfigObject() {
|
|
||||||
if (systemProperties == null) {
|
|
||||||
systemProperties = loadSystemProperties();
|
|
||||||
}
|
|
||||||
return systemProperties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AbstractConfigObject loadSystemProperties() {
|
private static AbstractConfigObject loadSystemProperties() {
|
||||||
return (AbstractConfigObject) Parseable.newProperties(
|
return (AbstractConfigObject) Parseable.newProperties(System.getProperties(),
|
||||||
System.getProperties(),
|
ConfigParseOptions.defaults().setOriginDescription("system properties")).parse();
|
||||||
ConfigParseOptions.defaults().setOriginDescription(
|
}
|
||||||
"system properties")).parse();
|
|
||||||
|
private static class SystemPropertiesHolder {
|
||||||
|
// this isn't final due to the reloadSystemPropertiesConfig() hack below
|
||||||
|
static AbstractConfigObject systemProperties = loadSystemProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject systemPropertiesAsConfigObject() {
|
||||||
|
return SystemPropertiesHolder.systemProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||||
|
|
@ -376,18 +327,10 @@ public class ConfigImpl {
|
||||||
return systemPropertiesAsConfigObject().toConfig();
|
return systemPropertiesAsConfigObject().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a hack to let us set system props in the test suite
|
// this is a hack to let us set system props in the test suite.
|
||||||
synchronized static void dropSystemPropertiesConfig() {
|
// obviously not thread-safe.
|
||||||
systemProperties = null;
|
static void reloadSystemPropertiesConfig() {
|
||||||
}
|
SystemPropertiesHolder.systemProperties = loadSystemProperties();
|
||||||
|
|
||||||
private static AbstractConfigObject envVariables = null;
|
|
||||||
|
|
||||||
synchronized static AbstractConfigObject envVariablesAsConfigObject() {
|
|
||||||
if (envVariables == null) {
|
|
||||||
envVariables = loadEnvVariables();
|
|
||||||
}
|
|
||||||
return envVariables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AbstractConfigObject loadEnvVariables() {
|
private static AbstractConfigObject loadEnvVariables() {
|
||||||
|
|
@ -395,15 +338,37 @@ public class ConfigImpl {
|
||||||
Map<String, AbstractConfigValue> m = new HashMap<String, AbstractConfigValue>();
|
Map<String, AbstractConfigValue> m = new HashMap<String, AbstractConfigValue>();
|
||||||
for (Map.Entry<String, String> entry : env.entrySet()) {
|
for (Map.Entry<String, String> entry : env.entrySet()) {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
m.put(key, new ConfigString(
|
m.put(key,
|
||||||
new SimpleConfigOrigin("env var " + key), entry.getValue()));
|
new ConfigString(SimpleConfigOrigin.newSimple("env var " + key), entry
|
||||||
|
.getValue()));
|
||||||
}
|
}
|
||||||
return new SimpleConfigObject(new SimpleConfigOrigin("env variables"),
|
return new SimpleConfigObject(SimpleConfigOrigin.newSimple("env variables"),
|
||||||
m, ResolveStatus.RESOLVED, false /* ignoresFallbacks */);
|
m, ResolveStatus.RESOLVED, false /* ignoresFallbacks */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class EnvVariablesHolder {
|
||||||
|
static final AbstractConfigObject envVariables = loadEnvVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject envVariablesAsConfigObject() {
|
||||||
|
return EnvVariablesHolder.envVariables;
|
||||||
|
}
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||||
public static Config envVariablesAsConfig() {
|
public static Config envVariablesAsConfig() {
|
||||||
return envVariablesAsConfigObject().toConfig();
|
return envVariablesAsConfigObject().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ReferenceHolder {
|
||||||
|
private static final Config unresolvedResources = Parseable
|
||||||
|
.newResources(ConfigImpl.class, "/reference.conf", ConfigParseOptions.defaults())
|
||||||
|
.parse().toConfig();
|
||||||
|
static final Config referenceConfig = systemPropertiesAsConfig().withFallback(
|
||||||
|
unresolvedResources).resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||||
|
public static Config defaultReference() {
|
||||||
|
return ReferenceHolder.referenceConfig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,9 @@ final class ConfigNull extends AbstractConfigValue {
|
||||||
String transformToString() {
|
String transformToString() {
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||||
|
sb.append("null");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,4 +29,9 @@ final class ConfigString extends AbstractConfigValue {
|
||||||
String transformToString() {
|
String transformToString() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||||
|
sb.append(ConfigUtil.renderJsonString(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import com.typesafe.config.ConfigValueType;
|
||||||
final class ConfigSubstitution extends AbstractConfigValue implements
|
final class ConfigSubstitution extends AbstractConfigValue implements
|
||||||
Unmergeable {
|
Unmergeable {
|
||||||
|
|
||||||
// this is a list of String and Path where the Path
|
// this is a list of String and SubstitutionExpression where the
|
||||||
// have to be resolved to values, then if there's more
|
// SubstitutionExpression has to be resolved to values, then if there's more
|
||||||
// than one piece everything is stringified and concatenated
|
// than one piece everything is stringified and concatenated
|
||||||
final private List<Object> pieces;
|
final private List<Object> pieces;
|
||||||
// the length of any prefixes added with relativized()
|
// the length of any prefixes added with relativized()
|
||||||
|
|
@ -40,19 +40,24 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||||
this.pieces = pieces;
|
this.pieces = pieces;
|
||||||
this.prefixLength = prefixLength;
|
this.prefixLength = prefixLength;
|
||||||
this.ignoresFallbacks = ignoresFallbacks;
|
this.ignoresFallbacks = ignoresFallbacks;
|
||||||
|
for (Object p : pieces) {
|
||||||
|
if (p instanceof Path)
|
||||||
|
throw new RuntimeException("broken here");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigValueType valueType() {
|
public ConfigValueType valueType() {
|
||||||
throw new ConfigException.NotResolved(
|
throw new ConfigException.NotResolved(
|
||||||
"tried to get value type on an unresolved substitution: "
|
"need to call resolve() on root config; tried to get value type on an unresolved substitution: "
|
||||||
+ this);
|
+ this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object unwrapped() {
|
public Object unwrapped() {
|
||||||
throw new ConfigException.NotResolved(
|
throw new ConfigException.NotResolved(
|
||||||
"tried to unwrap an unresolved substitution: " + this);
|
"need to call resolve() on root config; tried to unwrap an unresolved substitution: "
|
||||||
|
+ this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -124,15 +129,15 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigValue resolve(SubstitutionResolver resolver, Path subst,
|
private ConfigValue resolve(SubstitutionResolver resolver, SubstitutionExpression subst,
|
||||||
int depth, ConfigResolveOptions options) {
|
int depth, ConfigResolveOptions options) {
|
||||||
ConfigValue result = findInObject(resolver.root(), resolver, subst,
|
ConfigValue result = findInObject(resolver.root(), resolver, subst.path(),
|
||||||
depth, options);
|
depth, options);
|
||||||
|
|
||||||
// when looking up system props and env variables,
|
// when looking up system props and env variables,
|
||||||
// we don't want the prefix that was added when
|
// we don't want the prefix that was added when
|
||||||
// we were included in another file.
|
// we were included in another file.
|
||||||
Path unprefixed = subst.subPath(prefixLength);
|
Path unprefixed = subst.path().subPath(prefixLength);
|
||||||
|
|
||||||
if (result == null && options.getUseSystemProperties()) {
|
if (result == null && options.getUseSystemProperties()) {
|
||||||
result = findInObject(ConfigImpl.systemPropertiesAsConfigObject(), null,
|
result = findInObject(ConfigImpl.systemPropertiesAsConfigObject(), null,
|
||||||
|
|
@ -144,10 +149,6 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||||
unprefixed, depth, options);
|
unprefixed, depth, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null) {
|
|
||||||
result = new ConfigNull(origin());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,28 +161,40 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||||
if (p instanceof String) {
|
if (p instanceof String) {
|
||||||
sb.append((String) p);
|
sb.append((String) p);
|
||||||
} else {
|
} else {
|
||||||
ConfigValue v = resolve(resolver, (Path) p, depth, options);
|
SubstitutionExpression exp = (SubstitutionExpression) p;
|
||||||
switch (v.valueType()) {
|
ConfigValue v = resolve(resolver, exp, depth, options);
|
||||||
case NULL:
|
|
||||||
// nothing; becomes empty string
|
if (v == null) {
|
||||||
break;
|
if (exp.optional()) {
|
||||||
case LIST:
|
// append nothing to StringBuilder
|
||||||
case OBJECT:
|
} else {
|
||||||
// cannot substitute lists and objects into strings
|
throw new ConfigException.UnresolvedSubstitution(origin(),
|
||||||
throw new ConfigException.WrongType(v.origin(),
|
exp.toString());
|
||||||
((Path) p).render(),
|
}
|
||||||
"not a list or object", v.valueType().name());
|
} else {
|
||||||
default:
|
switch (v.valueType()) {
|
||||||
sb.append(((AbstractConfigValue) v).transformToString());
|
case LIST:
|
||||||
|
case OBJECT:
|
||||||
|
// cannot substitute lists and objects into strings
|
||||||
|
throw new ConfigException.WrongType(v.origin(), exp.path().render(),
|
||||||
|
"not a list or object", v.valueType().name());
|
||||||
|
default:
|
||||||
|
sb.append(((AbstractConfigValue) v).transformToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ConfigString(origin(), sb.toString());
|
return new ConfigString(origin(), sb.toString());
|
||||||
} else {
|
} else {
|
||||||
if (!(pieces.get(0) instanceof Path))
|
if (!(pieces.get(0) instanceof SubstitutionExpression))
|
||||||
throw new ConfigException.BugOrBroken(
|
throw new ConfigException.BugOrBroken(
|
||||||
"ConfigSubstitution should never contain a single String piece");
|
"ConfigSubstitution should never contain a single String piece");
|
||||||
return resolve(resolver, (Path) pieces.get(0), depth, options);
|
SubstitutionExpression exp = (SubstitutionExpression) pieces.get(0);
|
||||||
|
ConfigValue v = resolve(resolver, exp, depth, options);
|
||||||
|
if (v == null && !exp.optional()) {
|
||||||
|
throw new ConfigException.UnresolvedSubstitution(origin(), exp.toString());
|
||||||
|
}
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,8 +223,10 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||||
ConfigSubstitution relativized(Path prefix) {
|
ConfigSubstitution relativized(Path prefix) {
|
||||||
List<Object> newPieces = new ArrayList<Object>();
|
List<Object> newPieces = new ArrayList<Object>();
|
||||||
for (Object p : pieces) {
|
for (Object p : pieces) {
|
||||||
if (p instanceof Path) {
|
if (p instanceof SubstitutionExpression) {
|
||||||
newPieces.add(((Path) p).prepend(prefix));
|
SubstitutionExpression exp = (SubstitutionExpression) p;
|
||||||
|
|
||||||
|
newPieces.add(exp.changePath(exp.path().prepend(prefix)));
|
||||||
} else {
|
} else {
|
||||||
newPieces.add(p);
|
newPieces.add(p);
|
||||||
}
|
}
|
||||||
|
|
@ -243,16 +258,13 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("SUBST");
|
|
||||||
sb.append("(");
|
|
||||||
for (Object p : pieces) {
|
for (Object p : pieces) {
|
||||||
sb.append(p.toString());
|
if (p instanceof SubstitutionExpression) {
|
||||||
sb.append(",");
|
sb.append(p.toString());
|
||||||
|
} else {
|
||||||
|
sb.append(ConfigUtil.renderJsonString((String) p));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sb.setLength(sb.length() - 1); // chop comma
|
|
||||||
sb.append(")");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
|
enum OriginType {
|
||||||
|
GENERIC,
|
||||||
|
FILE,
|
||||||
|
URL,
|
||||||
|
RESOURCE
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
|
@ -39,6 +40,7 @@ import com.typesafe.config.ConfigValue;
|
||||||
public abstract class Parseable implements ConfigParseable {
|
public abstract class Parseable implements ConfigParseable {
|
||||||
private ConfigIncludeContext includeContext;
|
private ConfigIncludeContext includeContext;
|
||||||
private ConfigParseOptions initialOptions;
|
private ConfigParseOptions initialOptions;
|
||||||
|
private ConfigOrigin initialOrigin;
|
||||||
|
|
||||||
protected Parseable() {
|
protected Parseable() {
|
||||||
|
|
||||||
|
|
@ -54,9 +56,6 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
ConfigParseOptions modified = baseOptions.setSyntax(syntax);
|
ConfigParseOptions modified = baseOptions.setSyntax(syntax);
|
||||||
|
|
||||||
if (modified.getOriginDescription() == null)
|
|
||||||
modified = modified.setOriginDescription(originDescription());
|
|
||||||
|
|
||||||
modified = modified.appendIncluder(ConfigImpl.defaultIncluder());
|
modified = modified.appendIncluder(ConfigImpl.defaultIncluder());
|
||||||
|
|
||||||
return modified;
|
return modified;
|
||||||
|
|
@ -71,6 +70,11 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
return Parseable.this.relativeTo(filename);
|
return Parseable.this.relativeTo(filename);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (initialOptions.getOriginDescription() != null)
|
||||||
|
initialOrigin = SimpleConfigOrigin.newSimple(initialOptions.getOriginDescription());
|
||||||
|
else
|
||||||
|
initialOrigin = createOrigin();
|
||||||
}
|
}
|
||||||
|
|
||||||
// the general idea is that any work should be in here, not in the
|
// the general idea is that any work should be in here, not in the
|
||||||
|
|
@ -108,33 +112,26 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
return forceParsedToObject(parseValue(baseOptions));
|
return forceParsedToObject(parseValue(baseOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractConfigValue parseValue(ConfigParseOptions baseOptions) {
|
final AbstractConfigValue parseValue(ConfigParseOptions baseOptions) {
|
||||||
// note that we are NOT using our "options" and "origin" fields,
|
// note that we are NOT using our "initialOptions",
|
||||||
// but using the ones from the passed-in options. The idea is that
|
// but using the ones from the passed-in options. The idea is that
|
||||||
// callers can get our original options and then parse with different
|
// callers can get our original options and then parse with different
|
||||||
// ones if they want.
|
// ones if they want.
|
||||||
ConfigParseOptions options = fixupOptions(baseOptions);
|
ConfigParseOptions options = fixupOptions(baseOptions);
|
||||||
ConfigOrigin origin = new SimpleConfigOrigin(
|
|
||||||
options.getOriginDescription());
|
// passed-in options can override origin
|
||||||
|
ConfigOrigin origin;
|
||||||
|
if (options.getOriginDescription() != null)
|
||||||
|
origin = SimpleConfigOrigin.newSimple(options.getOriginDescription());
|
||||||
|
else
|
||||||
|
origin = initialOrigin;
|
||||||
return parseValue(origin, options);
|
return parseValue(origin, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AbstractConfigValue parseValue(ConfigOrigin origin,
|
final private AbstractConfigValue parseValue(ConfigOrigin origin,
|
||||||
ConfigParseOptions finalOptions) {
|
ConfigParseOptions finalOptions) {
|
||||||
try {
|
try {
|
||||||
Reader reader = reader();
|
return rawParseValue(origin, finalOptions);
|
||||||
try {
|
|
||||||
if (finalOptions.getSyntax() == ConfigSyntax.PROPERTIES) {
|
|
||||||
return PropertiesParser.parse(reader, origin);
|
|
||||||
} else {
|
|
||||||
Iterator<Token> tokens = Tokenizer.tokenize(origin, reader,
|
|
||||||
finalOptions.getSyntax());
|
|
||||||
return Parser.parse(tokens, origin, finalOptions,
|
|
||||||
includeContext());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (finalOptions.getAllowMissing()) {
|
if (finalOptions.getAllowMissing()) {
|
||||||
return SimpleConfigObject.emptyMissing(origin);
|
return SimpleConfigObject.emptyMissing(origin);
|
||||||
|
|
@ -144,6 +141,28 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is parseValue without post-processing the IOException or handling
|
||||||
|
// options.getAllowMissing()
|
||||||
|
protected AbstractConfigValue rawParseValue(ConfigOrigin origin, ConfigParseOptions finalOptions)
|
||||||
|
throws IOException {
|
||||||
|
Reader reader = reader();
|
||||||
|
try {
|
||||||
|
return rawParseValue(reader, origin, finalOptions);
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractConfigValue rawParseValue(Reader reader, ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) throws IOException {
|
||||||
|
if (finalOptions.getSyntax() == ConfigSyntax.PROPERTIES) {
|
||||||
|
return PropertiesParser.parse(reader, origin);
|
||||||
|
} else {
|
||||||
|
Iterator<Token> tokens = Tokenizer.tokenize(origin, reader, finalOptions.getSyntax());
|
||||||
|
return Parser.parse(tokens, origin, finalOptions, includeContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ConfigObject parse() {
|
public ConfigObject parse() {
|
||||||
return forceParsedToObject(parseValue(options()));
|
return forceParsedToObject(parseValue(options()));
|
||||||
}
|
}
|
||||||
|
|
@ -152,13 +171,13 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
return parseValue(options());
|
return parseValue(options());
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract String originDescription();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL url() {
|
public final ConfigOrigin origin() {
|
||||||
return null;
|
return initialOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract ConfigOrigin createOrigin();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigParseOptions options() {
|
public ConfigParseOptions options() {
|
||||||
return initialOptions;
|
return initialOptions;
|
||||||
|
|
@ -228,34 +247,6 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static class ParseableInputStream extends Parseable {
|
|
||||||
final private InputStream input;
|
|
||||||
|
|
||||||
ParseableInputStream(InputStream input, ConfigParseOptions options) {
|
|
||||||
this.input = input;
|
|
||||||
postConstruct(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Reader reader() {
|
|
||||||
return doNotClose(readerFromStream(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String originDescription() {
|
|
||||||
return "InputStream";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* note that we will never close this stream; you have to do it when parsing
|
|
||||||
* is complete.
|
|
||||||
*/
|
|
||||||
public static Parseable newInputStream(InputStream input,
|
|
||||||
ConfigParseOptions options) {
|
|
||||||
return new ParseableInputStream(input, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final static class ParseableReader extends Parseable {
|
private final static class ParseableReader extends Parseable {
|
||||||
final private Reader reader;
|
final private Reader reader;
|
||||||
|
|
||||||
|
|
@ -270,8 +261,8 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String originDescription() {
|
protected ConfigOrigin createOrigin() {
|
||||||
return "Reader";
|
return SimpleConfigOrigin.newSimple("Reader");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,8 +288,8 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String originDescription() {
|
protected ConfigOrigin createOrigin() {
|
||||||
return "String";
|
return SimpleConfigOrigin.newSimple("String");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,13 +326,8 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String originDescription() {
|
protected ConfigOrigin createOrigin() {
|
||||||
return input.toExternalForm();
|
return SimpleConfigOrigin.newURL(input);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URL url() {
|
|
||||||
return input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -387,17 +373,8 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String originDescription() {
|
protected ConfigOrigin createOrigin() {
|
||||||
return input.getPath();
|
return SimpleConfigOrigin.newFile(input.getPath());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URL url() {
|
|
||||||
try {
|
|
||||||
return input.toURI().toURL();
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -410,25 +387,61 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
return new ParseableFile(input, options);
|
return new ParseableFile(input, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static class ParseableResource extends Parseable {
|
private final static class ParseableResources extends Parseable {
|
||||||
final private Class<?> klass;
|
final private ClassLoader loader;
|
||||||
final private String resource;
|
final private String resource;
|
||||||
|
|
||||||
ParseableResource(Class<?> klass, String resource,
|
ParseableResources(ClassLoader loader, String resource,
|
||||||
ConfigParseOptions options) {
|
ConfigParseOptions options) {
|
||||||
this.klass = klass;
|
this.loader = loader;
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
postConstruct(options);
|
postConstruct(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Reader reader() throws IOException {
|
protected Reader reader() throws IOException {
|
||||||
InputStream stream = klass.getResourceAsStream(resource);
|
throw new ConfigException.BugOrBroken(
|
||||||
if (stream == null) {
|
"reader() should not be called on resources");
|
||||||
throw new IOException("resource not found on classpath: "
|
}
|
||||||
+ resource);
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConfigObject rawParseValue(ConfigOrigin origin,
|
||||||
|
ConfigParseOptions finalOptions) throws IOException {
|
||||||
|
Enumeration<URL> e = loader.getResources(resource);
|
||||||
|
if (!e.hasMoreElements()) {
|
||||||
|
throw new IOException("resource not found on classpath: " + resource);
|
||||||
}
|
}
|
||||||
return readerFromStream(stream);
|
AbstractConfigObject merged = SimpleConfigObject.empty(origin);
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
URL url = e.nextElement();
|
||||||
|
|
||||||
|
ConfigOrigin elementOrigin = ((SimpleConfigOrigin) origin).addURL(url);
|
||||||
|
|
||||||
|
AbstractConfigValue v;
|
||||||
|
|
||||||
|
// it's tempting to use ParseableURL here but it would be wrong
|
||||||
|
// because the wrong relativeTo() would be used for includes.
|
||||||
|
InputStream stream = url.openStream();
|
||||||
|
try {
|
||||||
|
Reader reader = readerFromStream(stream);
|
||||||
|
stream = null; // reader now owns it
|
||||||
|
try {
|
||||||
|
// parse in "raw" mode which will throw any IOException
|
||||||
|
// from here.
|
||||||
|
v = rawParseValue(reader, elementOrigin, finalOptions);
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// stream is null if the reader owns it
|
||||||
|
if (stream != null)
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
merged = merged.withFallback(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -436,48 +449,76 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
return syntaxFromExtension(resource);
|
return syntaxFromExtension(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
static String parent(String resource) {
|
||||||
ConfigParseable relativeTo(String filename) {
|
int i = resource.lastIndexOf('/');
|
||||||
// not using File.isAbsolute because resource paths always use '/'
|
if (i < 0) {
|
||||||
// (?)
|
|
||||||
if (filename.startsWith("/"))
|
|
||||||
return null;
|
return null;
|
||||||
|
} else {
|
||||||
|
return resource.substring(0, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ConfigParseable relativeTo(String sibling) {
|
||||||
// here we want to build a new resource name and let
|
// here we want to build a new resource name and let
|
||||||
// the class loader have it, rather than getting the
|
// the class loader have it, rather than getting the
|
||||||
// url with getResource() and relativizing to that url.
|
// url with getResource() and relativizing to that url.
|
||||||
// This is needed in case the class loader is going to
|
// This is needed in case the class loader is going to
|
||||||
// search a classpath.
|
// search a classpath.
|
||||||
File parent = new File(resource).getParentFile();
|
String parent = parent(resource);
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
return newResource(klass, "/" + filename, options()
|
return newResources(loader, sibling, options()
|
||||||
.setOriginDescription(null));
|
.setOriginDescription(null));
|
||||||
else
|
else
|
||||||
return newResource(klass, new File(parent, filename).getPath(),
|
return newResources(loader, parent + "/" + sibling,
|
||||||
options().setOriginDescription(null));
|
options().setOriginDescription(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String originDescription() {
|
protected ConfigOrigin createOrigin() {
|
||||||
return resource + " on classpath";
|
return SimpleConfigOrigin.newResource(resource);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URL url() {
|
|
||||||
return klass.getResource(resource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName() + "(" + resource + ","
|
return getClass().getSimpleName() + "(" + resource + ","
|
||||||
+ klass.getName()
|
+ loader.getClass().getSimpleName() + ")";
|
||||||
+ ")";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Parseable newResource(Class<?> klass, String resource,
|
public static Parseable newResources(Class<?> klass, String resource,
|
||||||
ConfigParseOptions options) {
|
ConfigParseOptions options) {
|
||||||
return new ParseableResource(klass, resource, options);
|
return newResources(klass.getClassLoader(), convertResourceName(klass, resource), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function is supposed to emulate the difference
|
||||||
|
// between Class.getResource and ClassLoader.getResource
|
||||||
|
// (unfortunately there doesn't seem to be public API for it).
|
||||||
|
// We're using it because the Class API is more limited,
|
||||||
|
// for example it lacks getResources(). So we want to be able to
|
||||||
|
// use ClassLoader directly.
|
||||||
|
private static String convertResourceName(Class<?> klass, String resource) {
|
||||||
|
if (resource.startsWith("/")) {
|
||||||
|
// "absolute" resource, chop the slash
|
||||||
|
return resource.substring(1);
|
||||||
|
} else {
|
||||||
|
String className = klass.getName();
|
||||||
|
int i = className.lastIndexOf('.');
|
||||||
|
if (i < 0) {
|
||||||
|
// no package
|
||||||
|
return resource;
|
||||||
|
} else {
|
||||||
|
// need to be relative to the package
|
||||||
|
String packageName = className.substring(0, i);
|
||||||
|
String packagePath = packageName.replace('.', '/');
|
||||||
|
return packagePath + "/" + resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parseable newResources(ClassLoader loader, String resource,
|
||||||
|
ConfigParseOptions options) {
|
||||||
|
return new ParseableResources(loader, resource, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static class ParseableProperties extends Parseable {
|
private final static class ParseableProperties extends Parseable {
|
||||||
|
|
@ -495,7 +536,7 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractConfigObject parseValue(ConfigOrigin origin,
|
protected AbstractConfigObject rawParseValue(ConfigOrigin origin,
|
||||||
ConfigParseOptions finalOptions) {
|
ConfigParseOptions finalOptions) {
|
||||||
return PropertiesParser.fromProperties(origin, props);
|
return PropertiesParser.fromProperties(origin, props);
|
||||||
}
|
}
|
||||||
|
|
@ -506,13 +547,8 @@ public abstract class Parseable implements ConfigParseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String originDescription() {
|
protected ConfigOrigin createOrigin() {
|
||||||
return "properties";
|
return SimpleConfigOrigin.newSimple("properties");
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URL url() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@ final class Parser {
|
||||||
Token t = nextToken();
|
Token t = nextToken();
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Tokens.isNewline(t)) {
|
if (Tokens.isNewline(t)) {
|
||||||
lineNumber = Tokens.getLineNumber(t);
|
// newline number is the line just ended, so add one
|
||||||
|
lineNumber = Tokens.getLineNumber(t) + 1;
|
||||||
sawSeparatorOrNewline = true;
|
sawSeparatorOrNewline = true;
|
||||||
// we want to continue to also eat
|
// we want to continue to also eat
|
||||||
// a comma if there is one.
|
// a comma if there is one.
|
||||||
|
|
@ -155,7 +156,7 @@ final class Parser {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this will be a list of String and Path
|
// this will be a list of String and SubstitutionExpression
|
||||||
List<Object> minimized = new ArrayList<Object>();
|
List<Object> minimized = new ArrayList<Object>();
|
||||||
|
|
||||||
// we have multiple value tokens or one unquoted text token;
|
// we have multiple value tokens or one unquoted text token;
|
||||||
|
|
@ -187,7 +188,9 @@ final class Parser {
|
||||||
.getSubstitutionPathExpression(valueToken);
|
.getSubstitutionPathExpression(valueToken);
|
||||||
Path path = parsePathExpression(expression.iterator(),
|
Path path = parsePathExpression(expression.iterator(),
|
||||||
Tokens.getSubstitutionOrigin(valueToken));
|
Tokens.getSubstitutionOrigin(valueToken));
|
||||||
minimized.add(path);
|
boolean optional = Tokens.getSubstitutionOptional(valueToken);
|
||||||
|
|
||||||
|
minimized.add(new SubstitutionExpression(path, optional));
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigException.BugOrBroken(
|
throw new ConfigException.BugOrBroken(
|
||||||
"should not be trying to consolidate token: "
|
"should not be trying to consolidate token: "
|
||||||
|
|
@ -219,8 +222,7 @@ final class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigOrigin lineOrigin() {
|
private ConfigOrigin lineOrigin() {
|
||||||
return new SimpleConfigOrigin(baseOrigin.description() + ": line "
|
return ((SimpleConfigOrigin) baseOrigin).setLineNumber(lineNumber);
|
||||||
+ lineNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigException parseError(String message) {
|
private ConfigException parseError(String message) {
|
||||||
|
|
@ -681,7 +683,7 @@ final class Parser {
|
||||||
return pb.result();
|
return pb.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ConfigOrigin apiOrigin = new SimpleConfigOrigin("path parameter");
|
static ConfigOrigin apiOrigin = SimpleConfigOrigin.newSimple("path parameter");
|
||||||
|
|
||||||
static Path parsePath(String path) {
|
static Path parsePath(String path) {
|
||||||
Path speculated = speculativeFastParsePath(path);
|
Path speculated = speculativeFastParsePath(path);
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ final class Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendToStringBuilder(StringBuilder sb) {
|
private void appendToStringBuilder(StringBuilder sb) {
|
||||||
if (hasFunkyChars(first))
|
if (hasFunkyChars(first) || first.isEmpty())
|
||||||
sb.append(ConfigUtil.renderJsonString(first));
|
sb.append(ConfigUtil.renderJsonString(first));
|
||||||
else
|
else
|
||||||
sb.append(first);
|
sb.append(first);
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
|
|
||||||
*/
|
|
||||||
package com.typesafe.config.impl;
|
|
||||||
|
|
||||||
import com.typesafe.config.ConfigMergeable;
|
|
||||||
import com.typesafe.config.ConfigResolveOptions;
|
|
||||||
import com.typesafe.config.ConfigRoot;
|
|
||||||
|
|
||||||
final class RootConfig extends SimpleConfig implements ConfigRoot {
|
|
||||||
|
|
||||||
final private Path rootPath;
|
|
||||||
|
|
||||||
RootConfig(AbstractConfigObject underlying, Path rootPath) {
|
|
||||||
super(underlying);
|
|
||||||
this.rootPath = rootPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RootConfig asRoot(AbstractConfigObject underlying,
|
|
||||||
Path newRootPath) {
|
|
||||||
if (newRootPath.equals(this.rootPath))
|
|
||||||
return this;
|
|
||||||
else
|
|
||||||
return new RootConfig(underlying, newRootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RootConfig resolve() {
|
|
||||||
return resolve(ConfigResolveOptions.defaults());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RootConfig resolve(ConfigResolveOptions options) {
|
|
||||||
// if the object is already resolved then we should end up returning
|
|
||||||
// "this" here, since asRoot() should return this if the path
|
|
||||||
// is unchanged.
|
|
||||||
AbstractConfigObject resolved = resolvedObject(options);
|
|
||||||
return newRootIfObjectChanged(this, resolved);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RootConfig withFallback(ConfigMergeable value) {
|
|
||||||
// this can return "this" if the withFallback does nothing
|
|
||||||
return newRootIfObjectChanged(this, super.withFallback(value).toObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
Path rootPathObject() {
|
|
||||||
return rootPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String rootPath() {
|
|
||||||
return rootPath.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Root" + super.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
import com.typesafe.config.Config;
|
||||||
|
|
@ -34,7 +36,7 @@ class SimpleConfig implements Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractConfigObject toObject() {
|
public AbstractConfigObject root() {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,35 +45,22 @@ class SimpleConfig implements Config {
|
||||||
return object.origin();
|
return object.origin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns a version of this config that implements the ConfigRoot
|
public SimpleConfig resolve() {
|
||||||
* interface.
|
return resolve(ConfigResolveOptions.defaults());
|
||||||
*
|
|
||||||
* @return a config root
|
|
||||||
*/
|
|
||||||
RootConfig asRoot(Path rootPath) {
|
|
||||||
return asRoot(object, rootPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RootConfig overrides this to avoid a new object on unchanged path.
|
@Override
|
||||||
protected RootConfig asRoot(AbstractConfigObject underlying,
|
public SimpleConfig resolve(ConfigResolveOptions options) {
|
||||||
Path newRootPath) {
|
|
||||||
return new RootConfig(underlying, newRootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static protected RootConfig newRootIfObjectChanged(RootConfig self, AbstractConfigObject underlying) {
|
|
||||||
if (underlying == self.object)
|
|
||||||
return self;
|
|
||||||
else
|
|
||||||
return new RootConfig(underlying, self.rootPathObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AbstractConfigObject resolvedObject(ConfigResolveOptions options) {
|
|
||||||
AbstractConfigValue resolved = SubstitutionResolver.resolve(object,
|
AbstractConfigValue resolved = SubstitutionResolver.resolve(object,
|
||||||
object, options);
|
object, options);
|
||||||
return (AbstractConfigObject) resolved;
|
if (resolved == object)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return new SimpleConfig((AbstractConfigObject) resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPath(String pathExpression) {
|
public boolean hasPath(String pathExpression) {
|
||||||
Path path = Path.newPath(pathExpression);
|
Path path = Path.newPath(pathExpression);
|
||||||
|
|
@ -196,13 +185,13 @@ class SimpleConfig implements Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getMemorySizeInBytes(String path) {
|
public Long getBytes(String path) {
|
||||||
Long size = null;
|
Long size = null;
|
||||||
try {
|
try {
|
||||||
size = getLong(path);
|
size = getLong(path);
|
||||||
} catch (ConfigException.WrongType e) {
|
} catch (ConfigException.WrongType e) {
|
||||||
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
ConfigValue v = find(path, ConfigValueType.STRING, path);
|
||||||
size = parseMemorySizeInBytes((String) v.unwrapped(),
|
size = parseBytes((String) v.unwrapped(),
|
||||||
v.origin(), path);
|
v.origin(), path);
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
|
|
@ -346,7 +335,7 @@ class SimpleConfig implements Config {
|
||||||
l.add(((Number) v.unwrapped()).longValue());
|
l.add(((Number) v.unwrapped()).longValue());
|
||||||
} else if (v.valueType() == ConfigValueType.STRING) {
|
} else if (v.valueType() == ConfigValueType.STRING) {
|
||||||
String s = (String) v.unwrapped();
|
String s = (String) v.unwrapped();
|
||||||
Long n = parseMemorySizeInBytes(s, v.origin(), path);
|
Long n = parseBytes(s, v.origin(), path);
|
||||||
l.add(n);
|
l.add(n);
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigException.WrongType(v.origin(), path,
|
throw new ConfigException.WrongType(v.origin(), path,
|
||||||
|
|
@ -509,23 +498,87 @@ class SimpleConfig implements Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static enum MemoryUnit {
|
private static enum MemoryUnit {
|
||||||
BYTES(1), KILOBYTES(1024), MEGABYTES(1024 * 1024), GIGABYTES(
|
BYTES("", 1024, 0),
|
||||||
1024 * 1024 * 1024), TERABYTES(1024 * 1024 * 1024 * 1024);
|
|
||||||
|
|
||||||
int bytes;
|
KILOBYTES("kilo", 1000, 1),
|
||||||
|
MEGABYTES("mega", 1000, 2),
|
||||||
|
GIGABYTES("giga", 1000, 3),
|
||||||
|
TERABYTES("tera", 1000, 4),
|
||||||
|
PETABYTES("peta", 1000, 5),
|
||||||
|
EXABYTES("exa", 1000, 6),
|
||||||
|
ZETTABYTES("zetta", 1000, 7),
|
||||||
|
YOTTABYTES("yotta", 1000, 8),
|
||||||
|
|
||||||
MemoryUnit(int bytes) {
|
KIBIBYTES("kibi", 1024, 1),
|
||||||
|
MEBIBYTES("mebi", 1024, 2),
|
||||||
|
GIBIBYTES("gibi", 1024, 3),
|
||||||
|
TEBIBYTES("tebi", 1024, 4),
|
||||||
|
PEBIBYTES("pebi", 1024, 5),
|
||||||
|
EXBIBYTES("exbi", 1024, 6),
|
||||||
|
ZEBIBYTES("zebi", 1024, 7),
|
||||||
|
YOBIBYTES("yobi", 1024, 8);
|
||||||
|
|
||||||
|
final String prefix;
|
||||||
|
final int powerOf;
|
||||||
|
final int power;
|
||||||
|
final long bytes;
|
||||||
|
|
||||||
|
MemoryUnit(String prefix, int powerOf, int power) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.powerOf = powerOf;
|
||||||
|
this.power = power;
|
||||||
|
int i = power;
|
||||||
|
long bytes = 1;
|
||||||
|
while (i > 0) {
|
||||||
|
bytes *= powerOf;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
this.bytes = bytes;
|
this.bytes = bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, MemoryUnit> makeUnitsMap() {
|
||||||
|
Map<String, MemoryUnit> map = new HashMap<String, MemoryUnit>();
|
||||||
|
for (MemoryUnit unit : MemoryUnit.values()) {
|
||||||
|
map.put(unit.prefix + "byte", unit);
|
||||||
|
map.put(unit.prefix + "bytes", unit);
|
||||||
|
if (unit.prefix.length() == 0) {
|
||||||
|
map.put("b", unit);
|
||||||
|
map.put("B", unit);
|
||||||
|
map.put("", unit); // no unit specified means bytes
|
||||||
|
} else {
|
||||||
|
String first = unit.prefix.substring(0, 1);
|
||||||
|
String firstUpper = first.toUpperCase();
|
||||||
|
if (unit.powerOf == 1024) {
|
||||||
|
map.put(first, unit); // 512m
|
||||||
|
map.put(firstUpper, unit); // 512M
|
||||||
|
map.put(firstUpper + "i", unit); // 512Mi
|
||||||
|
map.put(firstUpper + "iB", unit); // 512MiB
|
||||||
|
} else if (unit.powerOf == 1000) {
|
||||||
|
if (unit.power == 1) {
|
||||||
|
map.put(first + "B", unit); // 512kB
|
||||||
|
} else {
|
||||||
|
map.put(firstUpper + "B", unit); // 512MB
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("broken MemoryUnit enum");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, MemoryUnit> unitsMap = makeUnitsMap();
|
||||||
|
|
||||||
|
static MemoryUnit parseUnit(String unit) {
|
||||||
|
return unitsMap.get(unit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a memory-size string. If no units are specified in the string, it
|
* Parses a size-in-bytes string. If no units are specified in the string,
|
||||||
* is assumed to be in bytes. The returned value is in bytes. The purpose of
|
* it is assumed to be in bytes. The returned value is in bytes. The purpose
|
||||||
* this function is to implement the memory-size-related methods in the
|
* of this function is to implement the size-in-bytes-related methods in the
|
||||||
* ConfigObject interface. The units parsed are interpreted as powers of
|
* Config interface.
|
||||||
* two, that is, the convention for memory rather than the convention for
|
|
||||||
* disk space.
|
|
||||||
*
|
*
|
||||||
* @param input
|
* @param input
|
||||||
* the string to parse
|
* the string to parse
|
||||||
|
|
@ -537,19 +590,12 @@ class SimpleConfig implements Config {
|
||||||
* @throws ConfigException
|
* @throws ConfigException
|
||||||
* if string is invalid
|
* if string is invalid
|
||||||
*/
|
*/
|
||||||
public static long parseMemorySizeInBytes(String input,
|
public static long parseBytes(String input, ConfigOrigin originForException,
|
||||||
ConfigOrigin originForException, String pathForException) {
|
String pathForException) {
|
||||||
String s = ConfigUtil.unicodeTrim(input);
|
String s = ConfigUtil.unicodeTrim(input);
|
||||||
String unitStringMaybePlural = getUnits(s);
|
String unitString = getUnits(s);
|
||||||
String unitString;
|
String numberString = ConfigUtil.unicodeTrim(s.substring(0,
|
||||||
if (unitStringMaybePlural.endsWith("s"))
|
s.length() - unitString.length()));
|
||||||
unitString = unitStringMaybePlural.substring(0,
|
|
||||||
unitStringMaybePlural.length() - 1);
|
|
||||||
else
|
|
||||||
unitString = unitStringMaybePlural;
|
|
||||||
String unitStringLower = unitString.toLowerCase();
|
|
||||||
String numberString = ConfigUtil.unicodeTrim(s.substring(0, s.length()
|
|
||||||
- unitStringMaybePlural.length()));
|
|
||||||
|
|
||||||
// this would be caught later anyway, but the error message
|
// this would be caught later anyway, but the error message
|
||||||
// is more helpful if we check it here.
|
// is more helpful if we check it here.
|
||||||
|
|
@ -558,40 +604,197 @@ class SimpleConfig implements Config {
|
||||||
pathForException, "No number in size-in-bytes value '"
|
pathForException, "No number in size-in-bytes value '"
|
||||||
+ input + "'");
|
+ input + "'");
|
||||||
|
|
||||||
MemoryUnit units = null;
|
MemoryUnit units = MemoryUnit.parseUnit(unitString);
|
||||||
|
|
||||||
// the short abbreviations are case-insensitive but you can't write the
|
if (units == null) {
|
||||||
// long form words in all caps.
|
throw new ConfigException.BadValue(originForException, pathForException,
|
||||||
if (unitString.equals("") || unitStringLower.equals("b")
|
"Could not parse size-in-bytes unit '" + unitString
|
||||||
|| unitString.equals("byte")) {
|
+ "' (try k, K, kB, KiB, kilobytes, kibibytes)");
|
||||||
units = MemoryUnit.BYTES;
|
|
||||||
} else if (unitStringLower.equals("k") || unitString.equals("kilobyte")) {
|
|
||||||
units = MemoryUnit.KILOBYTES;
|
|
||||||
} else if (unitStringLower.equals("m") || unitString.equals("megabyte")) {
|
|
||||||
units = MemoryUnit.MEGABYTES;
|
|
||||||
} else if (unitStringLower.equals("g") || unitString.equals("gigabyte")) {
|
|
||||||
units = MemoryUnit.GIGABYTES;
|
|
||||||
} else if (unitStringLower.equals("t") || unitString.equals("terabyte")) {
|
|
||||||
units = MemoryUnit.TERABYTES;
|
|
||||||
} else {
|
|
||||||
throw new ConfigException.BadValue(originForException,
|
|
||||||
pathForException, "Could not parse size unit '"
|
|
||||||
+ unitStringMaybePlural + "' (try b, k, m, g, t)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// if the string is purely digits, parse as an integer to avoid
|
// if the string is purely digits, parse as an integer to avoid
|
||||||
// possible precision loss;
|
// possible precision loss; otherwise as a double.
|
||||||
// otherwise as a double.
|
|
||||||
if (numberString.matches("[0-9]+")) {
|
if (numberString.matches("[0-9]+")) {
|
||||||
return Long.parseLong(numberString) * units.bytes;
|
return Long.parseLong(numberString) * units.bytes;
|
||||||
} else {
|
} else {
|
||||||
return (long) (Double.parseDouble(numberString) * units.bytes);
|
return (long) (Double.parseDouble(numberString) * units.bytes);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new ConfigException.BadValue(originForException,
|
throw new ConfigException.BadValue(originForException, pathForException,
|
||||||
pathForException, "Could not parse memory size number '"
|
"Could not parse size-in-bytes number '" + numberString + "'");
|
||||||
+ numberString + "'");
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractConfigValue peekPath(Path path) {
|
||||||
|
return root().peekPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addProblem(List<ConfigException.ValidationProblem> accumulator, Path path,
|
||||||
|
ConfigOrigin origin, String problem) {
|
||||||
|
accumulator.add(new ConfigException.ValidationProblem(path.render(), origin, problem));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getDesc(ConfigValue refValue) {
|
||||||
|
if (refValue instanceof AbstractConfigObject) {
|
||||||
|
AbstractConfigObject obj = (AbstractConfigObject) refValue;
|
||||||
|
if (obj.isEmpty())
|
||||||
|
return "object";
|
||||||
|
else
|
||||||
|
return "object with keys " + obj.keySet();
|
||||||
|
} else if (refValue instanceof SimpleConfigList) {
|
||||||
|
return "list";
|
||||||
|
} else {
|
||||||
|
return refValue.valueType().name().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addMissing(List<ConfigException.ValidationProblem> accumulator,
|
||||||
|
ConfigValue refValue, Path path, ConfigOrigin origin) {
|
||||||
|
addProblem(accumulator, path, origin, "No setting at '" + path.render() + "', expecting: "
|
||||||
|
+ getDesc(refValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addWrongType(List<ConfigException.ValidationProblem> accumulator,
|
||||||
|
ConfigValue refValue, AbstractConfigValue actual, Path path) {
|
||||||
|
addProblem(accumulator, path, actual.origin(), "Wrong value type at '" + path.render()
|
||||||
|
+ "', expecting: " + getDesc(refValue) + " but got: "
|
||||||
|
+ getDesc(actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean couldBeNull(AbstractConfigValue v) {
|
||||||
|
return DefaultTransformer.transform(v, ConfigValueType.NULL)
|
||||||
|
.valueType() == ConfigValueType.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean haveCompatibleTypes(ConfigValue reference, AbstractConfigValue value) {
|
||||||
|
if (couldBeNull((AbstractConfigValue) reference) || couldBeNull(value)) {
|
||||||
|
// we allow any setting to be null
|
||||||
|
return true;
|
||||||
|
} else if (reference instanceof AbstractConfigObject) {
|
||||||
|
if (value instanceof AbstractConfigObject) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (reference instanceof SimpleConfigList) {
|
||||||
|
if (value instanceof SimpleConfigList) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (reference instanceof ConfigString) {
|
||||||
|
// assume a string could be gotten as any non-collection type;
|
||||||
|
// allows things like getMilliseconds including domain-specific
|
||||||
|
// interpretations of strings
|
||||||
|
return true;
|
||||||
|
} else if (value instanceof ConfigString) {
|
||||||
|
// assume a string could be gotten as any non-collection type
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (reference.valueType() == value.valueType()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// path is null if we're at the root
|
||||||
|
private static void checkValidObject(Path path, AbstractConfigObject reference,
|
||||||
|
AbstractConfigObject value,
|
||||||
|
List<ConfigException.ValidationProblem> accumulator) {
|
||||||
|
for (Map.Entry<String, ConfigValue> entry : reference.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
|
||||||
|
Path childPath;
|
||||||
|
if (path != null)
|
||||||
|
childPath = Path.newKey(key).prepend(path);
|
||||||
|
else
|
||||||
|
childPath = Path.newKey(key);
|
||||||
|
|
||||||
|
AbstractConfigValue v = value.get(key);
|
||||||
|
if (v == null) {
|
||||||
|
addMissing(accumulator, entry.getValue(), childPath, value.origin());
|
||||||
|
} else {
|
||||||
|
checkValid(childPath, entry.getValue(), v, accumulator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkValid(Path path, ConfigValue reference, AbstractConfigValue value,
|
||||||
|
List<ConfigException.ValidationProblem> accumulator) {
|
||||||
|
// Unmergeable is supposed to be impossible to encounter in here
|
||||||
|
// because we check for resolve status up front.
|
||||||
|
|
||||||
|
if (haveCompatibleTypes(reference, value)) {
|
||||||
|
if (reference instanceof AbstractConfigObject && value instanceof AbstractConfigObject) {
|
||||||
|
checkValidObject(path, (AbstractConfigObject) reference,
|
||||||
|
(AbstractConfigObject) value, accumulator);
|
||||||
|
} else if (reference instanceof SimpleConfigList && value instanceof SimpleConfigList) {
|
||||||
|
SimpleConfigList listRef = (SimpleConfigList) reference;
|
||||||
|
SimpleConfigList listValue = (SimpleConfigList) value;
|
||||||
|
if (listRef.isEmpty() || listValue.isEmpty()) {
|
||||||
|
// can't verify type, leave alone
|
||||||
|
} else {
|
||||||
|
AbstractConfigValue refElement = listRef.get(0);
|
||||||
|
for (ConfigValue elem : listValue) {
|
||||||
|
AbstractConfigValue e = (AbstractConfigValue) elem;
|
||||||
|
if (!haveCompatibleTypes(refElement, e)) {
|
||||||
|
addProblem(accumulator, path, e.origin(), "List at '" + path.render()
|
||||||
|
+ "' contains wrong value type, expecting list of "
|
||||||
|
+ getDesc(refElement) + " but got element of type "
|
||||||
|
+ getDesc(e));
|
||||||
|
// don't add a problem for every last array element
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addWrongType(accumulator, reference, value, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkValid(Config reference, String... restrictToPaths) {
|
||||||
|
SimpleConfig ref = (SimpleConfig) reference;
|
||||||
|
|
||||||
|
// unresolved reference config is a bug in the caller of checkValid
|
||||||
|
if (ref.root().resolveStatus() != ResolveStatus.RESOLVED)
|
||||||
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"do not call checkValid() with an unresolved reference config, call Config.resolve()");
|
||||||
|
|
||||||
|
// unresolved config under validation is probably a bug in something,
|
||||||
|
// but our whole goal here is to check for bugs in this config, so
|
||||||
|
// BugOrBroken is not the appropriate exception.
|
||||||
|
if (root().resolveStatus() != ResolveStatus.RESOLVED)
|
||||||
|
throw new ConfigException.NotResolved(
|
||||||
|
"config has unresolved substitutions; must call Config.resolve()");
|
||||||
|
|
||||||
|
// Now we know that both reference and this config are resolved
|
||||||
|
|
||||||
|
List<ConfigException.ValidationProblem> problems = new ArrayList<ConfigException.ValidationProblem>();
|
||||||
|
|
||||||
|
if (restrictToPaths.length == 0) {
|
||||||
|
checkValidObject(null, ref.root(), root(), problems);
|
||||||
|
} else {
|
||||||
|
for (String p : restrictToPaths) {
|
||||||
|
Path path = Path.newPath(p);
|
||||||
|
AbstractConfigValue refValue = ref.peekPath(path);
|
||||||
|
if (refValue != null) {
|
||||||
|
AbstractConfigValue child = peekPath(path);
|
||||||
|
if (child != null) {
|
||||||
|
checkValid(path, refValue, child, problems);
|
||||||
|
} else {
|
||||||
|
addMissing(problems, refValue, path, origin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!problems.isEmpty()) {
|
||||||
|
throw new ConfigException.ValidationFailed(problems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
|
||||||
import com.typesafe.config.ConfigException;
|
|
||||||
import com.typesafe.config.ConfigList;
|
import com.typesafe.config.ConfigList;
|
||||||
import com.typesafe.config.ConfigOrigin;
|
import com.typesafe.config.ConfigOrigin;
|
||||||
import com.typesafe.config.ConfigResolveOptions;
|
import com.typesafe.config.ConfigResolveOptions;
|
||||||
|
|
@ -68,8 +67,9 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// once the new list is created, all elements
|
// once the new list is created, all elements
|
||||||
// have to go in it.
|
// have to go in it. if modifyChild returned
|
||||||
if (changed != null) {
|
// null, we drop that element.
|
||||||
|
if (changed != null && modified != null) {
|
||||||
changed.add(modified);
|
changed.add(modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,9 +77,6 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed != null) {
|
if (changed != null) {
|
||||||
if (changed.size() != value.size())
|
|
||||||
throw new ConfigException.BugOrBroken(
|
|
||||||
"substituted list's size doesn't match");
|
|
||||||
return new SimpleConfigList(origin(), changed, newResolveStatus);
|
return new SimpleConfigList(origin(), changed, newResolveStatus);
|
||||||
} else {
|
} else {
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -135,18 +132,34 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||||
StringBuilder sb = new StringBuilder();
|
if (value.isEmpty()) {
|
||||||
sb.append(valueType().name());
|
sb.append("[]");
|
||||||
sb.append("(");
|
} else {
|
||||||
for (ConfigValue e : value) {
|
sb.append("[");
|
||||||
sb.append(e.toString());
|
if (formatted)
|
||||||
sb.append(",");
|
sb.append('\n');
|
||||||
|
for (AbstractConfigValue v : value) {
|
||||||
|
if (formatted) {
|
||||||
|
indent(sb, indent + 1);
|
||||||
|
sb.append("# ");
|
||||||
|
sb.append(v.origin().description());
|
||||||
|
sb.append("\n");
|
||||||
|
indent(sb, indent + 1);
|
||||||
|
}
|
||||||
|
v.render(sb, indent + 1, formatted);
|
||||||
|
sb.append(",");
|
||||||
|
if (formatted)
|
||||||
|
sb.append('\n');
|
||||||
|
}
|
||||||
|
sb.setLength(sb.length() - 1); // chop or newline
|
||||||
|
if (formatted) {
|
||||||
|
sb.setLength(sb.length() - 1); // also chop comma
|
||||||
|
sb.append('\n');
|
||||||
|
indent(sb, indent);
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
}
|
}
|
||||||
if (!value.isEmpty())
|
|
||||||
sb.setLength(sb.length() - 1); // chop comma
|
|
||||||
sb.append(")");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -160,7 +173,7 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigValue get(int index) {
|
public AbstractConfigValue get(int index) {
|
||||||
return value.get(index);
|
return value.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,8 +112,8 @@ final class SimpleConfigObject extends AbstractConfigObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
final private static String EMPTY_NAME = "empty config";
|
final private static String EMPTY_NAME = "empty config";
|
||||||
final private static SimpleConfigObject emptyInstance = empty(new SimpleConfigOrigin(
|
final private static SimpleConfigObject emptyInstance = empty(SimpleConfigOrigin
|
||||||
EMPTY_NAME));
|
.newSimple(EMPTY_NAME));
|
||||||
|
|
||||||
final static SimpleConfigObject empty() {
|
final static SimpleConfigObject empty() {
|
||||||
return emptyInstance;
|
return emptyInstance;
|
||||||
|
|
@ -128,7 +128,7 @@ final class SimpleConfigObject extends AbstractConfigObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
final static SimpleConfigObject emptyMissing(ConfigOrigin baseOrigin) {
|
final static SimpleConfigObject emptyMissing(ConfigOrigin baseOrigin) {
|
||||||
return new SimpleConfigObject(new SimpleConfigOrigin(
|
return new SimpleConfigObject(SimpleConfigOrigin.newSimple(
|
||||||
baseOrigin.description() + " (not found)"),
|
baseOrigin.description() + " (not found)"),
|
||||||
Collections.<String, AbstractConfigValue> emptyMap());
|
Collections.<String, AbstractConfigValue> emptyMap());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,101 @@
|
||||||
*/
|
*/
|
||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.typesafe.config.ConfigException;
|
||||||
import com.typesafe.config.ConfigOrigin;
|
import com.typesafe.config.ConfigOrigin;
|
||||||
|
|
||||||
|
// it would be cleaner to have a class hierarchy for various origin types,
|
||||||
|
// but was hoping this would be enough simpler to be a little messy. eh.
|
||||||
final class SimpleConfigOrigin implements ConfigOrigin {
|
final class SimpleConfigOrigin implements ConfigOrigin {
|
||||||
|
|
||||||
final private String description;
|
final private String description;
|
||||||
|
final private int lineNumber;
|
||||||
|
final private int endLineNumber;
|
||||||
|
final private OriginType originType;
|
||||||
|
final private String urlOrNull;
|
||||||
|
|
||||||
SimpleConfigOrigin(String description) {
|
protected SimpleConfigOrigin(String description, int lineNumber, int endLineNumber,
|
||||||
|
OriginType originType,
|
||||||
|
String urlOrNull) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
this.lineNumber = lineNumber;
|
||||||
|
this.endLineNumber = endLineNumber;
|
||||||
|
this.originType = originType;
|
||||||
|
this.urlOrNull = urlOrNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newSimple(String description) {
|
||||||
|
return new SimpleConfigOrigin(description, -1, -1, OriginType.GENERIC, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newFile(String filename) {
|
||||||
|
String url;
|
||||||
|
try {
|
||||||
|
url = (new File(filename)).toURI().toURL().toExternalForm();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
url = null;
|
||||||
|
}
|
||||||
|
return new SimpleConfigOrigin(filename, -1, -1, OriginType.FILE, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newURL(URL url) {
|
||||||
|
String u = url.toExternalForm();
|
||||||
|
return new SimpleConfigOrigin(u, -1, -1, OriginType.URL, u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newResource(String resource, URL url) {
|
||||||
|
return new SimpleConfigOrigin(resource, -1, -1, OriginType.RESOURCE,
|
||||||
|
url != null ? url.toExternalForm() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleConfigOrigin newResource(String resource) {
|
||||||
|
return newResource(resource, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigOrigin setLineNumber(int lineNumber) {
|
||||||
|
if (lineNumber == this.lineNumber && lineNumber == this.endLineNumber) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return new SimpleConfigOrigin(this.description, lineNumber, lineNumber,
|
||||||
|
this.originType, this.urlOrNull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleConfigOrigin addURL(URL url) {
|
||||||
|
return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber, this.originType,
|
||||||
|
url != null ? url.toExternalForm() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String description() {
|
public String description() {
|
||||||
return description;
|
// not putting the URL in here for files and resources, because people
|
||||||
|
// parsing "file: line" syntax would hit the ":" in the URL.
|
||||||
|
if (lineNumber < 0) {
|
||||||
|
return description;
|
||||||
|
} else if (endLineNumber == lineNumber) {
|
||||||
|
return description + ": " + lineNumber;
|
||||||
|
} else {
|
||||||
|
return description + ": " + lineNumber + "-" + endLineNumber;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (other instanceof SimpleConfigOrigin) {
|
if (other instanceof SimpleConfigOrigin) {
|
||||||
return this.description
|
SimpleConfigOrigin otherOrigin = (SimpleConfigOrigin) other;
|
||||||
.equals(((SimpleConfigOrigin) other).description);
|
|
||||||
|
return this.description.equals(otherOrigin.description)
|
||||||
|
&& this.lineNumber == otherOrigin.lineNumber
|
||||||
|
&& this.endLineNumber == otherOrigin.endLineNumber
|
||||||
|
&& this.originType == otherOrigin.originType
|
||||||
|
&& ConfigUtil.equalsHandlingNull(this.urlOrNull, otherOrigin.urlOrNull);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -30,11 +105,201 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return description.hashCode();
|
int h = 41 * (41 + description.hashCode());
|
||||||
|
h = 41 * (h + lineNumber);
|
||||||
|
h = 41 * (h + endLineNumber);
|
||||||
|
h = 41 * (h + originType.hashCode());
|
||||||
|
if (urlOrNull != null)
|
||||||
|
h = 41 * (h + urlOrNull.hashCode());
|
||||||
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ConfigOrigin(" + description + ")";
|
// the url is only really useful on top of description for resources
|
||||||
|
if (originType == OriginType.RESOURCE && urlOrNull != null) {
|
||||||
|
return "ConfigOrigin(" + description + "," + urlOrNull + ")";
|
||||||
|
} else {
|
||||||
|
return "ConfigOrigin(" + description + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String filename() {
|
||||||
|
if (originType == OriginType.FILE) {
|
||||||
|
return description;
|
||||||
|
} else if (urlOrNull != null) {
|
||||||
|
URL url;
|
||||||
|
try {
|
||||||
|
url = new URL(urlOrNull);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (url.getProtocol().equals("file")) {
|
||||||
|
return url.getFile();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL url() {
|
||||||
|
if (urlOrNull == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return new URL(urlOrNull);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resource() {
|
||||||
|
if (originType == OriginType.RESOURCE) {
|
||||||
|
return description;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lineNumber() {
|
||||||
|
return lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String MERGE_OF_PREFIX = "merge of ";
|
||||||
|
|
||||||
|
private static SimpleConfigOrigin mergeTwo(SimpleConfigOrigin a, SimpleConfigOrigin b) {
|
||||||
|
String mergedDesc;
|
||||||
|
int mergedStartLine;
|
||||||
|
int mergedEndLine;
|
||||||
|
|
||||||
|
OriginType mergedType;
|
||||||
|
if (a.originType == b.originType) {
|
||||||
|
mergedType = a.originType;
|
||||||
|
} else {
|
||||||
|
mergedType = OriginType.GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first use the "description" field which has no line numbers
|
||||||
|
// cluttering it.
|
||||||
|
String aDesc = a.description;
|
||||||
|
String bDesc = b.description;
|
||||||
|
if (aDesc.startsWith(MERGE_OF_PREFIX))
|
||||||
|
aDesc = aDesc.substring(MERGE_OF_PREFIX.length());
|
||||||
|
if (bDesc.startsWith(MERGE_OF_PREFIX))
|
||||||
|
bDesc = bDesc.substring(MERGE_OF_PREFIX.length());
|
||||||
|
|
||||||
|
if (aDesc.equals(bDesc)) {
|
||||||
|
mergedDesc = aDesc;
|
||||||
|
|
||||||
|
if (a.lineNumber < 0)
|
||||||
|
mergedStartLine = b.lineNumber;
|
||||||
|
else if (b.lineNumber < 0)
|
||||||
|
mergedStartLine = a.lineNumber;
|
||||||
|
else
|
||||||
|
mergedStartLine = Math.min(a.lineNumber, b.lineNumber);
|
||||||
|
|
||||||
|
mergedEndLine = Math.max(a.endLineNumber, b.endLineNumber);
|
||||||
|
} else {
|
||||||
|
// this whole merge song-and-dance was intended to avoid this case
|
||||||
|
// whenever possible, but we've lost. Now we have to lose some
|
||||||
|
// structured information and cram into a string.
|
||||||
|
|
||||||
|
// description() method includes line numbers, so use it instead
|
||||||
|
// of description field.
|
||||||
|
String aFull = a.description();
|
||||||
|
String bFull = b.description();
|
||||||
|
if (aFull.startsWith(MERGE_OF_PREFIX))
|
||||||
|
aFull = aFull.substring(MERGE_OF_PREFIX.length());
|
||||||
|
if (bFull.startsWith(MERGE_OF_PREFIX))
|
||||||
|
bFull = bFull.substring(MERGE_OF_PREFIX.length());
|
||||||
|
|
||||||
|
mergedDesc = MERGE_OF_PREFIX + aFull + "," + bFull;
|
||||||
|
|
||||||
|
mergedStartLine = -1;
|
||||||
|
mergedEndLine = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mergedURL;
|
||||||
|
if (ConfigUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull)) {
|
||||||
|
mergedURL = a.urlOrNull;
|
||||||
|
} else {
|
||||||
|
mergedURL = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleConfigOrigin(mergedDesc, mergedStartLine, mergedEndLine, mergedType,
|
||||||
|
mergedURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int similarity(SimpleConfigOrigin a, SimpleConfigOrigin b) {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (a.originType == b.originType)
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
if (a.description.equals(b.description)) {
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
// only count these if the description field (which is the file
|
||||||
|
// or resource name) also matches.
|
||||||
|
if (a.lineNumber == b.lineNumber)
|
||||||
|
count += 1;
|
||||||
|
if (a.endLineNumber == b.endLineNumber)
|
||||||
|
count += 1;
|
||||||
|
if (ConfigUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull))
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this picks the best pair to merge, because the pair has the most in
|
||||||
|
// common. we want to merge two lines in the same file rather than something
|
||||||
|
// else with one of the lines; because two lines in the same file can be
|
||||||
|
// better consolidated.
|
||||||
|
private static SimpleConfigOrigin mergeThree(SimpleConfigOrigin a, SimpleConfigOrigin b,
|
||||||
|
SimpleConfigOrigin c) {
|
||||||
|
if (similarity(a, b) >= similarity(b, c)) {
|
||||||
|
return mergeTwo(mergeTwo(a, b), c);
|
||||||
|
} else {
|
||||||
|
return mergeTwo(a, mergeTwo(b, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConfigOrigin mergeOrigins(Collection<? extends ConfigOrigin> stack) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
throw new ConfigException.BugOrBroken("can't merge empty list of origins");
|
||||||
|
} else if (stack.size() == 1) {
|
||||||
|
return stack.iterator().next();
|
||||||
|
} else if (stack.size() == 2) {
|
||||||
|
Iterator<? extends ConfigOrigin> i = stack.iterator();
|
||||||
|
return mergeTwo((SimpleConfigOrigin) i.next(), (SimpleConfigOrigin) i.next());
|
||||||
|
} else {
|
||||||
|
List<SimpleConfigOrigin> remaining = new ArrayList<SimpleConfigOrigin>();
|
||||||
|
for (ConfigOrigin o : stack) {
|
||||||
|
remaining.add((SimpleConfigOrigin) o);
|
||||||
|
}
|
||||||
|
while (remaining.size() > 2) {
|
||||||
|
SimpleConfigOrigin c = remaining.get(remaining.size() - 1);
|
||||||
|
remaining.remove(remaining.size() - 1);
|
||||||
|
SimpleConfigOrigin b = remaining.get(remaining.size() - 1);
|
||||||
|
remaining.remove(remaining.size() - 1);
|
||||||
|
SimpleConfigOrigin a = remaining.get(remaining.size() - 1);
|
||||||
|
remaining.remove(remaining.size() - 1);
|
||||||
|
|
||||||
|
SimpleConfigOrigin merged = mergeThree(a, b, c);
|
||||||
|
|
||||||
|
remaining.add(merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be down to either 1 or 2
|
||||||
|
return mergeOrigins(remaining);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
|
final class SubstitutionExpression {
|
||||||
|
|
||||||
|
final private Path path;
|
||||||
|
final private boolean optional;
|
||||||
|
|
||||||
|
SubstitutionExpression(Path path, boolean optional) {
|
||||||
|
this.path = path;
|
||||||
|
this.optional = optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path path() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean optional() {
|
||||||
|
return optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubstitutionExpression changePath(Path newPath) {
|
||||||
|
return new SubstitutionExpression(newPath, optional);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "${" + (optional ? "?" : "") + path.render() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof SubstitutionExpression) {
|
||||||
|
SubstitutionExpression otherExp = (SubstitutionExpression) other;
|
||||||
|
return otherExp.path.equals(this.path) && otherExp.optional == this.optional;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = 41 * (41 + path.hashCode());
|
||||||
|
h = 41 * (h + (optional ? 1 : 0));
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,8 @@ import com.typesafe.config.ConfigResolveOptions;
|
||||||
*/
|
*/
|
||||||
final class SubstitutionResolver {
|
final class SubstitutionResolver {
|
||||||
final private AbstractConfigObject root;
|
final private AbstractConfigObject root;
|
||||||
|
// note that we can resolve things to undefined (represented as Java null,
|
||||||
|
// rather than ConfigNull) so this map can have null values.
|
||||||
final private Map<AbstractConfigValue, AbstractConfigValue> memos;
|
final private Map<AbstractConfigValue, AbstractConfigValue> memos;
|
||||||
|
|
||||||
SubstitutionResolver(AbstractConfigObject root) {
|
SubstitutionResolver(AbstractConfigObject root) {
|
||||||
|
|
@ -31,9 +33,11 @@ final class SubstitutionResolver {
|
||||||
} else {
|
} else {
|
||||||
AbstractConfigValue resolved = original.resolveSubstitutions(this,
|
AbstractConfigValue resolved = original.resolveSubstitutions(this,
|
||||||
depth, options);
|
depth, options);
|
||||||
if (resolved.resolveStatus() != ResolveStatus.RESOLVED)
|
if (resolved != null) {
|
||||||
throw new ConfigException.BugOrBroken(
|
if (resolved.resolveStatus() != ResolveStatus.RESOLVED)
|
||||||
"resolveSubstitutions() did not give us a resolved object");
|
throw new ConfigException.BugOrBroken(
|
||||||
|
"resolveSubstitutions() did not give us a resolved object");
|
||||||
|
}
|
||||||
memos.put(original, resolved);
|
memos.put(original, resolved);
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,8 +219,7 @@ final class Tokenizer {
|
||||||
|
|
||||||
private static ConfigOrigin lineOrigin(ConfigOrigin baseOrigin,
|
private static ConfigOrigin lineOrigin(ConfigOrigin baseOrigin,
|
||||||
int lineNumber) {
|
int lineNumber) {
|
||||||
return new SimpleConfigOrigin(baseOrigin.description() + ": line "
|
return ((SimpleConfigOrigin) baseOrigin).setLineNumber(lineNumber);
|
||||||
+ lineNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// chars JSON allows a number to start with
|
// chars JSON allows a number to start with
|
||||||
|
|
@ -228,7 +227,7 @@ final class Tokenizer {
|
||||||
// chars JSON allows to be part of a number
|
// chars JSON allows to be part of a number
|
||||||
static final String numberChars = "0123456789eE+-.";
|
static final String numberChars = "0123456789eE+-.";
|
||||||
// chars that stop an unquoted string
|
// chars that stop an unquoted string
|
||||||
static final String notInUnquotedText = "$\"{}[]:=,\\+#";
|
static final String notInUnquotedText = "$\"{}[]:=,+#`^?!@*&\\";
|
||||||
|
|
||||||
// The rules here are intended to maximize convenience while
|
// The rules here are intended to maximize convenience while
|
||||||
// avoiding confusion with real valid JSON. Basically anything
|
// avoiding confusion with real valid JSON. Basically anything
|
||||||
|
|
@ -404,6 +403,14 @@ final class Tokenizer {
|
||||||
throw parseError("'$' not followed by {");
|
throw parseError("'$' not followed by {");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean optional = false;
|
||||||
|
c = nextCharSkippingComments();
|
||||||
|
if (c == '?') {
|
||||||
|
optional = true;
|
||||||
|
} else {
|
||||||
|
putBack(c);
|
||||||
|
}
|
||||||
|
|
||||||
WhitespaceSaver saver = new WhitespaceSaver();
|
WhitespaceSaver saver = new WhitespaceSaver();
|
||||||
List<Token> expression = new ArrayList<Token>();
|
List<Token> expression = new ArrayList<Token>();
|
||||||
|
|
||||||
|
|
@ -428,7 +435,7 @@ final class Tokenizer {
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
return Tokens.newSubstitution(origin, expression);
|
return Tokens.newSubstitution(origin, optional, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token pullNextToken(WhitespaceSaver saver) {
|
private Token pullNextToken(WhitespaceSaver saver) {
|
||||||
|
|
|
||||||
|
|
@ -125,11 +125,13 @@ final class Tokens {
|
||||||
// This is not a Value, because it requires special processing
|
// This is not a Value, because it requires special processing
|
||||||
static private class Substitution extends Token {
|
static private class Substitution extends Token {
|
||||||
final private ConfigOrigin origin;
|
final private ConfigOrigin origin;
|
||||||
|
final private boolean optional;
|
||||||
final private List<Token> value;
|
final private List<Token> value;
|
||||||
|
|
||||||
Substitution(ConfigOrigin origin, List<Token> expression) {
|
Substitution(ConfigOrigin origin, boolean optional, List<Token> expression) {
|
||||||
super(TokenType.SUBSTITUTION);
|
super(TokenType.SUBSTITUTION);
|
||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
|
this.optional = optional;
|
||||||
this.value = expression;
|
this.value = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,6 +139,10 @@ final class Tokens {
|
||||||
return origin;
|
return origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean optional() {
|
||||||
|
return optional;
|
||||||
|
}
|
||||||
|
|
||||||
List<Token> value() {
|
List<Token> value() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
@ -237,6 +243,15 @@ final class Tokens {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean getSubstitutionOptional(Token token) {
|
||||||
|
if (token instanceof Substitution) {
|
||||||
|
return ((Substitution) token).optional();
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BugOrBroken("tried to get substitution optionality from "
|
||||||
|
+ token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final static Token START = new Token(TokenType.START);
|
final static Token START = new Token(TokenType.START);
|
||||||
final static Token END = new Token(TokenType.END);
|
final static Token END = new Token(TokenType.END);
|
||||||
final static Token COMMA = new Token(TokenType.COMMA);
|
final static Token COMMA = new Token(TokenType.COMMA);
|
||||||
|
|
@ -255,8 +270,8 @@ final class Tokens {
|
||||||
return new UnquotedText(origin, s);
|
return new UnquotedText(origin, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Token newSubstitution(ConfigOrigin origin, List<Token> expression) {
|
static Token newSubstitution(ConfigOrigin origin, boolean optional, List<Token> expression) {
|
||||||
return new Substitution(origin, expression);
|
return new Substitution(origin, optional, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Token newValue(AbstractConfigValue value) {
|
static Token newValue(AbstractConfigValue value) {
|
||||||
|
|
|
||||||
42
akka-actor/src/main/java/com/typesafe/config/package.html
Normal file
42
akka-actor/src/main/java/com/typesafe/config/package.html
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body bgcolor="white">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
An API for loading and using configuration files, see <a href="https://github.com/havocp/config/">the project site</a>
|
||||||
|
for more information.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use
|
||||||
|
it with methods in the {@link com.typesafe.config.Config} interface.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
An application can simply call {@link com.typesafe.config.ConfigFactory#load()} and place
|
||||||
|
its configuration in "application.conf" on the classpath.
|
||||||
|
If you use the default configuration from {@link com.typesafe.config.ConfigFactory#load()}
|
||||||
|
there's no need to pass a configuration to your libraries
|
||||||
|
and frameworks, as long as they all default to this same default, which they should.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
A library or framework should ship a file "reference.conf" in its jar, and allow an application to pass in a
|
||||||
|
{@link com.typesafe.config.Config} to be used for the library. If no {@link com.typesafe.config.Config} is provided,
|
||||||
|
call {@link com.typesafe.config.ConfigFactory#load()}
|
||||||
|
to get the default one. Typically a library might offer two constructors, one with a <code>Config</code> parameter
|
||||||
|
and one which uses {@link com.typesafe.config.ConfigFactory#load()}.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You can find an example app and library <a href="https://github.com/havocp/config/tree/master/examples">on GitHub</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
############################################
|
|
||||||
# Akka Serialization Reference Config File #
|
|
||||||
############################################
|
|
||||||
|
|
||||||
# This the reference config file has all the default settings.
|
|
||||||
# Make your edits/overrides in your akka.conf.
|
|
||||||
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
|
|
||||||
# Entries for pluggable serializers and their bindings. If a binding for a specific class is not found,
|
|
||||||
# then the default serializer (Java serialization) is used.
|
|
||||||
#
|
|
||||||
serializers {
|
|
||||||
# java = "akka.serialization.JavaSerializer"
|
|
||||||
# proto = "akka.testing.ProtobufSerializer"
|
|
||||||
# sjson = "akka.testing.SJSONSerializer"
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
# serialization-bindings {
|
|
||||||
# java = ["akka.serialization.SerializeSpec$Address",
|
|
||||||
# "akka.serialization.MyJavaSerializableActor",
|
|
||||||
# "akka.serialization.MyStatelessActorWithMessagesInMailbox",
|
|
||||||
# "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
|
||||||
# sjson = ["akka.serialization.SerializeSpec$Person"]
|
|
||||||
# proto = ["com.google.protobuf.Message",
|
|
||||||
# "akka.actor.ProtobufProtocol$MyMessage"]
|
|
||||||
# }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -16,7 +16,7 @@ akka {
|
||||||
loglevel = "INFO" # Options: ERROR, WARNING, INFO, DEBUG
|
loglevel = "INFO" # Options: ERROR, WARNING, INFO, DEBUG
|
||||||
# this level is used by the configured loggers (see "event-handlers") as soon
|
# this level is used by the configured loggers (see "event-handlers") as soon
|
||||||
# as they have been started; before that, see "stdout-loglevel"
|
# as they have been started; before that, see "stdout-loglevel"
|
||||||
stdout-loglevel = "INFO" # Loglevel for the very basic logger activated during AkkaApplication startup
|
stdout-loglevel = "WARNING" # Loglevel for the very basic logger activated during AkkaApplication startup
|
||||||
# FIXME: Is there any sensible reason why we have 2 different log levels?
|
# FIXME: Is there any sensible reason why we have 2 different log levels?
|
||||||
|
|
||||||
logConfigOnStart = off # Log the complete configuration at INFO level when the actor system is started.
|
logConfigOnStart = off # Log the complete configuration at INFO level when the actor system is started.
|
||||||
|
|
@ -125,6 +125,26 @@ akka {
|
||||||
fsm = off # enable DEBUG logging of all LoggingFSMs for events, transitions and timers
|
fsm = off # enable DEBUG logging of all LoggingFSMs for events, transitions and timers
|
||||||
event-stream = off # enable DEBUG logging of subscription changes on the eventStream
|
event-stream = off # enable DEBUG logging of subscription changes on the eventStream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Entries for pluggable serializers and their bindings. If a binding for a specific class is not found,
|
||||||
|
# then the default serializer (Java serialization) is used.
|
||||||
|
#
|
||||||
|
serializers {
|
||||||
|
# java = "akka.serialization.JavaSerializer"
|
||||||
|
# proto = "akka.testing.ProtobufSerializer"
|
||||||
|
# sjson = "akka.testing.SJSONSerializer"
|
||||||
|
default = "akka.serialization.JavaSerializer"
|
||||||
|
}
|
||||||
|
|
||||||
|
# serialization-bindings {
|
||||||
|
# java = ["akka.serialization.SerializeSpec$Address",
|
||||||
|
# "akka.serialization.MyJavaSerializableActor",
|
||||||
|
# "akka.serialization.MyStatelessActorWithMessagesInMailbox",
|
||||||
|
# "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
||||||
|
# sjson = ["akka.serialization.SerializeSpec$Person"]
|
||||||
|
# proto = ["com.google.protobuf.Message",
|
||||||
|
# "akka.actor.ProtobufProtocol$MyMessage"]
|
||||||
|
# }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,6 +162,5 @@ akka {
|
||||||
tickDuration = 100ms
|
tickDuration = 100ms
|
||||||
ticksPerWheel = 512
|
ticksPerWheel = 512
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -13,9 +13,10 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import java.util.concurrent.TimeUnit.NANOSECONDS
|
import java.util.concurrent.TimeUnit.NANOSECONDS
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import com.typesafe.config.ConfigParseOptions
|
||||||
|
import com.typesafe.config.ConfigResolveOptions
|
||||||
|
import com.typesafe.config.ConfigException
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import akka.util.{ Helpers, Duration, ReflectiveAccess }
|
import akka.util.{ Helpers, Duration, ReflectiveAccess }
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
|
@ -43,17 +44,32 @@ object ActorSystem {
|
||||||
def create(name: String, config: Config): ActorSystem = apply(name, config)
|
def create(name: String, config: Config): ActorSystem = apply(name, config)
|
||||||
def apply(name: String, config: Config): ActorSystem = new ActorSystemImpl(name, config).start()
|
def apply(name: String, config: Config): ActorSystem = new ActorSystemImpl(name, config).start()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the standard default Config from ConfigFactory.load(), since none is provided.
|
||||||
|
*/
|
||||||
def create(name: String): ActorSystem = apply(name)
|
def create(name: String): ActorSystem = apply(name)
|
||||||
def apply(name: String): ActorSystem = apply(name, DefaultConfigurationLoader.defaultConfig)
|
/**
|
||||||
|
* Uses the standard default Config from ConfigFactory.load(), since none is provided.
|
||||||
|
*/
|
||||||
|
def apply(name: String): ActorSystem = apply(name, ConfigFactory.load())
|
||||||
|
|
||||||
def create(): ActorSystem = apply()
|
def create(): ActorSystem = apply()
|
||||||
def apply(): ActorSystem = apply("default")
|
def apply(): ActorSystem = apply("default")
|
||||||
|
|
||||||
class Settings(cfg: Config) {
|
class Settings(cfg: Config) {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-actor-reference.conf",
|
// Verify that the Config is sane and has our reference config.
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
val config: Config =
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-actor").withFallback(cfg).withFallback(referenceConfig).resolve()
|
try {
|
||||||
|
cfg.checkValid(ConfigFactory.defaultReference, "akka")
|
||||||
|
cfg
|
||||||
|
} catch {
|
||||||
|
case e: ConfigException ⇒
|
||||||
|
// try again with added defaultReference
|
||||||
|
val cfg2 = cfg.withFallback(ConfigFactory.defaultReference)
|
||||||
|
cfg2.checkValid(ConfigFactory.defaultReference, "akka")
|
||||||
|
cfg2
|
||||||
|
}
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import config._
|
import config._
|
||||||
|
|
@ -102,9 +118,13 @@ object ActorSystem {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object DefaultConfigurationLoader {
|
// TODO move to migration kit
|
||||||
|
object OldConfigurationLoader {
|
||||||
|
|
||||||
val defaultConfig: Config = fromProperties orElse fromClasspath orElse fromHome getOrElse emptyConfig
|
val defaultConfig: Config = {
|
||||||
|
val cfg = fromProperties orElse fromClasspath orElse fromHome getOrElse emptyConfig
|
||||||
|
cfg.withFallback(ConfigFactory.defaultReference).resolve(ConfigResolveOptions.defaults)
|
||||||
|
}
|
||||||
|
|
||||||
// file extensions (.conf, .json, .properties), are handled by parseFileAnySyntax
|
// file extensions (.conf, .json, .properties), are handled by parseFileAnySyntax
|
||||||
val defaultLocation: String = (systemMode orElse envMode).map("akka." + _).getOrElse("akka")
|
val defaultLocation: String = (systemMode orElse envMode).map("akka." + _).getOrElse("akka")
|
||||||
|
|
@ -130,7 +150,7 @@ object ActorSystem {
|
||||||
|
|
||||||
private def fromClasspath = try {
|
private def fromClasspath = try {
|
||||||
Option(ConfigFactory.systemProperties.withFallback(
|
Option(ConfigFactory.systemProperties.withFallback(
|
||||||
ConfigFactory.parseResourceAnySyntax(ActorSystem.getClass, "/" + defaultLocation, configParseOptions)))
|
ConfigFactory.parseResourcesAnySyntax(ActorSystem.getClass, "/" + defaultLocation, configParseOptions)))
|
||||||
} catch { case _ ⇒ None }
|
} catch { case _ ⇒ None }
|
||||||
|
|
||||||
private def fromHome = try {
|
private def fromHome = try {
|
||||||
|
|
@ -273,7 +293,7 @@ abstract class ActorSystem extends ActorRefFactory {
|
||||||
def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean
|
def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActorSystemImpl(val name: String, val applicationConfig: Config) extends ActorSystem {
|
class ActorSystemImpl(val name: String, applicationConfig: Config) extends ActorSystem {
|
||||||
|
|
||||||
import ActorSystem._
|
import ActorSystem._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class Deployer(val settings: ActorSystem.Settings, val eventStream: EventStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
settings.config.getConfig("akka.actor.deployment").toObject.keySet.asScala
|
settings.config.getConfig("akka.actor.deployment").toValue.keySet.asScala
|
||||||
.filterNot("default" ==)
|
.filterNot("default" ==)
|
||||||
.map(path ⇒ pathSubstring(path))
|
.map(path ⇒ pathSubstring(path))
|
||||||
.toSet.toList // toSet to force uniqueness
|
.toSet.toList // toSet to force uniqueness
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ package akka.serialization
|
||||||
import akka.AkkaException
|
import akka.AkkaException
|
||||||
import akka.util.ReflectiveAccess
|
import akka.util.ReflectiveAccess
|
||||||
import scala.util.DynamicVariable
|
import scala.util.DynamicVariable
|
||||||
import com.typesafe.config.{ ConfigRoot, ConfigParseOptions, ConfigFactory, Config }
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.Config._
|
|
||||||
import akka.config.ConfigurationException
|
import akka.config.ConfigurationException
|
||||||
import akka.actor.{ Extension, ActorSystem, ActorSystemImpl }
|
import akka.actor.{ Extension, ActorSystem, ActorSystemImpl }
|
||||||
|
|
||||||
|
|
@ -19,11 +18,7 @@ object Serialization {
|
||||||
// TODO ensure that these are always set (i.e. withValue()) when doing deserialization
|
// TODO ensure that these are always set (i.e. withValue()) when doing deserialization
|
||||||
val currentSystem = new DynamicVariable[ActorSystemImpl](null)
|
val currentSystem = new DynamicVariable[ActorSystemImpl](null)
|
||||||
|
|
||||||
class Settings(cfg: Config) {
|
class Settings(val config: Config) {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-serialization-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-serialization").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import config._
|
import config._
|
||||||
|
|
@ -37,7 +32,7 @@ object Serialization {
|
||||||
hasPath(configPath) match {
|
hasPath(configPath) match {
|
||||||
case false ⇒ Map()
|
case false ⇒ Map()
|
||||||
case true ⇒
|
case true ⇒
|
||||||
val serializationBindings: Map[String, Seq[String]] = getConfig(configPath).toObject.unwrapped.asScala.toMap.map {
|
val serializationBindings: Map[String, Seq[String]] = getConfig(configPath).toValue.unwrapped.asScala.toMap.map {
|
||||||
case (k: String, v: java.util.Collection[_]) ⇒ (k -> v.asScala.toSeq.asInstanceOf[Seq[String]])
|
case (k: String, v: java.util.Collection[_]) ⇒ (k -> v.asScala.toSeq.asInstanceOf[Seq[String]])
|
||||||
case invalid ⇒ throw new ConfigurationException("Invalid serialization-bindings [%s]".format(invalid))
|
case invalid ⇒ throw new ConfigurationException("Invalid serialization-bindings [%s]".format(invalid))
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +42,7 @@ object Serialization {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def toStringMap(mapConfig: Config): Map[String, String] =
|
private def toStringMap(mapConfig: Config): Map[String, String] =
|
||||||
mapConfig.toObject.unwrapped.asScala.toMap.map { case (k, v) ⇒ (k, v.toString) }
|
mapConfig.toValue.unwrapped.asScala.toMap.map { case (k, v) ⇒ (k, v.toString) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +53,7 @@ object Serialization {
|
||||||
class Serialization(val system: ActorSystemImpl) extends Extension {
|
class Serialization(val system: ActorSystemImpl) extends Extension {
|
||||||
import Serialization._
|
import Serialization._
|
||||||
|
|
||||||
val settings = new Settings(system.applicationConfig)
|
val settings = new Settings(system.settings.config)
|
||||||
|
|
||||||
//TODO document me
|
//TODO document me
|
||||||
def serialize(o: AnyRef): Either[Exception, Array[Byte]] =
|
def serialize(o: AnyRef): Either[Exception, Array[Byte]] =
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import org.scalatest.matchers.MustMatchers
|
||||||
//#imports
|
//#imports
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
|
|
||||||
|
|
@ -21,7 +20,7 @@ class ConfigDocSpec extends WordSpec {
|
||||||
nr-of-instances = 3
|
nr-of-instances = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""", ConfigParseOptions.defaults)
|
""")
|
||||||
val system = ActorSystem("MySystem", ConfigFactory.systemProperties.withFallback(customConf))
|
val system = ActorSystem("MySystem", ConfigFactory.systemProperties.withFallback(customConf))
|
||||||
//#custom-config
|
//#custom-config
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ configuration files that you see below. You can specify your own configuration f
|
||||||
property in the reference config. You only have to define the properties that differ from the default
|
property in the reference config. You only have to define the properties that differ from the default
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
|
FIXME: These default locations has changed
|
||||||
|
|
||||||
The location of the config file to use can be specified in various ways:
|
The location of the config file to use can be specified in various ways:
|
||||||
|
|
||||||
* Define the ``-Dakka.config=...`` system property parameter with a file path to configuration file.
|
* Define the ``-Dakka.config=...`` system property parameter with a file path to configuration file.
|
||||||
|
|
@ -44,47 +46,42 @@ Each Akka module has a reference configuration file with the default values.
|
||||||
|
|
||||||
*akka-actor:*
|
*akka-actor:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-actor/src/main/resources/akka-actor-reference.conf
|
.. literalinclude:: ../../akka-actor/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
*akka-remote:*
|
*akka-remote:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-remote/src/main/resources/akka-remote-reference.conf
|
.. literalinclude:: ../../akka-remote/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
*akka-serialization:*
|
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-actor/src/main/resources/akka-serialization-reference.conf
|
|
||||||
:language: none
|
|
||||||
|
|
||||||
*akka-testkit:*
|
*akka-testkit:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-testkit/src/main/resources/akka-testkit-reference.conf
|
.. literalinclude:: ../../akka-testkit/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
*akka-beanstalk-mailbox:*
|
*akka-beanstalk-mailbox:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-durable-mailboxes/akka-beanstalk-mailbox/src/main/resources/akka-beanstalk-mailbox-reference.conf
|
.. literalinclude:: ../../akka-durable-mailboxes/akka-beanstalk-mailbox/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
*akka-file-mailbox:*
|
*akka-file-mailbox:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-durable-mailboxes/akka-file-mailbox/src/main/resources/akka-file-mailbox-reference.conf
|
.. literalinclude:: ../../akka-durable-mailboxes/akka-file-mailbox/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
*akka-mongo-mailbox:*
|
*akka-mongo-mailbox:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-durable-mailboxes/akka-mongo-mailbox/src/main/resources/akka-mongo-mailbox-reference.conf
|
.. literalinclude:: ../../akka-durable-mailboxes/akka-mongo-mailbox/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
*akka-redis-mailbox:*
|
*akka-redis-mailbox:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-durable-mailboxes/akka-redis-mailbox/src/main/resources/akka-redis-mailbox-reference.conf
|
.. literalinclude:: ../../akka-durable-mailboxes/akka-redis-mailbox/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
*akka-zookeeper-mailbox:*
|
*akka-zookeeper-mailbox:*
|
||||||
|
|
||||||
.. literalinclude:: ../../akka-durable-mailboxes/akka-zookeeper-mailbox/src/main/resources/akka-zookeeper-mailbox-reference.conf
|
.. literalinclude:: ../../akka-durable-mailboxes/akka-zookeeper-mailbox/src/main/resources/reference.conf
|
||||||
:language: none
|
:language: none
|
||||||
|
|
||||||
A custom ``akka.conf`` might look like this::
|
A custom ``akka.conf`` might look like this::
|
||||||
|
|
@ -121,7 +118,6 @@ A custom ``akka.conf`` might look like this::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.. _-Dakka.mode:
|
|
||||||
|
|
||||||
Config file format
|
Config file format
|
||||||
------------------
|
------------------
|
||||||
|
|
@ -129,9 +125,13 @@ Config file format
|
||||||
The configuration file syntax is described in the `HOCON <https://github.com/havocp/config/blob/master/HOCON.md>`_
|
The configuration file syntax is described in the `HOCON <https://github.com/havocp/config/blob/master/HOCON.md>`_
|
||||||
specification. Note that it supports three formats; conf, json, and properties.
|
specification. Note that it supports three formats; conf, json, and properties.
|
||||||
|
|
||||||
|
.. _-Dakka.mode:
|
||||||
|
|
||||||
Specifying files for different modes
|
Specifying files for different modes
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
FIXME: mode doesn't exist, or will it?
|
||||||
|
|
||||||
You can use different configuration files for different purposes by specifying a mode option, either as
|
You can use different configuration files for different purposes by specifying a mode option, either as
|
||||||
``-Dakka.mode=...`` system property or as ``AKKA_MODE=...`` environment variable. For example using DEBUG log level
|
``-Dakka.mode=...`` system property or as ``AKKA_MODE=...`` environment variable. For example using DEBUG log level
|
||||||
when in development mode. Run with ``-Dakka.mode=dev`` and place the following ``akka.dev.conf`` in the root of
|
when in development mode. Run with ``-Dakka.mode=dev`` and place the following ``akka.dev.conf`` in the root of
|
||||||
|
|
@ -152,6 +152,8 @@ The mode option is not used when specifying the configuration file with ``-Dakka
|
||||||
Including files
|
Including files
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
FIXME: The include syntax has changed
|
||||||
|
|
||||||
Sometimes it can be useful to include another configuration file, for example if you have one ``akka.conf`` with all
|
Sometimes it can be useful to include another configuration file, for example if you have one ``akka.conf`` with all
|
||||||
environment independent settings and then override some settings for specific modes.
|
environment independent settings and then override some settings for specific modes.
|
||||||
|
|
||||||
|
|
@ -165,14 +167,14 @@ akka.dev.conf:
|
||||||
loglevel = "DEBUG"
|
loglevel = "DEBUG"
|
||||||
}
|
}
|
||||||
|
|
||||||
.. _-Dakka.output.config.source:
|
.. _-Dakka.logConfigOnStart:
|
||||||
|
|
||||||
Showing Configuration Source
|
Logging of Configuration
|
||||||
----------------------------
|
------------------------
|
||||||
|
|
||||||
If the system property ``akka.output.config.source`` is set to anything but
|
If the system or config property ``akka.logConfigOnStart`` is set to ``on``, then the
|
||||||
null, then the source from which Akka reads its configuration is printed to the
|
complete configuration at INFO level when the actor system is started. This is useful
|
||||||
console during application startup.
|
when you are uncertain of what configuration is used.
|
||||||
|
|
||||||
Summary of System Properties
|
Summary of System Properties
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
@ -180,4 +182,3 @@ Summary of System Properties
|
||||||
* :ref:`akka.home <-Dakka.home>` (``AKKA_HOME``): where Akka searches for configuration
|
* :ref:`akka.home <-Dakka.home>` (``AKKA_HOME``): where Akka searches for configuration
|
||||||
* :ref:`akka.config <-Dakka.config>`: explicit configuration file location
|
* :ref:`akka.config <-Dakka.config>`: explicit configuration file location
|
||||||
* :ref:`akka.mode <-Dakka.mode>` (``AKKA_MODE``): modify configuration file name for multiple profiles
|
* :ref:`akka.mode <-Dakka.mode>` (``AKKA_MODE``): modify configuration file name for multiple profiles
|
||||||
* :ref:`akka.output.config.source <-Dakka.output.config.source>`: whether to print configuration source to console
|
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,16 @@
|
||||||
package akka.actor.mailbox
|
package akka.actor.mailbox
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
|
|
||||||
object BeanstalkBasedMailboxExtension extends ExtensionId[BeanstalkMailboxSettings] with ExtensionIdProvider {
|
object BeanstalkBasedMailboxExtension extends ExtensionId[BeanstalkMailboxSettings] with ExtensionIdProvider {
|
||||||
def lookup() = this
|
def lookup() = this
|
||||||
def createExtension(system: ActorSystemImpl) = new BeanstalkMailboxSettings(system.applicationConfig)
|
def createExtension(system: ActorSystemImpl) = new BeanstalkMailboxSettings(system.settings.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
class BeanstalkMailboxSettings(cfg: Config) extends Extension {
|
class BeanstalkMailboxSettings(val config: Config) extends Extension {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-beanstalk-mailbox-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-beanstalk-mailbox").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ akka {
|
||||||
max-items = 2147483647
|
max-items = 2147483647
|
||||||
max-item-size = 2147483647 bytes
|
max-item-size = 2147483647 bytes
|
||||||
max-age = 0s
|
max-age = 0s
|
||||||
max-journal-size = 16 megabytes
|
max-journal-size = 16 MiB
|
||||||
max-memory-size = 128 megabytes
|
max-memory-size = 128 MiB
|
||||||
max-journal-overflow = 10
|
max-journal-overflow = 10
|
||||||
max-journal-size-absolute = 9223372036854775807 bytes
|
max-journal-size-absolute = 9223372036854775807 bytes
|
||||||
discard-old-when-full = on
|
discard-old-when-full = on
|
||||||
|
|
@ -4,36 +4,29 @@
|
||||||
package akka.actor.mailbox
|
package akka.actor.mailbox
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
|
|
||||||
object FileBasedMailboxExtension extends ExtensionId[FileBasedMailboxSettings] with ExtensionIdProvider {
|
object FileBasedMailboxExtension extends ExtensionId[FileBasedMailboxSettings] with ExtensionIdProvider {
|
||||||
def lookup() = this
|
def lookup() = this
|
||||||
def createExtension(system: ActorSystemImpl) = new FileBasedMailboxSettings(system.applicationConfig)
|
def createExtension(system: ActorSystemImpl) = new FileBasedMailboxSettings(system.settings.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileBasedMailboxSettings(cfg: Config) extends Extension {
|
class FileBasedMailboxSettings(val config: Config) extends Extension {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-file-mailbox-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-file-mailbox").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
val QueuePath = getString("akka.actor.mailbox.file-based.directory-path")
|
val QueuePath = getString("akka.actor.mailbox.file-based.directory-path")
|
||||||
|
|
||||||
val MaxItems = getInt("akka.actor.mailbox.file-based.max-items")
|
val MaxItems = getInt("akka.actor.mailbox.file-based.max-items")
|
||||||
val MaxSize = getMemorySizeInBytes("akka.actor.mailbox.file-based.max-size")
|
val MaxSize = getBytes("akka.actor.mailbox.file-based.max-size")
|
||||||
val MaxItemSize = getMemorySizeInBytes("akka.actor.mailbox.file-based.max-item-size")
|
val MaxItemSize = getBytes("akka.actor.mailbox.file-based.max-item-size")
|
||||||
val MaxAge = Duration(getMilliseconds("akka.actor.mailbox.file-based.max-age"), MILLISECONDS)
|
val MaxAge = Duration(getMilliseconds("akka.actor.mailbox.file-based.max-age"), MILLISECONDS)
|
||||||
val MaxJournalSize = getMemorySizeInBytes("akka.actor.mailbox.file-based.max-journal-size")
|
val MaxJournalSize = getBytes("akka.actor.mailbox.file-based.max-journal-size")
|
||||||
val MaxMemorySize = getMemorySizeInBytes("akka.actor.mailbox.file-based.max-memory-size")
|
val MaxMemorySize = getBytes("akka.actor.mailbox.file-based.max-memory-size")
|
||||||
val MaxJournalOverflow = getInt("akka.actor.mailbox.file-based.max-journal-overflow")
|
val MaxJournalOverflow = getInt("akka.actor.mailbox.file-based.max-journal-overflow")
|
||||||
val MaxJournalSizeAbsolute = getMemorySizeInBytes("akka.actor.mailbox.file-based.max-journal-size-absolute")
|
val MaxJournalSizeAbsolute = getBytes("akka.actor.mailbox.file-based.max-journal-size-absolute")
|
||||||
val DiscardOldWhenFull = getBoolean("akka.actor.mailbox.file-based.discard-old-when-full")
|
val DiscardOldWhenFull = getBoolean("akka.actor.mailbox.file-based.discard-old-when-full")
|
||||||
val KeepJournal = getBoolean("akka.actor.mailbox.file-based.keep-journal")
|
val KeepJournal = getBoolean("akka.actor.mailbox.file-based.keep-journal")
|
||||||
val SyncJournal = getBoolean("akka.actor.mailbox.file-based.sync-journal")
|
val SyncJournal = getBoolean("akka.actor.mailbox.file-based.sync-journal")
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,16 @@
|
||||||
package akka.actor.mailbox
|
package akka.actor.mailbox
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
|
|
||||||
object MongoBasedMailboxExtension extends ExtensionId[MongoBasedMailboxSettings] with ExtensionIdProvider {
|
object MongoBasedMailboxExtension extends ExtensionId[MongoBasedMailboxSettings] with ExtensionIdProvider {
|
||||||
def lookup() = this
|
def lookup() = this
|
||||||
def createExtension(system: ActorSystemImpl) = new MongoBasedMailboxSettings(system.applicationConfig)
|
def createExtension(system: ActorSystemImpl) = new MongoBasedMailboxSettings(system.settings.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MongoBasedMailboxSettings(cfg: Config) extends Extension {
|
class MongoBasedMailboxSettings(val config: Config) extends Extension {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-mongo-mailbox-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-mongo-mailbox").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,14 @@
|
||||||
package akka.actor.mailbox
|
package akka.actor.mailbox
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
|
|
||||||
object RedisBasedMailboxExtension extends ExtensionId[RedisBasedMailboxSettings] with ExtensionIdProvider {
|
object RedisBasedMailboxExtension extends ExtensionId[RedisBasedMailboxSettings] with ExtensionIdProvider {
|
||||||
def lookup() = this
|
def lookup() = this
|
||||||
def createExtension(system: ActorSystemImpl) = new RedisBasedMailboxSettings(system.applicationConfig)
|
def createExtension(system: ActorSystemImpl) = new RedisBasedMailboxSettings(system.settings.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RedisBasedMailboxSettings(cfg: Config) extends Extension {
|
class RedisBasedMailboxSettings(val config: Config) extends Extension {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-redis-mailbox-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-redis-mailbox").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,15 @@
|
||||||
package akka.actor.mailbox
|
package akka.actor.mailbox
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
|
|
||||||
object ZooKeeperBasedMailboxExtension extends ExtensionId[ZooKeeperBasedMailboxSettings] with ExtensionIdProvider {
|
object ZooKeeperBasedMailboxExtension extends ExtensionId[ZooKeeperBasedMailboxSettings] with ExtensionIdProvider {
|
||||||
def lookup() = this
|
def lookup() = this
|
||||||
def createExtension(system: ActorSystemImpl) = new ZooKeeperBasedMailboxSettings(system.applicationConfig)
|
def createExtension(system: ActorSystemImpl) = new ZooKeeperBasedMailboxSettings(system.settings.config)
|
||||||
}
|
}
|
||||||
class ZooKeeperBasedMailboxSettings(cfg: Config) extends Extension {
|
class ZooKeeperBasedMailboxSettings(val config: Config) extends Extension {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-zookeeper-mailbox-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-zookeeper-mailbox").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ akka {
|
||||||
server {
|
server {
|
||||||
hostname = "" # The hostname or ip to bind the remoting to, InetAddress.getLocalHost.getHostAddress is used if empty
|
hostname = "" # The hostname or ip to bind the remoting to, InetAddress.getLocalHost.getHostAddress is used if empty
|
||||||
port = 2552 # The default remote server port clients should connect to. Default is 2552 (AKKA)
|
port = 2552 # The default remote server port clients should connect to. Default is 2552 (AKKA)
|
||||||
message-frame-size = 1048576 # Increase this if you want to be able to send messages with large payloads
|
message-frame-size = 1 MiB # Increase this if you want to be able to send messages with large payloads
|
||||||
connection-timeout = 120s # Timeout duration
|
connection-timeout = 120s # Timeout duration
|
||||||
require-cookie = off # Should the remote server require that it peers share the same secure-cookie (defined in the 'remote' section)?
|
require-cookie = off # Should the remote server require that it peers share the same secure-cookie (defined in the 'remote' section)?
|
||||||
untrusted-mode = off # Enable untrusted mode for full security of server managed actors, allows untrusted clients to connect.
|
untrusted-mode = off # Enable untrusted mode for full security of server managed actors, allows untrusted clients to connect.
|
||||||
|
|
@ -46,7 +46,7 @@ akka {
|
||||||
}
|
}
|
||||||
reconnect-delay = 5s
|
reconnect-delay = 5s
|
||||||
read-timeout = 3600s
|
read-timeout = 3600s
|
||||||
message-frame-size = 1048576
|
message-frame-size = 1 MiB
|
||||||
reconnection-time-window = 600s # Maximum time window that a client should try to reconnect for
|
reconnection-time-window = 600s # Maximum time window that a client should try to reconnect for
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,9 +4,6 @@
|
||||||
package akka.remote
|
package akka.remote
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
|
|
@ -18,14 +15,10 @@ import scala.collection.JavaConverters._
|
||||||
|
|
||||||
object RemoteExtension extends ExtensionId[RemoteExtensionSettings] with ExtensionIdProvider {
|
object RemoteExtension extends ExtensionId[RemoteExtensionSettings] with ExtensionIdProvider {
|
||||||
def lookup() = this
|
def lookup() = this
|
||||||
def createExtension(system: ActorSystemImpl) = new RemoteExtensionSettings(system.applicationConfig)
|
def createExtension(system: ActorSystemImpl) = new RemoteExtensionSettings(system.settings.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoteExtensionSettings(cfg: Config) extends Extension {
|
class RemoteExtensionSettings(val config: Config) extends Extension {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-remote-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-remote").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
|
|
@ -57,12 +50,12 @@ class RemoteExtensionSettings(cfg: Config) extends Extension {
|
||||||
val ReconnectionTimeWindow = Duration(config.getMilliseconds("akka.remote.client.reconnection-time-window"), MILLISECONDS)
|
val ReconnectionTimeWindow = Duration(config.getMilliseconds("akka.remote.client.reconnection-time-window"), MILLISECONDS)
|
||||||
val ReadTimeout = Duration(config.getMilliseconds("akka.remote.client.read-timeout"), MILLISECONDS)
|
val ReadTimeout = Duration(config.getMilliseconds("akka.remote.client.read-timeout"), MILLISECONDS)
|
||||||
val ReconnectDelay = Duration(config.getMilliseconds("akka.remote.client.reconnect-delay"), MILLISECONDS)
|
val ReconnectDelay = Duration(config.getMilliseconds("akka.remote.client.reconnect-delay"), MILLISECONDS)
|
||||||
val MessageFrameSize = config.getInt("akka.remote.client.message-frame-size")
|
val MessageFrameSize = config.getBytes("akka.remote.client.message-frame-size").toInt
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoteServerSettings {
|
class RemoteServerSettings {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
val MessageFrameSize = config.getInt("akka.remote.server.message-frame-size")
|
val MessageFrameSize = config.getBytes("akka.remote.server.message-frame-size").toInt
|
||||||
val SecureCookie: Option[String] = config.getString("akka.remote.secure-cookie") match {
|
val SecureCookie: Option[String] = config.getString("akka.remote.secure-cookie") match {
|
||||||
case "" ⇒ None
|
case "" ⇒ None
|
||||||
case cookie ⇒ Some(cookie)
|
case cookie ⇒ Some(cookie)
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,27 @@ package akka.remote
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import akka.actor.ActorSystemImpl
|
import akka.actor.ActorSystemImpl
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import com.typesafe.config.ConfigParseOptions
|
||||||
|
import com.typesafe.config.ConfigResolveOptions
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
abstract class AkkaRemoteSpec extends AkkaSpec with MultiJvmSync {
|
object AkkaRemoteSpec {
|
||||||
|
private def configParseOptions = ConfigParseOptions.defaults.setAllowMissing(false)
|
||||||
|
|
||||||
|
val testConf: Config = {
|
||||||
|
System.getProperty("akka.config") match {
|
||||||
|
case null ⇒ AkkaSpec.testConf
|
||||||
|
case location ⇒
|
||||||
|
ConfigFactory.systemProperties
|
||||||
|
.withFallback(ConfigFactory.parseFileAnySyntax(new File(location), configParseOptions))
|
||||||
|
.withFallback(ConfigFactory.defaultReference).resolve(ConfigResolveOptions.defaults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AkkaRemoteSpec extends AkkaSpec(AkkaRemoteSpec.testConf) with MultiJvmSync {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for accessing the underlying remoting.
|
* Helper function for accessing the underlying remoting.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class RemoteConfigSpec extends AkkaSpec {
|
||||||
|
|
||||||
//akka.remote.server
|
//akka.remote.server
|
||||||
getInt("akka.remote.server.port") must equal(2552)
|
getInt("akka.remote.server.port") must equal(2552)
|
||||||
getInt("akka.remote.server.message-frame-size") must equal(1048576)
|
getBytes("akka.remote.server.message-frame-size") must equal(1048576L)
|
||||||
getMilliseconds("akka.remote.server.connection-timeout") must equal(120 * 1000)
|
getMilliseconds("akka.remote.server.connection-timeout") must equal(120 * 1000)
|
||||||
getBoolean("akka.remote.server.require-cookie") must equal(false)
|
getBoolean("akka.remote.server.require-cookie") must equal(false)
|
||||||
getBoolean("akka.remote.server.untrusted-mode") must equal(false)
|
getBoolean("akka.remote.server.untrusted-mode") must equal(false)
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,10 @@ import org.scalatest.junit.JUnitRunner
|
||||||
import org.scalatest.matchers.MustMatchers
|
import org.scalatest.matchers.MustMatchers
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
|
|
||||||
@RunWith(classOf[JUnitRunner])
|
@RunWith(classOf[JUnitRunner])
|
||||||
class ConfigSpec extends AkkaSpec(ConfigFactory.parseResource(classOf[ConfigSpec], "/akka-stm-reference.conf", ConfigParseOptions.defaults)) {
|
class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference) {
|
||||||
|
|
||||||
"The default configuration file (i.e. akka-stm-reference.conf)" should {
|
"The default configuration file (i.e. akka-stm-reference.conf)" should {
|
||||||
"contain all configuration properties for akka-stm that are used in code with their correct defaults" in {
|
"contain all configuration properties for akka-stm that are used in code with their correct defaults" in {
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,15 @@
|
||||||
package akka.testkit
|
package akka.testkit
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
import com.typesafe.config.ConfigRoot
|
|
||||||
import akka.util.Duration
|
import akka.util.Duration
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import akka.actor.{ ExtensionId, ActorSystem, Extension, ActorSystemImpl }
|
import akka.actor.{ ExtensionId, ActorSystem, Extension, ActorSystemImpl }
|
||||||
|
|
||||||
object TestKitExtension extends ExtensionId[TestKitSettings] {
|
object TestKitExtension extends ExtensionId[TestKitSettings] {
|
||||||
def createExtension(system: ActorSystemImpl): TestKitSettings = new TestKitSettings(system.applicationConfig)
|
def createExtension(system: ActorSystemImpl): TestKitSettings = new TestKitSettings(system.settings.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestKitSettings(cfg: Config) extends Extension {
|
class TestKitSettings(val config: Config) extends Extension {
|
||||||
private def referenceConfig: Config =
|
|
||||||
ConfigFactory.parseResource(classOf[ActorSystem], "/akka-testkit-reference.conf",
|
|
||||||
ConfigParseOptions.defaults.setAllowMissing(false))
|
|
||||||
val config: ConfigRoot = ConfigFactory.emptyRoot("akka-testkit").withFallback(cfg).withFallback(referenceConfig).resolve()
|
|
||||||
|
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,12 @@ import akka.util.duration._
|
||||||
import akka.dispatch.FutureTimeoutException
|
import akka.dispatch.FutureTimeoutException
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
|
||||||
|
|
||||||
object TimingTest extends Tag("timing")
|
object TimingTest extends Tag("timing")
|
||||||
|
|
||||||
object AkkaSpec {
|
object AkkaSpec {
|
||||||
val testConf =
|
val testConf = {
|
||||||
ActorSystem.DefaultConfigurationLoader.defaultConfig.withFallback(
|
val cfg = ConfigFactory.parseString("""
|
||||||
ConfigFactory.parseString("""
|
|
||||||
akka {
|
akka {
|
||||||
event-handlers = ["akka.testkit.TestEventListener"]
|
event-handlers = ["akka.testkit.TestEventListener"]
|
||||||
loglevel = "WARNING"
|
loglevel = "WARNING"
|
||||||
|
|
@ -32,7 +30,9 @@ object AkkaSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""", ConfigParseOptions.defaults))
|
""")
|
||||||
|
ConfigFactory.load(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
def mapToConfig(map: Map[String, Any]): Config = {
|
def mapToConfig(map: Map[String, Any]): Config = {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
|
@ -64,7 +64,7 @@ abstract class AkkaSpec(_system: ActorSystem = ActorSystem(getClass.getSimpleNam
|
||||||
|
|
||||||
def this(config: Config) = this(ActorSystem(getClass.getSimpleName, config.withFallback(AkkaSpec.testConf)))
|
def this(config: Config) = this(ActorSystem(getClass.getSimpleName, config.withFallback(AkkaSpec.testConf)))
|
||||||
|
|
||||||
def this(s: String) = this(ConfigFactory.parseString(s, ConfigParseOptions.defaults))
|
def this(s: String) = this(ConfigFactory.parseString(s))
|
||||||
|
|
||||||
def this(configMap: Map[String, _]) = {
|
def this(configMap: Map[String, _]) = {
|
||||||
this(AkkaSpec.mapToConfig(configMap))
|
this(AkkaSpec.mapToConfig(configMap))
|
||||||
|
|
@ -87,12 +87,11 @@ abstract class AkkaSpec(_system: ActorSystem = ActorSystem(getClass.getSimpleNam
|
||||||
class AkkaSpecSpec extends WordSpec with MustMatchers {
|
class AkkaSpecSpec extends WordSpec with MustMatchers {
|
||||||
"An AkkaSpec" must {
|
"An AkkaSpec" must {
|
||||||
"terminate all actors" in {
|
"terminate all actors" in {
|
||||||
import ActorSystem.DefaultConfigurationLoader.defaultConfig
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
val conf = Map(
|
val conf = Map(
|
||||||
"akka.actor.debug.lifecycle" -> true, "akka.actor.debug.event-stream" -> true,
|
"akka.actor.debug.lifecycle" -> true, "akka.actor.debug.event-stream" -> true,
|
||||||
"akka.loglevel" -> "DEBUG", "akka.stdout-loglevel" -> "DEBUG")
|
"akka.loglevel" -> "DEBUG", "akka.stdout-loglevel" -> "DEBUG")
|
||||||
val system = ActorSystem("test", ConfigFactory.parseMap(conf.asJava).withFallback(defaultConfig))
|
val system = ActorSystem("test", ConfigFactory.parseMap(conf.asJava).withFallback(AkkaSpec.testConf))
|
||||||
val spec = new AkkaSpec(system) {
|
val spec = new AkkaSpec(system) {
|
||||||
val ref = Seq(testActor, system.actorOf(Props.empty, "name"))
|
val ref = Seq(testActor, system.actorOf(Props.empty, "name"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue