diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /config-lib/src |
Publish
Diffstat (limited to 'config-lib/src')
56 files changed, 2960 insertions, 0 deletions
diff --git a/config-lib/src/main/java/com/yahoo/config/BooleanNode.java b/config-lib/src/main/java/com/yahoo/config/BooleanNode.java new file mode 100644 index 00000000000..8347b800272 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/BooleanNode.java @@ -0,0 +1,50 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * The BooleanNode class represents a boolean in a configuration tree. + */ +public class BooleanNode extends LeafNode<Boolean> { + public BooleanNode() { + } + + public BooleanNode(boolean value) { + super(true); + this.value = value; + } + + public Boolean value() { + return value; + } + + @Override + public String getValue() { + return "" + value; + } + + @Override + public String toString() { + return getValue(); + } + + @Override + protected boolean doSetValue(@NonNull String value) { + if (! value.equalsIgnoreCase("false") && ! value.equalsIgnoreCase("true")) { + return false; + } + this.value = Boolean.valueOf(value); + return true; + } + + @Override + void serialize(String name, Serializer serializer) { + serializer.serialize(name, value); + } + + @Override + void serialize(Serializer serializer) { + serializer.serialize(value); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/ChangesRequiringRestart.java b/config-lib/src/main/java/com/yahoo/config/ChangesRequiringRestart.java new file mode 100644 index 00000000000..6c17a21c649 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/ChangesRequiringRestart.java @@ -0,0 +1,162 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.joining; + +/** + * @author <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + * + * This class aggregates information about config changes that causes a restart to be required. + */ +public class ChangesRequiringRestart { + static class ReportLine { + private String name; + private final Node from; + private final Node to; + private final String comment; + + public ReportLine(String name, Node from, Node to, String comment) { + this.name = name; + this.from = from; + this.to = to; + this.comment = comment; + } + + public void addNamePrefix(String prefix) { + if (!name.isEmpty()) { + name = prefix + "." + name; + } else { + name = prefix; + } + } + + private String getCommentAndName(String indent, String namePrefix) { + return indent + (comment.isEmpty()? "" : "# " + comment.replace("\n", "\n" + indent + "# ") + "\n" + indent) + + namePrefix + name; + } + + private static String formatValue(String indent, Node n) { + String str = n.toString(); + if (str.contains("\n")) { // Struct + str = "\n" + indent + " { " + str.replace("\n", "\n" + indent + " ") + " }"; + } + return str; + } + + @Override + public String toString() { + return toString("", ""); + } + + public String toString(String indent, String namePrefix) { + if (from == null) { + return getCommentAndName(indent, namePrefix) + " was added with value " + formatValue(indent, to); + } else if (to == null) { + return getCommentAndName(indent, namePrefix) + " with value " + formatValue(indent, from) + " was removed"; + } + return getCommentAndName(indent, namePrefix) + " has changed from " + formatValue(indent, from) + " to " + formatValue(indent, to); + } + } + + private ArrayList<ReportLine> report = new ArrayList<>(); + private String componentName; + + public ChangesRequiringRestart(String componentName) { + this.componentName = componentName; + } + + public String getName() { + return componentName; + } + + public ChangesRequiringRestart compare(Node from, Node to, String name, String comment) { + if (!from.equals(to)) { + report.add(new ReportLine(name, from, to, comment)); + } + return this; + } + + public void mergeChanges(String prefix, ChangesRequiringRestart childReport) { + for (ReportLine line : childReport.getReportLines()) { + line.addNamePrefix(prefix); + report.add(line); + } + } + + /** + * Interface used to pass lambda functions from generated code to compareArray/-Map functions. + */ + public interface CompareFunc { + // Generates a report based on a config change. + ChangesRequiringRestart getChangesRequiringRestart(Node from, Node to); + } + + public ChangesRequiringRestart compareArray(List<? extends Node> from, + List<? extends Node> to, + String name, + String comment, + CompareFunc func) { + if (!from.equals(to)) { + int commonElements = Math.min(from.size(), to.size()); + for (int i = 0; i < commonElements; ++i) { + ChangesRequiringRestart childReport = func.getChangesRequiringRestart(from.get(i), to.get(i)); + String prefix = childReport.componentName + "[" + Integer.toString(i) + "]"; + mergeChanges(prefix, childReport); + } + for (int i = commonElements; i < from.size(); ++i) { + report.add(new ReportLine(name + "[" + Integer.toString(i) + "]", from.get(i), null, comment)); + } + for (int i = commonElements; i < to.size(); ++i) { + report.add(new ReportLine(name + "[" + Integer.toString(i) + "]", null, to.get(i), comment)); + } + } + return this; + } + + public ChangesRequiringRestart compareMap(Map<String, ? extends Node> from, + Map<String, ? extends Node> to, + String name, + String comment, + CompareFunc func) { + if (!from.equals(to)) { + for (String key : from.keySet()) { + if (to.containsKey(key)) { + ChangesRequiringRestart childReport = func.getChangesRequiringRestart(from.get(key), to.get(key)); + String prefix = childReport.componentName + "{" + key + "}"; + mergeChanges(prefix, childReport); + } else { + report.add(new ReportLine(name + "{" + key + "}", from.get(key), null, comment)); + } + } + for (String key : to.keySet()) { + if (!from.containsKey(key)) { + report.add(new ReportLine(name + "{" + key + "}", null, to.get(key), comment)); + } + } + } + return this; + } + + List<ReportLine> getReportLines() { + return report; + } + + @Override + public String toString() { + return toString(""); + } + + public String toString(String indent) { + return report.stream() + .map(line -> line.toString(indent, componentName + ".")) + .collect(joining("\n")); + } + + public boolean needsRestart() { + return !report.isEmpty(); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/ConfigBuilder.java b/config-lib/src/main/java/com/yahoo/config/ConfigBuilder.java new file mode 100644 index 00000000000..dc90df9b12f --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/ConfigBuilder.java @@ -0,0 +1,11 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +/** + * Root interface for all config builders. + * + * @author gjoranv + * @since 5.1.6 + */ +public interface ConfigBuilder { +} diff --git a/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java b/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java new file mode 100644 index 00000000000..bb091a6b4e1 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java @@ -0,0 +1,122 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Represents an instance of an application config with a specific configId. + * <p> + * An instance of this class contains all values (represented by Nodes) for the config object as it + * is the superclass of the generated config class used by the client. + */ +public abstract class ConfigInstance extends InnerNode { + + public interface Builder extends ConfigBuilder { + /** + * Dispatches a getConfig() call if this instance's producer is of the right type + * @param producer a config producer + * @return true if this instance's producer was the correct type, and hence a getConfig call was dispatched + */ + public boolean dispatchGetConfig(Producer producer); + + public String getDefName(); + public String getDefNamespace(); + public String getDefMd5(); + } + + public interface Producer {} + + private String configMd5 = ""; + + String configId; + + /** + * Gets the name of the given config instance + */ + public static String getDefName(Class<?> type) { + return getStaticStringField(type, "CONFIG_DEF_NAME"); + } + + /** + * Gets the namespace of the given config instance + */ + public static String getDefNamespace(Class<?> type) { + return getStaticStringField(type, "CONFIG_DEF_NAMESPACE"); + } + + /** + * Returns the serialized representation of the given node. + * <p> + * Declared static, instead of InnerNode member, to avoid a public 0-arg method with a commonly used name. + * + * @param node The inner node + * @return a list of strings, containing the serialized representation of this config + */ + public static List<String> serialize(InnerNode node) { + List<String> ret = new ArrayList<>(); + for (Map.Entry<String, LeafNode<?>> entry : getAllDescendantLeafNodes(node).entrySet()) { + ret.add(entry.getKey() + " " + entry.getValue().toString()); + } + return ret; + } + + public static void serialize(InnerNode node, Serializer serializer) { + serializeMap(node.getChildren(), serializer); + } + + @SuppressWarnings("unchecked") + private static void serializeObject(String name, Object child, Serializer serializer) { + if (child instanceof InnerNode) { + Serializer childSerializer = serializer.createInner(name); + serialize((InnerNode) child, childSerializer); + } else if (child instanceof Map) { + Serializer mapSerializer = serializer.createMap(name); + serializeMap((Map<String, Object>)child, mapSerializer); + } else if (child instanceof NodeVector) { + Serializer arraySerializer = serializer.createArray(name); + serializeArray((NodeVector) child, arraySerializer); + } else if (child instanceof LeafNode) { + ((LeafNode) child).serialize(name, serializer); + } + } + + private static void serializeMap(Map<String, Object> childMap, Serializer serializer) { + for (Map.Entry<String, Object> entry : childMap.entrySet()) { + String name = entry.getKey(); + Object child = entry.getValue(); + serializeObject(name, child, serializer); + } + } + + private static void serializeArray(NodeVector<?> nodeVector, Serializer arraySerializer) { + for (Object child : nodeVector.vector) { + if (child instanceof InnerNode) { + Serializer childSerializer = arraySerializer.createInner(); + serialize((InnerNode) child, childSerializer); + } else if (child instanceof LeafNode) { + ((LeafNode) child).serialize(arraySerializer); + } + } + } + + + public String getConfigMd5() { + return configMd5; + } + + public void setConfigMd5(String configMd5) { + this.configMd5 = configMd5; + } + + private static String getStaticStringField(Class<?> type, String fieldName) { + try { + return (String) type.getField(fieldName).get(null); + } catch (Exception e) { + throw new RuntimeException + (e.getMessage() + ": Static field " + fieldName + " not " + "accessible in " + type.getName()); + } + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/ConfigurationRuntimeException.java b/config-lib/src/main/java/com/yahoo/config/ConfigurationRuntimeException.java new file mode 100644 index 00000000000..56020906499 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/ConfigurationRuntimeException.java @@ -0,0 +1,21 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +/** + * This exception is thrown on internal errors in the configuration system. + */ +@SuppressWarnings("serial") +public class ConfigurationRuntimeException extends RuntimeException { + public ConfigurationRuntimeException(String message) { + super(message); + } + + public ConfigurationRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public ConfigurationRuntimeException(Throwable cause) { + super(cause); + } + +}
\ No newline at end of file diff --git a/config-lib/src/main/java/com/yahoo/config/DoubleNode.java b/config-lib/src/main/java/com/yahoo/config/DoubleNode.java new file mode 100644 index 00000000000..6ff97df579f --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/DoubleNode.java @@ -0,0 +1,51 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * The DoubleNode class represents a double in a configuration tree. + */ +public class DoubleNode extends LeafNode<Double> { + public DoubleNode() { + } + + public DoubleNode(double value) { + super(true); + this.value = value; + } + + public Double value() { + return value; + } + + @Override + public String getValue() { + return "" + value; + } + + @Override + public String toString() { + return getValue(); + } + + @Override + protected boolean doSetValue(@NonNull String value) { + try { + this.value = Double.parseDouble(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + void serialize(String name, Serializer serializer) { + serializer.serialize(name, value); + } + + @Override + void serialize(Serializer serializer) { + serializer.serialize(value); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/EnumNode.java b/config-lib/src/main/java/com/yahoo/config/EnumNode.java new file mode 100644 index 00000000000..5443d063bbd --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/EnumNode.java @@ -0,0 +1,24 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +/** + * The EnumNode class is a superclass for Enumerations in a configuration tree. + */ +public abstract class EnumNode<ENUM extends Enum<?>> extends LeafNode<ENUM> { + public EnumNode() { + } + + public EnumNode(boolean b) { + super(b); + } + + @Override + public String toString() { + return (value == null) ? "(null)" : value.toString(); + } + + @Override + public String getValue() { + return (value == null) ? null : value.toString(); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/FileNode.java b/config-lib/src/main/java/com/yahoo/config/FileNode.java new file mode 100644 index 00000000000..0d6bccb59a5 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/FileNode.java @@ -0,0 +1,42 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a 'file' in a {@link ConfigInstance}, usually a filename. + * + * @author gjoranv + */ +public class FileNode extends LeafNode<FileReference> { + + public FileNode() { + } + + public FileNode(String stringVal) { + super(true); + this.value = new FileReference(ReferenceNode.stripQuotes(stringVal)); + } + + public FileReference value() { + return value; + } + + @Override + public String getValue() { + return value.value(); + } + + @Override + public String toString() { + return (value == null) ? "(null)" : '"' + getValue() + '"'; + } + + @Override + protected boolean doSetValue(@NonNull String stringVal) { + value = new FileReference(ReferenceNode.stripQuotes(stringVal)); + return true; + } + + +} diff --git a/config-lib/src/main/java/com/yahoo/config/FileReference.java b/config-lib/src/main/java/com/yahoo/config/FileReference.java new file mode 100755 index 00000000000..de86fc09be7 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/FileReference.java @@ -0,0 +1,67 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * An immutable file reference that can only be created from classes within the same package. + * This is to prevent clients from creating arbitrary and invalid file references. + * + * @author tonytv + */ +public final class FileReference { + + private final String value; + + FileReference(String value) { + this.value = value; + } + + public String value() { + return value; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object other) { + return other instanceof FileReference && + value.equals(((FileReference)other).value); + } + + @Override + public String toString() { + return "file '" + value + "'"; + } + + public static List<String> toValues(Collection<FileReference> references) { + List<String> ret = new ArrayList<String>(); + for (FileReference r: references) { + ret.add(r.value()); + } + return ret; + } + + public static Map<String, String> toValueMap(Map<String, FileReference> map) { + Map<String, String> ret = new LinkedHashMap<>(); + for (Map.Entry<String, FileReference> e : map.entrySet()) { + ret.put(e.getKey(), e.getValue().value()); + } + return ret; + } + + public static FileReference mockFileReferenceForUnitTesting(File file) { + if (! file.exists()) + throw new IllegalArgumentException("File '" + file.getAbsolutePath() + "' does not exist."); + return new FileReference(file.getPath()); + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/InnerNode.java b/config-lib/src/main/java/com/yahoo/config/InnerNode.java new file mode 100644 index 00000000000..eb7f36bbaea --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/InnerNode.java @@ -0,0 +1,171 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Superclass for all inner nodes in a {@link ConfigInstance}. + * <p> + * This class cannot have non-private members because such members + * will interfere with the members in the generated subclass. + * + * @author gjoranv + */ +public abstract class InnerNode extends Node { + + /** + * Creates a new InnerNode. + */ + public InnerNode() { + } + + @Override + public String toString() { + return mkString(ConfigInstance.serialize(this), "\n"); + } + + private static <T> String mkString(Collection<T> collection, String sep) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + + for (T elem : collection) { + if (! first) + sb.append(sep); + first = false; + sb.append(elem); + } + return sb.toString(); + } + + /** + * Overrides {@link Node#postInitialize(String)}. + * Perform post initialization on this nodes children. + * + * @param configId The config id of this instance. + */ + @Override + public void postInitialize(String configId) { + Map<String, Node> children = getChildrenWithVectorsFlattened(); + for (Node node : children.values()) { + node.postInitialize(configId); + } + } + + @Override + public boolean equals(Object other) { + if (other == this) + return true; + + if ( !(other instanceof InnerNode) || (other.getClass() != this.getClass())) + return false; + + Collection<Object> children = getChildren().values(); + Collection<Object> otherChildren = ((InnerNode)other).getChildren().values(); + + Iterator<Object> e1 = children.iterator(); + Iterator<Object> e2 = otherChildren.iterator(); + while(e1.hasNext() && e2.hasNext()) { + Object o1 = e1.next(); + Object o2 = e2.next(); + if (!(o1 == null ? o2 == null : o1.equals(o2))) + return false; + } + return !(e1.hasNext() || e2.hasNext()); + } + + @Override + public int hashCode() { + int res = 17; + for (Object child : getChildren().values()) + res = 31 * res + child.hashCode(); + return res; + } + + protected Map<String, Object> getChildren() { + HashMap<String, Object> ret = new LinkedHashMap<String, Object>(); + Field fields[] = getClass().getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + Object fieldValue; + try { + fieldValue = field.get(this); + } catch (IllegalAccessException e) { + throw new ConfigurationRuntimeException(e); + } + if (fieldValue instanceof Node + || fieldValue instanceof NodeVector<?> + || fieldValue instanceof Map<?,?>) + ret.put(field.getName(), fieldValue); + } + return ret; + } + + /** + * Returns a flat map of this node's direct children, including all NodeVectors' elements. + * Keys are the node name, including index for vector elements, e.g. 'arr[0]'. + */ + @SuppressWarnings("unchecked") + protected Map<String, Node> getChildrenWithVectorsFlattened() { + HashMap<String, Node> ret = new LinkedHashMap<String, Node>(); + + Map<String, Object> children = getChildren(); + for (Map.Entry<String, Object> childEntry : children.entrySet()) { + String name = childEntry.getKey(); + Object child = childEntry.getValue(); + if (child instanceof NodeVector) { + addNodeVectorEntries(ret, name, (NodeVector<?>) child); + } else if (child instanceof Map<?,?>) { + addMapEntries(ret, name, (Map<String, Node>) child); + } else if (child instanceof Node) { + ret.put(name, (Node)child); + } + } + return ret; + } + + private static void addMapEntries(HashMap<String, Node> ret, String name, Map<String, Node> map) { + for (Map.Entry<String, Node> entry : map.entrySet()) + ret.put(name + "{" + entry.getKey() + "}", entry.getValue()); + } + + + private static void addNodeVectorEntries(HashMap<String, Node> ret, String name, NodeVector<?> vector) { + for (int j = 0; j < vector.length(); j++) + ret.put(name + "[" + j + "]", (Node) vector.get(j)); + } + + protected static Map<String, LeafNode<?>> getAllDescendantLeafNodes(InnerNode node) { + return getAllDescendantLeafNodes("", node); + } + + /** + * Generates a map of all leaf nodes, with full.paths[3] in key + * + * @param parentName Name of the parent node, can be empty. + * @param node The node to get leaf nodes for. + * @return map of leaf nodes + */ + private static Map<String, LeafNode<?>> getAllDescendantLeafNodes(String parentName, InnerNode node) { + Map<String, LeafNode<?>> ret = new LinkedHashMap<String, LeafNode<?>>(); + String prefix = parentName.isEmpty() ? "" : parentName + "."; + Map<String, Node> children = node.getChildrenWithVectorsFlattened(); + for (Map.Entry<String, Node> childEntry : children.entrySet()) { + String name = childEntry.getKey(); + String prefixedName = prefix + name; + name=name.replaceAll("\\[.*", ""); + Node child = childEntry.getValue(); + if (child instanceof LeafNode) { + ret.put(prefixedName, (LeafNode<?>) child); + } else if (child instanceof InnerNode) { + ret.putAll(getAllDescendantLeafNodes(prefixedName, (InnerNode) child)); + } + } + return ret; + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/InnerNodeVector.java b/config-lib/src/main/java/com/yahoo/config/InnerNodeVector.java new file mode 100644 index 00000000000..72ca0138a53 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/InnerNodeVector.java @@ -0,0 +1,47 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.util.List; + +/** + * @author gjoranv + * @since 5.1.4 + */ +public class InnerNodeVector<NODE extends InnerNode> extends NodeVector<NODE> { + + NODE defaultNode; + + /** + * Creates a new vector with the given default node. + */ + // TODO: remove this ctor when the library uses reflection via builders, and resizing won't be necessary + public InnerNodeVector(NODE defaultNode) { + assert (defaultNode != null) : "The default node cannot be null"; + + this.defaultNode = defaultNode; + if (createNew() == null) { + throw new NullPointerException("Unable to duplicate the default node."); + } + } + + public InnerNodeVector(List<NODE> nodes, NODE defaultNode) { + this(defaultNode); + for (NODE node : nodes) { + vector.add(node); + } + } + + /** + * Creates a new Node by creating a new instance with the 0-argument constructor + */ + // TODO: remove when the library uses reflection via builders + @SuppressWarnings("unchecked") + protected NODE createNew() { + try { + return (NODE) defaultNode.getClass().newInstance(); + } catch (IllegalAccessException | InstantiationException ex) { + throw new ConfigurationRuntimeException(ex); + } + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/IntegerNode.java b/config-lib/src/main/java/com/yahoo/config/IntegerNode.java new file mode 100644 index 00000000000..82db4cab030 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/IntegerNode.java @@ -0,0 +1,52 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * The IntegerNode class represents an integer in a configuration tree. + */ +public class IntegerNode extends LeafNode<Integer> { + + public IntegerNode() { + } + + public IntegerNode(int value) { + super(true); + this.value = value; + } + + public Integer value() { + return value; + } + + @Override + public String getValue() { + return "" + value; + } + + @Override + public String toString() { + return getValue(); + } + + @Override + protected boolean doSetValue(@NonNull String value) { + try { + this.value = Integer.parseInt(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + void serialize(String name, Serializer serializer) { + serializer.serialize(name, value); + } + + @Override + void serialize(Serializer serializer) { + serializer.serialize(value); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/LeafNode.java b/config-lib/src/main/java/com/yahoo/config/LeafNode.java new file mode 100644 index 00000000000..24ec534e222 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/LeafNode.java @@ -0,0 +1,120 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Superclass for all leaf nodes in a {@link ConfigInstance}. + * <p> + * Subclasses represents leaf nodes with different types. These + * implementations should implement method value() with return-value + * corresponding to the actual type. + * + */ +public abstract class LeafNode<T> extends Node implements Cloneable { + + protected boolean initialized; + protected T value; + + /** + * Creates a new, uninitialized LeafNode + */ + protected LeafNode() { + initialized = false; + } + + /** + * Creates a new LeafNode. + * + * @param initialized true if this node is initialized. + */ + protected LeafNode(boolean initialized) { + this.initialized = initialized; + } + + public T value() { + return value; + } + + /** + * Try to initialize this node with the given value. Returns true + * on success, false otherwise. + * + * @param value the string represention of the desired node value. + * @return true on success, false otherwise. + */ + final boolean initialize(String value) { + boolean success = setValue(value); + initialized |= success; + return success; + } + + /** + * Subclasses must implement this, in compliance with the rules given in the return tag. + * + * @return the String representation of the node value, or the string "(null)" if the value is null. + */ + public abstract String toString(); + + /** + * Subclasses must implement this, in compliance with the rules given in the return tag. + * + * @return the String representation of the node value, or the 'null' object if the node value is null. + */ + public abstract String getValue(); + + /** + * Sets the value based on a string representation. Returns false if the value could + * not be set from the given string. + * TODO: return void (see doSetValue) + * + * @param value the value to set + * @return true on success, false otherwise + * @throws IllegalArgumentException when value is null + */ + protected final boolean setValue(String value) { + if (value == null) + throw new IllegalArgumentException("Null value is not allowed"); + return doSetValue(value); + } + + // TODO: should throw exception instead of return false. + protected abstract boolean doSetValue(@NonNull String value); + + /** + * This method is meant for internal use in the configuration + * system. Overrides Object.clone(). + * + * @return a new instance similar to this object. + */ + @Override + protected Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new ConfigurationRuntimeException(e); + } + } + + @Override + public boolean equals(Object o) { + if (! (o instanceof LeafNode)) + return false; + + LeafNode<?> other = (LeafNode)o; + return value == null ? other.value == null : value().equals(other.value); + } + + @Override + public int hashCode() { + return (value == null) ? 0 : value.hashCode(); + } + + void serialize(String name, Serializer serializer) { + serializer.serialize(name, getValue()); + } + + void serialize(Serializer serializer) { + serializer.serialize(getValue()); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java b/config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java new file mode 100644 index 00000000000..789969662da --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java @@ -0,0 +1,65 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author gjoranv + * @since 5.1.17 + */ +public class LeafNodeMaps { + + /** + * Converts a map of String→NODE to String→REAL, where REAL is the underlying value type. + */ + public static <NODE extends LeafNode<REAL>, REAL> + Map<String, REAL> asValueMap(Map<String, NODE> input) + { + Map<String, REAL> ret = new LinkedHashMap<>(); + for(String key : input.keySet()) { + ret.put(key, input.get(key).value()); + } + return Collections.unmodifiableMap(ret); + } + + /** + * Converts a map of String→REAL to String→NODE, where REAL is the underlying value type. + */ + @SuppressWarnings("unchecked") + public static <NODE extends LeafNode<REAL>, REAL> + Map<String, NODE> asNodeMap(Map<String, REAL> input, NODE defaultNode) + { + Map<String, NODE> ret = new LinkedHashMap<>(); + for(String key : input.keySet()) { + NODE node = (NODE)defaultNode.clone(); + node.value = input.get(key); + ret.put(key, node); + } + return Collections.unmodifiableMap(ret); + } + + + /** + * Special case for file type, since FileNode param type (FileReference) is not same as type (String) in config builder + */ + public static Map<String, FileNode> asFileNodeMap(Map<String, String> stringMap) { + Map<String, FileNode> fileNodeMap = new LinkedHashMap<>(); + for (Map.Entry<String, String> e : stringMap.entrySet()) { + fileNodeMap.put(e.getKey(), new FileNode(e.getValue())); + } + return Collections.unmodifiableMap(fileNodeMap); + } + + public static Map<String, PathNode> asPathNodeMap(Map<String, FileReference> fileReferenceMap) { + Map<String, PathNode> pathNodeMap = new LinkedHashMap<>(); + for (Map.Entry<String, FileReference> e : fileReferenceMap.entrySet()) { + pathNodeMap.put(e.getKey(), new PathNode(e.getValue())); + } + return Collections.unmodifiableMap(pathNodeMap); + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java b/config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java new file mode 100644 index 00000000000..59e070f1d56 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java @@ -0,0 +1,79 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * A vector of leaf nodes. + * + * @author gjoranv + * @since 5.1.4 + */ +public class LeafNodeVector<REAL, NODE extends LeafNode<REAL>> extends NodeVector<NODE> { + + NODE defaultNode; + + /** + * Creates a new vector with the given default node. + */ + // TODO: remove this ctor when the library uses reflection via builders, and resizing won't be necessary + public LeafNodeVector(NODE defaultNode) { + assert (defaultNode != null) : "The default node cannot be null"; + + this.defaultNode = defaultNode; + if (createNew() == null) { + throw new NullPointerException("Unable to duplicate the default node."); + } + } + + // TODO: take class instead of default node when the library uses reflection via builders + public LeafNodeVector(List<REAL> values, NODE defaultNode) { + this(defaultNode); + for (REAL value : values) { + NODE node = createNew(); + node.value = value; + vector.add(node); + } + } + + /** + * Creates a new Node by cloning the default node. + */ + @SuppressWarnings("unchecked") + protected NODE createNew() { + return (NODE) (defaultNode).clone(); + } + + // TODO: create unmodifiable list in ctor when the library uses reflection via builders + @SuppressWarnings("unchecked") + public List<REAL> asList() { + List<REAL> ret = new ArrayList<REAL>(); + for(NODE node : vector) { + ret.add(node.value()); + } + return Collections.unmodifiableList(ret); + } + + // TODO: Try to eliminate the need for this method when we have moved FileAcquirer to the config library + // It is needed now because the builder has a list of String, while REAL=FileReference. + public static LeafNodeVector<FileReference, FileNode> createFileNodeVector(Collection<String> values) { + List<FileReference> fileReferences = new ArrayList<FileReference>(); + for (String s : values) + fileReferences.add(new FileReference(ReferenceNode.stripQuotes(s))); + + return new LeafNodeVector<FileReference, FileNode>(fileReferences, new FileNode()); + } + + public static LeafNodeVector<Path, PathNode> createPathNodeVector(Collection<FileReference> values) { + List<Path> paths = new ArrayList<>(); + for (FileReference fileReference : values) + paths.add(Paths.get(fileReference.value())); + + return new LeafNodeVector<Path, PathNode>(paths, new PathNode()); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/LongNode.java b/config-lib/src/main/java/com/yahoo/config/LongNode.java new file mode 100755 index 00000000000..7a3b29c1101 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/LongNode.java @@ -0,0 +1,53 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a long in a configuration tree. + * @author gjoranv + */ +public class LongNode extends LeafNode<Long> { + + public LongNode() { + } + + public LongNode(long value) { + super(true); + this.value = value; + } + + public Long value() { + return value; + } + + @Override + public String getValue() { + return "" + value; + } + + @Override + public String toString() { + return getValue(); + } + + @Override + protected boolean doSetValue(@NonNull String value) { + try { + this.value = Long.parseLong(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + void serialize(String name, Serializer serializer) { + serializer.serialize(name, value); + } + + @Override + void serialize(Serializer serializer) { + serializer.serialize(value); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/Node.java b/config-lib/src/main/java/com/yahoo/config/Node.java new file mode 100644 index 00000000000..3dba5d083f4 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/Node.java @@ -0,0 +1,29 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +/** + * The Node class is superclass for all nodes in a {@link + * ConfigInstance}. Important subclasses of this node are {@link + * InnerNode} and {@link LeafNode}. + * + */ +public abstract class Node { + + /** + * Postinitialize this node. Any node needing to process its values depending on the config + * id should override this method. + * + * @param configId the configId of the ConfigInstance that owns (or is) this node + */ + public void postInitialize(String configId) { return; } + + /** + * This method is meant for internal use in the configuration system. + * Overrides Object.clone(), and is overriden by LeafNode.clone(). + * + * @return a new instance similar to this object. + */ + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/NodeVector.java b/config-lib/src/main/java/com/yahoo/config/NodeVector.java new file mode 100644 index 00000000000..8ce5689937e --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/NodeVector.java @@ -0,0 +1,151 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import java.util.*; + + +/** + * A NodeVector represents an array declared with '[]' in a config definition file. + * It is a List that stores nodes with a given type. A given default node must + * be given, and this node will be cloned as the NodeVector size are increased. + * + */ +public abstract class NodeVector<NODE> implements java.util.List<NODE> { + + protected final ArrayList<NODE> vector = new ArrayList<NODE>(); + + /** + * Creates a new Node. + */ + protected abstract NODE createNew(); + + /** + * Returns the number of elements in this NodeVector. + * Alias for size(). + * + * @return the number of elements in this NodeVector. + */ + public int length() { + return size(); + } + + /** + * Resizes this NodeVector. Removes or adds new nodes as needed. + * + * @param n the new size of this NodeVector + */ + // TODO: remove when the library uses reflection via builders, and resizing won't be necessary + public void setSize(int n) { + while (size() > n) vector.remove(n); + while (size() < n) vector.add(createNew()); + } + + @SuppressWarnings("serial") + public static class ReadOnlyException extends RuntimeException { + } + + private static final ReadOnlyException e = new ReadOnlyException(); + + public void add(int index, NODE element) { + throw e; + } + + public boolean add(NODE o) { + throw e; + } + + public boolean addAll(Collection<? extends NODE> c) { + throw e; + } + + public boolean addAll(int index, Collection<? extends NODE> c) { + throw e; + } + + public void clear() { + throw e; + } + + public NODE remove(int index) { + throw e; + } + + public boolean remove(Object o) { + throw e; + } + + public boolean removeAll(Collection<?> c) { + throw e; + } + + public boolean retainAll(Collection<?> c) { + throw e; + } + + public NODE set(int index, NODE element) { + throw e; + } + + public boolean contains(Object o) { + return vector.contains(o); + } + + public boolean containsAll(Collection<?> c) { + return vector.containsAll(c); + } + + @Override + public boolean equals(Object o) { + return o instanceof NodeVector && vector.equals(((NodeVector) o).vector); + } + + @Override + public int hashCode() { + return vector.hashCode(); + } + + @SuppressWarnings("unchecked") + public NODE get(int index) { + return vector.get(index); + } + + public int indexOf(Object o) { + return vector.indexOf(o); + } + + public boolean isEmpty() { + return vector.isEmpty(); + } + + public Iterator<NODE> iterator() { + return vector.iterator(); + } + + public int lastIndexOf(Object o) { + return vector.lastIndexOf(o); + } + + public ListIterator<NODE> listIterator() { + return vector.listIterator(); + } + + public ListIterator<NODE> listIterator(int index) { + return vector.listIterator(index); + } + + public int size() { + return vector.size(); + } + + public List<NODE> subList(int fromIndex, int toIndex) { + return vector.subList(fromIndex, toIndex); + } + + public Object[] toArray() { + return vector.toArray(); + } + + public <T> T[] toArray(T[] a) { + return vector.toArray(a); + } +} diff --git a/config-lib/src/main/java/com/yahoo/config/PathNode.java b/config-lib/src/main/java/com/yahoo/config/PathNode.java new file mode 100644 index 00000000000..91676137214 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/PathNode.java @@ -0,0 +1,71 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents a 'path' in a {@link ConfigInstance}, usually a filename. + * + * @author gjoranv + * @since 5.1.30 + */ +public class PathNode extends LeafNode<Path> { + + private final FileReference fileReference; + + public PathNode() { + fileReference = null; + } + + public PathNode(FileReference fileReference) { + super(true); + this.value = new File(fileReference.value()).toPath(); + this.fileReference = fileReference; + } + + public Path value() { + return value; + } + + @Override + public String getValue() { + return value.toString(); + } + + @Override + public String toString() { + return (value == null) ? "(null)" : '"' + getValue() + '"'; + } + + @Override + protected boolean doSetValue(@NonNull String stringVal) { + throw new UnsupportedOperationException("doSetValue should not be necessary since the library anymore!"); + } + + public FileReference getFileReference() { + return fileReference; + } + + public static List<FileReference> toFileReferences(List<PathNode> pathNodes) { + List<FileReference> fileReferences = new ArrayList<>(); + for (PathNode pathNode : pathNodes) + fileReferences.add(pathNode.getFileReference()); + return fileReferences; + } + + public static Map<String, FileReference> toFileReferenceMap(Map<String, PathNode> map) { + Map<String, FileReference> ret = new LinkedHashMap<>(); + for (Map.Entry<String, PathNode> e : map.entrySet()) { + ret.put(e.getKey(), e.getValue().getFileReference()); + } + return ret; + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/ReferenceNode.java b/config-lib/src/main/java/com/yahoo/config/ReferenceNode.java new file mode 100644 index 00000000000..a5e6400f456 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/ReferenceNode.java @@ -0,0 +1,83 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A ReferenceNode class represents a reference (that is a config-id) + * in a {@link ConfigInstance}. + */ +public class ReferenceNode extends LeafNode<String> { + + public ReferenceNode() { + } + + /** + * Creates a new ReferenceNode with the given value. + * + * @param value the value of this ReferenceNode + */ + public ReferenceNode(String value) { + super(true); + this.value = stripQuotes(value); + } + + /** + * Returns the value of this reference node. Same as {@link + * #toString()} since the value of a ReferenceNode is a String (but + * implementations in other {@link LeafNode} sub-classes differ). + * + * @return the string representation of this ReferenceNode. + */ + public String value() { + return value; + } + + @Override + public String getValue() { + return value(); + } + + @Override + public String toString() { + return (value == null) ? "(null)" : getValue(); + } + + @Override + protected boolean doSetValue(@NonNull String value) { + this.value = stripQuotes(value); + return true; + } + + /** + * Overrides {@link Node#postInitialize(String)} + * Checks for ":parent:" values, which will be replaced by the configId. + * + * @param configId the configId of the ConfigInstance that owns (or is) this node + */ + @Override + public void postInitialize(String configId) { + super.postInitialize(configId); + if (":parent:".equals(value())) { + doSetValue(configId); + } + } + + /** + * Strips the quotes before or after the value, if present. + */ + static String stripQuotes(String value) { + if (value == null) { + return value; + } + StringBuffer buffer = new StringBuffer(value.trim()); + if (buffer.length() > 0 && buffer.charAt(0) == '"') { + buffer.deleteCharAt(0); + } + if (buffer.length() > 0 && buffer.charAt(buffer.length() - 1) == '"') { + buffer.setLength(buffer.length() - 1); + } + return buffer.toString(); + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/Serializer.java b/config-lib/src/main/java/com/yahoo/config/Serializer.java new file mode 100644 index 00000000000..95d20011f2c --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/Serializer.java @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +/** +* @author lulf +* @since 5.1 +*/ +public interface Serializer { + Serializer createInner(String name); + Serializer createArray(String name); + Serializer createInner(); + Serializer createMap(String name); + + /** + * Serialize leaf values. + */ + void serialize(String name, boolean value); + void serialize(String name, double value); + void serialize(String name, long value); + void serialize(String name, int value); + void serialize(String name, String value); + + /** + * Serialize array values. + */ + void serialize(boolean value); + void serialize(double value); + void serialize(long value); + void serialize(int value); + void serialize(String value); +} diff --git a/config-lib/src/main/java/com/yahoo/config/StringNode.java b/config-lib/src/main/java/com/yahoo/config/StringNode.java new file mode 100644 index 00000000000..344a7e2dd40 --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/StringNode.java @@ -0,0 +1,123 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import com.yahoo.config.text.StringUtilities; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A StringNode class represents a string in a {@link ConfigInstance}. + * + * @author larschr + */ +public class StringNode extends LeafNode<String> { + + /** + * Creates a new un-initialized StringNode. + */ + public StringNode() { + } + + /** + * Creates a new StringNode, initialized to <code>value</code>. + * + * @param value the value of this StringNode. + */ + public StringNode(String value) { + super(true); + this.value = value; + } + + /** + * Returns the value of this string. Same as {@link #getValue()} + * since the value of this node is a String (but implementations + * in other {@link LeafNode} sub-classes differ). + * + * @return the string representation of this StringNode, or null if + * the vlaue is explicitly set to null + */ + public String value() { + return value; + } + + @Override + public String getValue() { + return value(); + } + + @Override + public String toString() { + return (value == null) ? "(null)" : '"' + StringUtilities.escape(getValue()) + '"'; + } + + /** + * Remove character escape codes. + * + * @param string escaped string + * @return unescaped string + */ + public static String unescapeQuotedString(String string) { + StringBuilder sb = new StringBuilder(string); + for (int i = 0; i < sb.length(); i++) { + if (sb.charAt(i) == '\\') { + sb.deleteCharAt(i); + if (i == sb.length()) { + throw new IllegalArgumentException("Parse error" + string); + } + switch (sb.charAt(i)) { + case'n': + sb.setCharAt(i, '\n'); + break; + case'r': + sb.setCharAt(i, '\r'); + break; + case't': + sb.setCharAt(i, '\t'); + break; + case'f': + sb.setCharAt(i, '\f'); + break; + case'x': + if (i + 2 >= sb.length()) { + throw new IllegalArgumentException + ("Could not parse hex value " + string); + } + sb.setCharAt(i, (char) Integer.parseInt + (sb.substring(i + 1, i + 3), 16)); + sb.delete(i + 1, i + 3); + break; + case'\\': + sb.setCharAt(i, '\\'); + break; + } + } + } + + if (sb.length() > 0 && (sb.charAt(0) == '"') && sb.charAt(sb.length() - 1) == '"') { + sb.deleteCharAt(sb.length() - 1);//remove last quote + if (sb.length() > 0) { + sb.deleteCharAt(0); //remove first quote + } + } + return sb.toString(); + } + + /** + * Sets the value of this string from a the string representation + * of this value in the (escaped) input configuration. The value + * supplied to this method needs un-escaping and will be + * un-escaped. + * + * @param value the new value of this node. + */ + @Override + protected boolean doSetValue(@NonNull String value) { + if (value.startsWith("\"") && value.endsWith("\"")) + this.value = unescapeQuotedString(value); + else { + //TODO: unquoted strings can be probably be prohibited now.(?) -gv + this.value = value; + } + return true; + } + +} diff --git a/config-lib/src/main/java/com/yahoo/config/package-info.java b/config-lib/src/main/java/com/yahoo/config/package-info.java new file mode 100644 index 00000000000..c1069b6649a --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/package-info.java @@ -0,0 +1,7 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +@PublicApi +package com.yahoo.config; + +import com.yahoo.api.annotations.PublicApi; +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/config-lib/src/main/java/com/yahoo/config/text/StringUtilities.java b/config-lib/src/main/java/com/yahoo/config/text/StringUtilities.java new file mode 100644 index 00000000000..1b05ae779bf --- /dev/null +++ b/config-lib/src/main/java/com/yahoo/config/text/StringUtilities.java @@ -0,0 +1,88 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.text; + +import java.nio.charset.Charset; +import java.io.ByteArrayOutputStream; + +/** + * Escapes strings into and out of a format where they only contain printable characters. + * + * Need to duplicate escape / unescape of strings as we have in C++ for java version of system states. + * + * @author <a href="mailto:humbe@yahoo-inc.com">Haakon Humberset</a> + */ +public class StringUtilities { + private static Charset UTF8 = Charset.forName("utf8"); + + private static byte toHex(int val) { return (byte) (val < 10 ? '0' + val : 'a' + (val - 10)); } + + private static class ReplacementCharacters { + public byte needEscape[] = new byte[256]; + public byte replacement1[] = new byte[256]; + public byte replacement2[] = new byte[256]; + + public ReplacementCharacters() { + for (int i=0; i<256; ++i) { + if (i >= 32 && i <= 126) { + needEscape[i] = 0; + } else if (i > 127) { + needEscape[i] = 0; + } else { + needEscape[i] = 3; + replacement1[i] = toHex((i >> 4) & 0xF); + replacement2[i] = toHex(i & 0xF); + } + } + makeSimpleEscape('"', '"'); + makeSimpleEscape('\\', '\\'); + makeSimpleEscape('\t', 't'); + makeSimpleEscape('\n', 'n'); + makeSimpleEscape('\r', 'r'); + makeSimpleEscape('\f', 'f'); + } + + private void makeSimpleEscape(char source, char dest) { + needEscape[source] = 1; + replacement1[source] = '\\'; + replacement2[source] = (byte) dest; + } + } + + private final static ReplacementCharacters replacementCharacters = new ReplacementCharacters(); + + public static String escape(String source) { return escape(source, '\0'); } + + /** + * Escapes strings into a format with only printable ASCII characters. + * + * @param source The string to escape + * @param delimiter Escape this character too, even if it is printable. + * @return The escaped string + */ + public static String escape(String source, char delimiter) { + byte bytes[] = source.getBytes(UTF8); + ByteArrayOutputStream result = new ByteArrayOutputStream(); + for (byte b : bytes) { + int val = b; + if (val < 0) val += 256; + if (b == delimiter) { + result.write('\\'); + result.write('x'); + result.write(toHex((val >> 4) & 0xF)); + result.write(toHex(val & 0xF)); + } else if (replacementCharacters.needEscape[val] == 0) { + result.write(b); + } else { + if (replacementCharacters.needEscape[val] == 3) { + result.write('\\'); + result.write('x'); + } + result.write(replacementCharacters.replacement1[val]); + result.write(replacementCharacters.replacement2[val]); + } + } + return new String(result.toByteArray(), UTF8); + } + + +} diff --git a/config-lib/src/test/java/com/yahoo/config/BooleanNodeTest.java b/config-lib/src/test/java/com/yahoo/config/BooleanNodeTest.java new file mode 100644 index 00000000000..f484c2080d1 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/BooleanNodeTest.java @@ -0,0 +1,24 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class BooleanNodeTest { + @Test + public void testSetValue() { + BooleanNode n = new BooleanNode(); + assertTrue(n.doSetValue("true")); + assertTrue(n.doSetValue("TRUE")); + assertTrue(n.doSetValue("false")); + assertTrue(n.doSetValue("FALSE")); + assertFalse(n.doSetValue("FALSEa")); + assertFalse(n.doSetValue("aFALSE")); + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/ConfigInstanceBuilderTest.java b/config-lib/src/test/java/com/yahoo/config/ConfigInstanceBuilderTest.java new file mode 100644 index 00000000000..78c97935d5a --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/ConfigInstanceBuilderTest.java @@ -0,0 +1,399 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import com.yahoo.foo.MaptypesConfig; +import com.yahoo.test.FunctionTestConfig; +import com.yahoo.test.IntConfig; +import com.yahoo.test.RestartConfig; +import org.junit.Test; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static com.yahoo.test.FunctionTestConfig.BasicStruct; +import static com.yahoo.test.FunctionTestConfig.Enum_val; +import static com.yahoo.test.FunctionTestConfig.Enumarr; +import static com.yahoo.test.FunctionTestConfig.Enumwithdef; +import static com.yahoo.test.FunctionTestConfig.Myarray; +import static com.yahoo.test.FunctionTestConfig.RootStruct; +import static com.yahoo.test.FunctionTestConfig.MyStructMap; +import static com.yahoo.foo.MaptypesConfig.Innermap; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author gjoranv + * @since 5.1.11 + */ +public class ConfigInstanceBuilderTest +{ + + @Test + public void leaf_map_setter_merges_maps() { + MaptypesConfig.Builder builder = new MaptypesConfig.Builder() + .intmap("one", 1); + + Map<String, Integer> newMap = new HashMap<>(); + newMap.put("two", 2); + builder.intmap(newMap); + + MaptypesConfig config = new MaptypesConfig(builder); + assertThat(config.intmap("one"), is(1)); + assertThat(config.intmap("two"), is(2)); + } + + @Test + public void inner_map_setter_merges_maps() { + MaptypesConfig.Builder builder = new MaptypesConfig.Builder() + .innermap("one", new Innermap.Builder() + .foo(1)); + + Map<String, Innermap.Builder> newMap = new HashMap<>(); + newMap.put("two", new Innermap.Builder().foo(2)); + builder.innermap(newMap); + + MaptypesConfig config = new MaptypesConfig(builder); + assertThat(config.innermap("one").foo(), is(1)); + assertThat(config.innermap("two").foo(), is(2)); + } + + @Test + public void testVariableAccessWithBuilder() { + FunctionTestConfig config = createVariableAccessConfigWithBuilder(); + assertVariableAccessValues(config, ":parent:"); + } + + @Test + public void require_that_unset_builder_fields_are_null() throws Exception { + FunctionTestConfig.Builder builder = new FunctionTestConfig.Builder(); + assertNull(getMember(builder, "bool_val")); + assertNull(getMember(builder, "bool_with_def")); + assertNull(getMember(builder, "int_val")); + assertNull(getMember(builder, "int_with_def")); + assertNull(getMember(builder, "long_val")); + assertNull(getMember(builder, "long_with_def")); + assertNull(getMember(builder, "double_val")); + assertNull(getMember(builder, "double_with_def")); + assertNull(getMember(builder, "string_val")); + assertNull(getMember(builder, "stringwithdef")); + assertNull(getMember(builder, "enum_val")); + assertNull(getMember(builder, "enumwithdef")); + assertNull(getMember(builder, "refval")); + assertNull(getMember(builder, "refwithdef")); + assertNull(getMember(builder, "fileVal")); + + BasicStruct.Builder basicStructBuilder = new BasicStruct.Builder(); + assertNull(getMember(basicStructBuilder, "foo")); + assertNull(getMember(basicStructBuilder, "bar")); + } + + @Test + public void require_that_set_builder_fields_are_nonNull() throws Exception { + FunctionTestConfig.Builder builder = createVariableAccessBuilder(); + assertNotNull(getMember(builder, "bool_val")); + assertNotNull(getMember(builder, "bool_with_def")); + assertNotNull(getMember(builder, "int_val")); + assertNotNull(getMember(builder, "int_with_def")); + assertNotNull(getMember(builder, "long_val")); + assertNotNull(getMember(builder, "long_with_def")); + assertNotNull(getMember(builder, "double_val")); + assertNotNull(getMember(builder, "double_with_def")); + assertNotNull(getMember(builder, "string_val")); + assertNotNull(getMember(builder, "stringwithdef")); + assertNotNull(getMember(builder, "enum_val")); + assertNotNull(getMember(builder, "enumwithdef")); + assertNotNull(getMember(builder, "refval")); + assertNotNull(getMember(builder, "refwithdef")); + assertNotNull(getMember(builder, "fileVal")); + + BasicStruct.Builder basicStructBuilder = (BasicStruct.Builder)getMember(builder, "basicStruct"); + assertNotNull(getMember(basicStructBuilder, "foo")); + assertNotNull(getMember(basicStructBuilder, "bar")); + } + + @Test + public void require_that_config_can_be_recreated_from_another_configs_builder() { + FunctionTestConfig original = createVariableAccessConfigWithBuilder(); + FunctionTestConfig copy = new FunctionTestConfig(new FunctionTestConfig.Builder(original)); + assertVariableAccessValues(copy, ":parent:"); + } + + private Object getMember(Object builder, String memberName) throws Exception { + Field field = builder.getClass().getDeclaredField(memberName); + field.setAccessible(true); + return field.get(builder); + } + + static FunctionTestConfig createVariableAccessConfigWithBuilder() { + return new FunctionTestConfig(createVariableAccessBuilder()); + } + + static FunctionTestConfig.Builder createVariableAccessBuilder() { + return new FunctionTestConfig.Builder(). + bool_val(false). + bool_with_def(true). + int_val(5). + int_with_def(-14). + long_val(12345678901L). + long_with_def(-9876543210L). + double_val(41.23). + double_with_def(-12). + string_val("foo"). + stringwithdef("bar and foo"). + enum_val(Enum_val.FOOBAR). + enumwithdef(Enumwithdef.BAR2). + refval(":parent:"). + refwithdef(":parent:"). + fileVal("etc"). + pathVal(FileReference.mockFileReferenceForUnitTesting(new File("pom.xml"))). + boolarr(false). + longarr(9223372036854775807L). + longarr(-9223372036854775808L). + doublearr(2344.0). + doublearr(123.0). + stringarr("bar"). + enumarr(Enumarr.VALUES). + refarr(Arrays.asList(":parent:", ":parent", "parent:")). // test collection based setter + fileArr("bin"). + intMap("one", 1). + intMap("two", 2). + stringMap("one", "first"). + filemap("f1", "/var"). + filemap("f2", "/store"). + + basicStruct(new BasicStruct.Builder(). + foo("basicFoo"). + bar(3). + intArr(310).intArr(311)). + + rootStruct(new RootStruct.Builder(). + inner0(new RootStruct.Inner0.Builder(). + index(11)). + inner1(new RootStruct.Inner1.Builder(). + index(12)). + innerArr(new RootStruct.InnerArr.Builder(). + boolVal(true). + stringVal("deep")). + innerArr(new RootStruct.InnerArr.Builder(). + boolVal(false). + stringVal("blue a=\"escaped\""))). + + myarray(new Myarray.Builder(). + intval(-5). + stringval("baah"). + stringval("yikes"). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file0"). + anotherarray(new Myarray.Anotherarray.Builder(). + foo(7)). + myStruct(new Myarray.MyStruct.Builder(). + a(1). + b(2))). + + myarray(new Myarray.Builder(). + intval(5). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file1"). + anotherarray(new Myarray.Anotherarray.Builder(). + foo(1). + foo(2)). + myStruct(new Myarray.MyStruct.Builder(). + a(-1). + b(-2))). + + myStructMap("one", new MyStructMap.Builder(). + myInt(1). + myString("bull"). + myIntDef(2). + myStringDef("bear"). + anotherMap("anotherOne", new MyStructMap.AnotherMap.Builder(). + anInt(3). + anIntDef(4))); + } + + public static void assertVariableAccessValues(FunctionTestConfig config, String configId) { + assertTrue(config.bool_with_def()); + assertEquals(5, config.int_val()); + assertEquals(-14, config.int_with_def()); + assertEquals(12345678901L, config.long_val()); + assertEquals(-9876543210L, config.long_with_def()); + assertEquals(41.23, config.double_val(), 0.000001); + assertEquals(-12, config.double_with_def(), 0.000001); + assertEquals("foo", config.string_val()); + assertEquals("bar and foo", config.stringwithdef()); + assertEquals(FunctionTestConfig.Enum_val.FOOBAR, config.enum_val()); + assertEquals(FunctionTestConfig.Enumwithdef.BAR2, config.enumwithdef()); + assertEquals(configId, config.refval()); + assertEquals(configId, config.refwithdef()); + assertEquals("etc", config.fileVal().value()); + assertEquals(1, config.boolarr().size()); + assertEquals(1, config.boolarr().size()); // new api with accessor for a List of the original Java type + assertEquals(false, config.boolarr().get(0)); // new List api + assertEquals(false, config.boolarr(0)); // short-hand + assertEquals(0, config.intarr().size()); + assertEquals(2, config.longarr().size()); + assertEquals(Long.MAX_VALUE, config.longarr(0)); + assertThat(config.longarr().get(1), is(Long.MIN_VALUE)); + assertEquals(2, config.doublearr().size()); + assertEquals(1, config.stringarr().size()); + assertEquals(1, config.enumarr().size()); + assertEquals(FunctionTestConfig.Enumarr.VALUES, config.enumarr().get(0)); // new List api, don't have to call value() + assertEquals(3, config.refarr().size()); + assertEquals(1, config.fileArr().size()); + assertEquals(configId, config.refarr(0)); + assertEquals(":parent", config.refarr(1)); + assertEquals("parent:", config.refarr(2)); + assertEquals("bin", config.fileArr(0).value()); + + assertThat(config.intMap("one"), is(1)); + assertThat(config.intMap("two"), is(2)); + assertThat(config.stringMap("one"), is("first")); + assertThat(config.filemap("f1").value(), is("/var")); + assertThat(config.filemap("f2").value(), is("/store")); + assertEquals("basicFoo", config.basicStruct().foo()); + assertEquals(3, config.basicStruct().bar()); // new List api + assertEquals(2, config.basicStruct().intArr().size()); + assertThat(config.basicStruct().intArr().get(0), is(310)); // new List api + assertThat(config.basicStruct().intArr().get(1), is(311)); // new List api + assertEquals(310, config.basicStruct().intArr(0)); // short-hand + assertEquals("inner0", config.rootStruct().inner0().name()); // new List api + assertEquals(11, config.rootStruct().inner0().index()); + assertEquals("inner1", config.rootStruct().inner1().name()); + assertEquals(12, config.rootStruct().inner1().index()); + assertEquals(2, config.rootStruct().innerArr().size()); + assertEquals(true, config.rootStruct().innerArr(0).boolVal()); + assertEquals("deep", config.rootStruct().innerArr(0).stringVal()); + assertEquals(false, config.rootStruct().innerArr(1).boolVal()); + assertEquals("blue a=\"escaped\"", config.rootStruct().innerArr(1).stringVal()); + + assertEquals(2, config.myarray().size()); // new List api + assertEquals(configId, config.myarray().get(0).refval()); // new List api + assertEquals(configId, config.myarray(0).refval()); // short-hand + assertEquals("file0", config.myarray(0).fileVal().value()); + assertEquals(1, config.myarray(0).myStruct().a()); + assertEquals(2, config.myarray(0).myStruct().b()); + assertEquals(configId, config.myarray(1).refval()); + assertEquals("file1", config.myarray(1).fileVal().value()); + assertEquals(-1, config.myarray(1).myStruct().a()); + assertEquals(-2, config.myarray(1).myStruct().b()); + + assertThat(config.myStructMap("one").myInt(), is(1)); + assertThat(config.myStructMap("one").myString(), is("bull")); + assertThat(config.myStructMap("one").myIntDef(), is(2)); + assertThat(config.myStructMap("one").myStringDef(), is("bear")); + assertThat(config.myStructMap("one").anotherMap("anotherOne").anInt(), is(3)); + assertThat(config.myStructMap("one").anotherMap("anotherOne").anIntDef(), is(4)); + } + + private boolean callContainsFieldsFlaggedWithRestart(Class<?> configClass) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Method m = configClass.getDeclaredMethod("containsFieldsFlaggedWithRestart"); + m.setAccessible(true); + return (boolean) m.invoke(null); + } + + private ChangesRequiringRestart callGetChangesRequiringRestart(ConfigInstance config1, ConfigInstance config2) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Method m = config1.getClass().getDeclaredMethod("getChangesRequiringRestart", config2.getClass()); + m.setAccessible(true); + return (ChangesRequiringRestart) m.invoke(config1, config2); + } + + @Test + public void require_that_config_class_reports_any_restart_values() throws Exception { + assertTrue(callContainsFieldsFlaggedWithRestart(RestartConfig.class)); + assertFalse(callContainsFieldsFlaggedWithRestart(IntConfig.class)); + } + + @Test + public void require_that_config_class_can_make_change_report() throws Exception { + IntConfig noRestart1 = new IntConfig(new IntConfig.Builder().intVal(42)); + IntConfig noRestart2 = new IntConfig(new IntConfig.Builder().intVal(21)); + ChangesRequiringRestart report = callGetChangesRequiringRestart(noRestart1, noRestart2); + assertFalse(report.needsRestart()); + + RestartConfig config1 = new RestartConfig(new RestartConfig.Builder().intVal(42)); + RestartConfig config2 = new RestartConfig(new RestartConfig.Builder().intVal(21)); + report = callGetChangesRequiringRestart(config1, config1); + assertFalse(report.needsRestart()); + report = callGetChangesRequiringRestart(config1, config2); + assertTrue(report.needsRestart()); + + FunctionTestConfig function1 = createVariableAccessConfigWithBuilder(); + FunctionTestConfig.Builder funcBuilder = createVariableAccessBuilder(); + funcBuilder.myStructMap.get("one").myInt(42); + funcBuilder.int_val(100); + funcBuilder.stringarr.set(0, "foo"); + funcBuilder.intMap.put("one", 42); + funcBuilder.intMap.remove("two"); + funcBuilder.intMap.put("three", 3); + funcBuilder.myarray.get(1).intval(17); + funcBuilder.myarray.get(0).anotherarray.get(0).foo(32); + funcBuilder.myarray.add(new Myarray.Builder().refval("refval").fileVal("fileval").myStruct(new Myarray.MyStruct.Builder().a(4))); + funcBuilder.myStructMap.put("new", new MyStructMap.Builder().myString("string").myInt(13)); + funcBuilder.basicStruct(new BasicStruct.Builder().bar(1234)); + FunctionTestConfig function2 = new FunctionTestConfig(funcBuilder); + report = callGetChangesRequiringRestart(function1, function1); + assertFalse(report.needsRestart()); + report = callGetChangesRequiringRestart(function1, function2); + assertTrue(report.needsRestart()); + assertEquals("function-test", report.getName()); + assertThat( + report.toString(), + startsWith( + "# An int value\n" + + "# Also test that multiline comments\n" + + "# work.\n" + + "function-test.int_val has changed from 5 to 100\n" + + "function-test.stringarr[0] has changed from \"bar\" to \"foo\"\n" + + "# This is a map of ints.\n" + + "function-test.intMap{one} has changed from 1 to 42\n" + + "# This is a map of ints.\n" + + "function-test.intMap{two} with value 2 was removed\n" + + "# This is a map of ints.\n" + + "function-test.intMap{three} was added with value 3\n" + + "# A basic struct\n" + + "function-test.basicStruct.foo has changed from \"basicFoo\" to \"basic\"\n" + + "function-test.basicStruct.bar has changed from 3 to 1234\n" + + "function-test.basicStruct.intArr[0] with value 310 was removed\n" + + "function-test.basicStruct.intArr[1] with value 311 was removed\n" + + "function-test.myarray[0].anotherarray[0].foo has changed from 7 to 32\n" + + "# This is my array\n" + + "function-test.myarray[1].intval has changed from 5 to 17\n" + + "function-test.myarray[2] was added with value \n" + ) + ); + + assertThat( + report.toString(), + containsString( + "function-test.myStructMap{one}.myInt has changed from 1 to 42\n" + + "function-test.myStructMap{new} was added with value \n" + ) + ); + + funcBuilder.myStructMap.remove("one"); + FunctionTestConfig function3 = new FunctionTestConfig(funcBuilder); + report = callGetChangesRequiringRestart(function2, function3); + assertEquals(1, report.getReportLines().size()); + assertThat( + report.toString(), + containsString("function-test.myStructMap{one} with value \n") + ); + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/ConfigInstanceEqualsTest.java b/config-lib/src/test/java/com/yahoo/config/ConfigInstanceEqualsTest.java new file mode 100644 index 00000000000..f99639420e2 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/ConfigInstanceEqualsTest.java @@ -0,0 +1,184 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import com.yahoo.test.AppConfig; +import com.yahoo.test.FunctionTestConfig; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Arrays; + +import static com.yahoo.test.FunctionTestConfig.BasicStruct; +import static com.yahoo.test.FunctionTestConfig.Enum_val; +import static com.yahoo.test.FunctionTestConfig.Enumarr; +import static com.yahoo.test.FunctionTestConfig.Myarray; +import static com.yahoo.test.FunctionTestConfig.RootStruct; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; + +public class ConfigInstanceEqualsTest { + FunctionTestConfig config1; + FunctionTestConfig.Builder builder2; + FunctionTestConfig config2; + + @Before + public void reset() { + config1 = new FunctionTestConfig(newBuilder()); + builder2 = newBuilder(); + config2 = new FunctionTestConfig(builder2); + } + + @Test + public void require_same_hashCode_for_equal_instances() { + assertThat(config1.hashCode(), is(config2.hashCode())); + } + + @Test + public void require_true_for_equal_instances() { + assertThat(config1, is(config2)); + } + + @Test + public void require_false_for_null() { + assertThat(config1, not((FunctionTestConfig) null)); + + } + + @Test + public void require_false_for_different_subclass() { + assertFalse(config1.equals(new AppConfig(new AppConfig.Builder()))); + } + + @Test + public void require_false_for_different_scalars_at_root_node() { + assertThat(config1, not(new FunctionTestConfig(newBuilder().bool_val(true)))); + assertThat(config1, not(new FunctionTestConfig(newBuilder().int_val(0)))); + assertThat(config1, not(new FunctionTestConfig(newBuilder().long_val(0L)))); + assertThat(config1, not(new FunctionTestConfig(newBuilder().double_val(0.0)))); + assertThat(config1, not(new FunctionTestConfig(newBuilder().string_val("")))); + assertThat(config1, not(new FunctionTestConfig(newBuilder().enum_val(Enum_val.FOO)))); + assertThat(config1, not(new FunctionTestConfig(newBuilder().refval("")))); + assertThat(config1, not(new FunctionTestConfig(newBuilder().fileVal("")))); + } + + @Test + public void require_false_for_different_leaf_array_at_root_node() { + builder2.longarr.set(0, 0L); + assertThat(config1, not(new FunctionTestConfig(builder2))); + } + + @Test + public void require_false_for_different_scalar_in_struct() { + builder2.basicStruct(new BasicStruct.Builder(config1.basicStruct()).bar(0)); + assertThat(config1, not(new FunctionTestConfig(builder2))); + } + + @Test + public void require_false_for_different_scalar_in_inner_array() { + builder2.myarray.get(0).intval(0); + assertThat(config1, not(new FunctionTestConfig(builder2))); + } + + @Test + public void require_false_for_different_leaf_array_in_inner_array() { + builder2.myarray.get(0).stringval.set(0, ""); + assertThat(config1, not(new FunctionTestConfig(builder2))); + } + + @Test + public void require_equal_structs_for_equal_configs() { + assertThat(config1.basicStruct(), is(config2.basicStruct())); + assertThat(config1.rootStruct(), is(config2.rootStruct())); + assertThat(config1.rootStruct().inner0(), is(config2.rootStruct().inner0())); + } + + @Test + public void require_equal_inner_arrays_for_equal_configs() { + assertThat(config1.myarray(), is(config2.myarray())); + assertThat(config1.myarray(0).anotherarray(), is(config2.myarray(0).anotherarray())); + } + + @Test + public void require_equal_inner_array_elements_for_equal_configs() { + assertThat(config1.myarray(0), is(config2.myarray(0))); + assertThat(config1.myarray(0).anotherarray(0), is(config2.myarray(0).anotherarray(0))); + } + + @Test + public void require_equal_leaf_arrays_for_equal_configs() { + assertThat(config1.intarr(), is(config2.intarr())); + assertThat(config1.boolarr(), is(config2.boolarr())); + assertThat(config1.longarr(), is(config2.longarr())); + assertThat(config1.doublearr(), is(config2.doublearr())); + assertThat(config1.stringarr(), is(config2.stringarr())); + assertThat(config1.enumarr(), is(config2.enumarr())); + assertThat(config1.refarr(), is(config2.refarr())); + assertThat(config1.fileArr(), is(config2.fileArr())); + } + + private static FunctionTestConfig.Builder newBuilder() { + FunctionTestConfig.Builder builder = new FunctionTestConfig.Builder(); + + return builder.bool_val(false). + int_val(5). + long_val(12345678901L). + double_val(41.23). + string_val("foo"). + enum_val(Enum_val.FOOBAR). + refval(":parent:"). + fileVal("etc"). + pathVal(FileReference.mockFileReferenceForUnitTesting(new File("pom.xml"))). + boolarr(false). + longarr(9223372036854775807L). + longarr(-9223372036854775808L). + doublearr(2344.0). + doublearr(123.0). + stringarr("bar"). + enumarr(Enumarr.VALUES). + refarr(Arrays.asList(":parent:", ":parent", "parent:")). // test collection based setter + fileArr("bin"). + + basicStruct(new BasicStruct.Builder(). + foo("basicFoo"). + bar(3). + intArr(310)). + + rootStruct(new RootStruct.Builder(). + inner0(new RootStruct.Inner0.Builder(). + index(11)). + inner1(new RootStruct.Inner1.Builder(). + index(12)). + innerArr(new RootStruct.InnerArr.Builder(). + boolVal(true). + stringVal("deep"))). + + myarray(new Myarray.Builder(). + intval(-5). + stringval("baah"). + stringval("yikes"). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file0"). + anotherarray(new Myarray.Anotherarray.Builder(). + foo(7)). + myStruct(new Myarray.MyStruct.Builder(). + a(1). + b(2))). + + myarray(new Myarray.Builder(). + intval(5). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file1"). + anotherarray(new Myarray.Anotherarray.Builder(). + foo(1). + foo(2)). + myStruct(new Myarray.MyStruct.Builder(). + a(-1). + b(-2))); + + } +}
\ No newline at end of file diff --git a/config-lib/src/test/java/com/yahoo/config/DoubleNodeTest.java b/config-lib/src/test/java/com/yahoo/config/DoubleNodeTest.java new file mode 100644 index 00000000000..0c0d85db951 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/DoubleNodeTest.java @@ -0,0 +1,21 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * @author lulf + * @since 5.1 + */ +public class DoubleNodeTest { + @Test + public void testSetValue() { + DoubleNode n = new DoubleNode(); + assertFalse(n.doSetValue("invalid")); + assertTrue(n.doSetValue("3.14")); + assertEquals(3.14, n.value(), 0.001); + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/EnumNodeTest.java b/config-lib/src/test/java/com/yahoo/config/EnumNodeTest.java new file mode 100644 index 00000000000..c7801d3eecb --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/EnumNodeTest.java @@ -0,0 +1,42 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import edu.umd.cs.findbugs.annotations.NonNull; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * @author lulf + * @since 5.1 + */ +public class EnumNodeTest { + private static class MyNode extends EnumNode<MyNode.Enum> { + public enum Enum { ONE, TWO } + public final static Enum ONE = Enum.ONE; + public final static Enum TWO = Enum.TWO; + + @Override + protected boolean doSetValue(@NonNull String name) { + try { + value = Enum.valueOf(name); + return true; + } catch (IllegalArgumentException e) { + } + return false; + } + + } + + @Test + public void testEnumNode() { + MyNode n = new MyNode(); + assertNull(n.getValue()); + assertThat(n.toString(), is("(null)")); + assertTrue(n.doSetValue("ONE")); + assertThat(n.getValue(), is("ONE")); + assertThat(n.toString(), is("ONE")); + assertFalse(n.doSetValue("THREE")); + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/FileNodeTest.java b/config-lib/src/test/java/com/yahoo/config/FileNodeTest.java new file mode 100644 index 00000000000..2b0816a5b81 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/FileNodeTest.java @@ -0,0 +1,26 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class FileNodeTest { + @Test + public void testSetValue() { + FileNode n = new FileNode(); + assertThat(n.toString(), is("(null)")); + assertTrue(n.doSetValue("foo.txt")); + assertThat(n.value().value(), is("foo.txt")); + assertTrue(n.doSetValue("\"foo.txt\"")); + assertThat(n.value().value(), is("foo.txt")); + assertThat(n.toString(), is("\"foo.txt\"")); + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/IntegerNodeTest.java b/config-lib/src/test/java/com/yahoo/config/IntegerNodeTest.java new file mode 100644 index 00000000000..bc675f63af8 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/IntegerNodeTest.java @@ -0,0 +1,23 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class IntegerNodeTest { + @Test + public void testSetValue() { + IntegerNode n = new IntegerNode(); + assertFalse(n.setValue("invalid")); + assertTrue(n.setValue("10")); + assertThat(n.value(), is(10)); + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/LongNodeTest.java b/config-lib/src/test/java/com/yahoo/config/LongNodeTest.java new file mode 100644 index 00000000000..f403d075aca --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/LongNodeTest.java @@ -0,0 +1,23 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class LongNodeTest { + @Test + public void testSetValue() { + LongNode n = new LongNode(); + assertFalse(n.setValue("invalid")); + assertTrue(n.setValue("10")); + assertThat(n.value(), is(10l)); + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/NodeVectorTest.java b/config-lib/src/test/java/com/yahoo/config/NodeVectorTest.java new file mode 100644 index 00000000000..4e869632335 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/NodeVectorTest.java @@ -0,0 +1,116 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import java.util.Arrays; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class NodeVectorTest { + @Test + public void require_vector_is_resized() { + TestNodeVector v = new TestNodeVector("foo"); + v.setSize(2); + assertThat(v.size(), is(2)); + v.setSize(1); + assertThat(v.size(), is(1)); + } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_add_throws_exception() { new TestNodeVector("foo").add("bar"); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_addindex_throws_exception() { new TestNodeVector("foo").add(0, "bar"); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_addAll_throws_exception() { new TestNodeVector("foo").addAll(Arrays.asList("bar")); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_addAllindex_throws_exception() { new TestNodeVector("foo").addAll(0, Arrays.asList("bar")); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_clear_throws_exception() { new TestNodeVector("foo").clear(); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_remove_index_throws_exception() { new TestNodeVector("foo").remove(0); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_remove_object_throws_exception() { new TestNodeVector("foo").remove(null); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_removeAll_throws_exception() { new TestNodeVector("foo").removeAll(null); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_retainAll_throws_exception() { new TestNodeVector("foo").retainAll(null); } + + @Test(expected = NodeVector.ReadOnlyException.class) + public void require_that_set_throws_exception() { new TestNodeVector("foo").set(0, null); } + + @Test + public void require_that_contains_works() { + String val = "foo"; + TestNodeVector v = new TestNodeVector(val); + v.setSize(1); + assertTrue(v.contains(val)); + assertFalse(v.contains("bar")); + assertTrue(v.containsAll(Arrays.asList(val))); + assertFalse(v.containsAll(Arrays.asList(val, "bar"))); + } + + @Test + public void require_that_indexOf_works() { + String val = "foo"; + TestNodeVector v = new TestNodeVector(val); + assertTrue(v.isEmpty()); + v.setSize(1); + assertFalse(v.isEmpty()); + assertThat(v.indexOf(val), is(0)); + assertThat(v.indexOf("bar"), is(-1)); + assertThat(v.lastIndexOf(val), is(0)); + assertThat(v.lastIndexOf("bar"), is(-1)); + } + + @Test + public void require_that_iterators_work() { + String val = "foo"; + TestNodeVector v = new TestNodeVector(val); + v.setSize(3); + assertTrue(v.listIterator().hasNext()); + assertTrue(v.listIterator(0).hasNext()); + assertTrue(v.listIterator(1).hasNext()); + assertTrue(v.listIterator(2).hasNext()); + assertFalse(v.listIterator(3).hasNext()); + } + + @Test + public void require_that_sublisting_works() { + String val = "foo"; + TestNodeVector v = new TestNodeVector(val); + v.setSize(3); + assertThat(v.subList(0, 1).size(), is(1)); + assertThat(v.subList(0, 2).size(), is(2)); + assertThat(v.subList(0, 3).size(), is(3)); + String[] vals = v.toArray(new String[0]); + assertThat(vals.length, is(3)); + } + + private static class TestNodeVector extends NodeVector<String> { + private final String value; + public TestNodeVector(String value) { + this.value = value; + } + + @Override + protected String createNew() { + return value; + } + } +} diff --git a/config-lib/src/test/java/com/yahoo/config/PathNodeTest.java b/config-lib/src/test/java/com/yahoo/config/PathNodeTest.java new file mode 100644 index 00000000000..d587834d3b2 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/PathNodeTest.java @@ -0,0 +1,26 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import java.io.File; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author gjoranv + * @since 5.1.28 + */ +public class PathNodeTest { + + @Test + public void testSetValue() { + PathNode n = new PathNode(); + assertThat(n.toString(), is("(null)")); + + n = new PathNode(new FileReference("foo.txt")); + assertThat(n.value(), is(new File("foo.txt").toPath())); + } + +} diff --git a/config-lib/src/test/java/com/yahoo/config/StringNodeTest.java b/config-lib/src/test/java/com/yahoo/config/StringNodeTest.java new file mode 100644 index 00000000000..c0169a06559 --- /dev/null +++ b/config-lib/src/test/java/com/yahoo/config/StringNodeTest.java @@ -0,0 +1,46 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config; + +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.7 + */ +public class StringNodeTest { + + @Test + public void testUnescapeQuotedString() { + String a = "\"Hei\""; + assertThat(StringNode.unescapeQuotedString(a), is("Hei")); + assertThat(StringNode.unescapeQuotedString("foo\"bar\""), is("foo\"bar\"")); + assertThat(StringNode.unescapeQuotedString("foo\\\"bar\\\""), is("foo\"bar\"")); + assertThat(StringNode.unescapeQuotedString("a\\rb\\tc\\fd"), is("a\rb\tc\fd")); + assertThat(StringNode.unescapeQuotedString("\\x55"), is("U")); + } + + @Test(expected = IllegalArgumentException.class) + public void testUnescapedQuotedStringExceptions() { + StringNode.unescapeQuotedString("foo\\"); + } + + @Test + public void testToString() { + StringNode n = new StringNode(); + assertThat(n.toString(), is("(null)")); + n.setValue("foo"); + assertThat(n.toString(), is("\"foo\"")); + } + + @Test + public void testSetValue() { + StringNode n = new StringNode(); + n.setValue("\"foo\""); + assertThat(n.getValue(), is("foo")); + n.setValue("foo"); + assertThat(n.getValue(), is("foo")); + } +} diff --git a/config-lib/src/test/resources/configdefinitions/app.def b/config-lib/src/test/resources/configdefinitions/app.def new file mode 100644 index 00000000000..40db5310927 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/app.def @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +message string default="Hello!" + +times int default=1 + +a[].name string diff --git a/config-lib/src/test/resources/configdefinitions/arraytypes.def b/config-lib/src/test/resources/configdefinitions/arraytypes.def new file mode 100644 index 00000000000..3529b906c4a --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/arraytypes.def @@ -0,0 +1,11 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only simple array types that can be used for testing +# individual types in detail. +namespace=test + +boolarr[] bool +doublearr[] double +enumarr[] enum { VAL1, VAL2 } +intarr[] int +longarr[] long +stringarr[] string diff --git a/config-lib/src/test/resources/configdefinitions/chains-test.def b/config-lib/src/test/resources/configdefinitions/chains-test.def new file mode 100644 index 00000000000..5cc593b1443 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/chains-test.def @@ -0,0 +1,42 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Chains configuration +namespace=test + +component[].id string + +# Configured functionality provided by this - comes in addition to those set in the code +component[].dependencies.provides[] string + +# Configured "before" dependencies provided by this - comes in addition to those set in the code +component[].dependencies.before[] string + +# Configured "after" dependencies provided by this - comes in addition to those set in the code +component[].dependencies.after[] string + +# The id of this chain. The id has the form name(:version)? +# where the version has the form 1(.2(.3(.identifier)?)?)?. +# The default chain must be called "default". +chain[].id string + +#The type of this chain +chain[].type enum {DOCPROC, SEARCH} default=SEARCH + +# The id of a component to include in this chain. +# The id has the form fullclassname(:version)? +# where the version has the form 1(.2(.3(.identifier)?)?)?. +chain[].component[] string + +# The optional list of chain ids this inherits. +# The ids has the form name(:version)? +# where the version has the form 1(.2(.3(.identifier)?)?)?. +# If the version is not specified the newest version is used. +chain[].inherit[] string + +# The optional list of component ids to exclude from this chain even if they exists in inherited chains +# If versions are specified in these ids, they are ignored. +chain[].exclude[] string + +# The phases for a chain +chain[].phase[].id string +chain[].phase[].before[] string +chain[].phase[].after[] string diff --git a/config-lib/src/test/resources/configdefinitions/datastructures.def b/config-lib/src/test/resources/configdefinitions/datastructures.def new file mode 100644 index 00000000000..648fe569020 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/datastructures.def @@ -0,0 +1,11 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +date[] string + +stock[].ticker string +stock[].type enum { COMMON, ETF, ETC } default=COMMON +stock[].volume[] int + +basicstruct.foo string default="foo" +basicstruct.bar int default=0 diff --git a/config-lib/src/test/resources/configdefinitions/defaulttest.def b/config-lib/src/test/resources/configdefinitions/defaulttest.def new file mode 100644 index 00000000000..eec012ffbb9 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/defaulttest.def @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +nondefaultstring string +defaultstring string default="thedefault" + +nondefaultreference reference +defaultreference reference default="thedefault" diff --git a/config-lib/src/test/resources/configdefinitions/function-test.def b/config-lib/src/test/resources/configdefinitions/function-test.def new file mode 100644 index 00000000000..935efe9ca21 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/function-test.def @@ -0,0 +1,93 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# +# This def file should test most aspects of def files that makes a difference +# for the autogenerated config classes. The goal is to trigger all blocks of +# code in the code generators. This includes: +# +# - Use all legal special characters in the def file name, to ensure that those +# that needs to be replaced in type names are actually replaced. +# - Use the same enum type twice to verify that we dont declare or define it +# twice. +# - Use the same struct type twice for the same reason. +# - Include arrays of primitives and structs. +# - Include enum primitives and array of enums. Arrays of enums must be handled +# specially by the C++ code. +# - Include enums both with and without default values. +# - Include primitive string, numbers & doubles both with and without default +# values. +# - Have an array within a struct, to verify that we correctly recurse. +# - Reuse type name further within to ensure that this works. + +version=8 # deprecated, remove in Vespa 7 +namespace=test + +# Some random bool without a default value. These comments exist to check + # that comment parsing works. +bool_val bool restart + ## A bool with a default value set. +bool_with_def bool default=false restart +# An int value +# Also test that multiline comments +# work. +int_val int restart +int_with_def int default=-545 restart +long_val long restart +long_with_def long default=-50000000000 restart +double_val double restart +double_with_def double default=-6.43 restart +# Another comment +string_val string restart +stringwithdef string default="foobar" restart +enum_val enum { FOO, BAR, FOOBAR } restart +enumwithdef enum { FOO2, BAR2, FOOBAR2 } default=BAR2 restart +onechoice enum { ONLYFOO } default=ONLYFOO restart +refval reference restart +refwithdef reference default=":parent:" restart +fileVal file restart +pathVal path restart + +boolarr[] bool restart +intarr[] int restart +longarr[] long restart +doublearr[] double restart +stringarr[] string restart +enumarr[] enum { ARRAY, VALUES } restart +refarr[] reference restart +fileArr[] file restart +pathArr[] path restart + +#This is a map of ints. +intMap{} int restart +stringMap{} string restart +filemap{} file restart +pathMap{} path restart + +# A basic struct +basicStruct.foo string default="basic" restart +basicStruct.bar int restart +basicStruct.intArr[] int restart + +# A struct of struct +rootStruct.inner0.name string default="inner0" restart +rootStruct.inner0.index int restart +rootStruct.inner1.name string default="inner1" restart +rootStruct.inner1.index int restart +rootStruct.innerArr[].boolVal bool default=false restart +rootStruct.innerArr[].stringVal string restart + +# This is my array +myarray[].intval int default=14 restart +myarray[].stringval[] string restart +myarray[].enumval enum { INNER, ENUM, TYPE } default=TYPE restart +myarray[].refval reference # Value in array without default restart +myarray[].fileVal file restart +myarray[].anotherarray[].foo int default=-4 restart +myarray[].myStruct.a int restart +myarray[].myStruct.b int default=2 restart + +myStructMap{}.myInt int restart +myStructMap{}.myString string restart +myStructMap{}.myIntDef int default=56 restart +myStructMap{}.myStringDef string default="g" restart +myStructMap{}.anotherMap{}.anInt int restart +myStructMap{}.anotherMap{}.anIntDef int default=11 restart diff --git a/config-lib/src/test/resources/configdefinitions/int.def b/config-lib/src/test/resources/configdefinitions/int.def new file mode 100755 index 00000000000..0bf82ed9987 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/int.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +intVal int default=1 diff --git a/config-lib/src/test/resources/configdefinitions/maptypes.def b/config-lib/src/test/resources/configdefinitions/maptypes.def new file mode 100644 index 00000000000..389a9b71012 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/maptypes.def @@ -0,0 +1,13 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only structs in various forms +namespace=foo + +boolmap{} bool +intmap{} int +longmap{} long +doublemap{} double +stringmap{} string +filemap{} file + +innermap{}.foo int +nestedmap{}.inner{} int diff --git a/config-lib/src/test/resources/configdefinitions/md5test.def b/config-lib/src/test/resources/configdefinitions/md5test.def new file mode 100644 index 00000000000..86a199ea785 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/md5test.def @@ -0,0 +1,24 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# version=4 , version in comment does not count. + +# Added empty line to see if we can confuse +# the server's md5 calculation +namespace=test + +#even adding a variable name starting with 'version' +versiontag int default=3 + +blabla string default="" +tabs string default=" " +test int + +# test multiple spaces/tabs +spaces int +singletab string +multitabs double + +# test enum +normal enum { VAL1, VAL2 } default=VAL1 +spacevalues enum { V1 , V2 , V3 , V4 } default=V3 + +# Comments and empty lines at the end diff --git a/config-lib/src/test/resources/configdefinitions/namespace.def b/config-lib/src/test/resources/configdefinitions/namespace.def new file mode 100644 index 00000000000..e51a06e87b8 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/namespace.def @@ -0,0 +1,6 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +namespace=myproject.config + +a int diff --git a/config-lib/src/test/resources/configdefinitions/restart.def b/config-lib/src/test/resources/configdefinitions/restart.def new file mode 100755 index 00000000000..417a015a3d0 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/restart.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +intVal int default=1 restart diff --git a/config-lib/src/test/resources/configdefinitions/simpletypes.def b/config-lib/src/test/resources/configdefinitions/simpletypes.def new file mode 100644 index 00000000000..314c67ae709 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/simpletypes.def @@ -0,0 +1,11 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only simple leaf types with default values, that can be used +# for testing individual types in detail. +namespace=test + +boolval bool default=false +doubleval double default=0.0 +enumval enum { VAL1, VAL2 } default=VAL1 +intval int default=0 +longval long default=0 +stringval string default="s" diff --git a/config-lib/src/test/resources/configdefinitions/specialtypes.def b/config-lib/src/test/resources/configdefinitions/specialtypes.def new file mode 100644 index 00000000000..3243288f0af --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/specialtypes.def @@ -0,0 +1,5 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +myfile file +myref reference diff --git a/config-lib/src/test/resources/configdefinitions/standard.def b/config-lib/src/test/resources/configdefinitions/standard.def new file mode 100644 index 00000000000..e065535f97d --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/standard.def @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only simple leaf types with default values, that can be used +# for testing individual types in detail. +namespace=test + +basicStruct.intVal int default=0 +basicStruct.stringVal string default="s" +stringArr[] string diff --git a/config-lib/src/test/resources/configdefinitions/string.def b/config-lib/src/test/resources/configdefinitions/string.def new file mode 100755 index 00000000000..80ac3f4b1e6 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/string.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +stringVal string default="_default_" diff --git a/config-lib/src/test/resources/configdefinitions/structtypes.def b/config-lib/src/test/resources/configdefinitions/structtypes.def new file mode 100644 index 00000000000..0a3c8e23a45 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/structtypes.def @@ -0,0 +1,21 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only structs in various forms +namespace=test + +simple.name string default="_default_" +simple.gender enum { MALE, FEMALE } default=MALE +simple.emails[] string + +nested.inner.name string default="_default_" +nested.inner.gender enum { MALE, FEMALE } default=MALE +nested.inner.emails[] string + +simplearr[].name string +simplearr[].gender enum { MALE, FEMALE } + +nestedarr[].inner.name string +nestedarr[].inner.gender enum { MALE, FEMALE } +nestedarr[].inner.emails[] string + +complexarr[].innerarr[].name string +complexarr[].innerarr[].gender enum { MALE, FEMALE } diff --git a/config-lib/src/test/resources/configdefinitions/test-nodefs.def b/config-lib/src/test/resources/configdefinitions/test-nodefs.def new file mode 100644 index 00000000000..4a80231d709 --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/test-nodefs.def @@ -0,0 +1,16 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +# test config vars with no defaults + +s string +j int +b bool +f double +e enum { AA, BB, CC } + +basicstruct.foo string +basicstruct.bar int + +arr[].s string +arr[].i int diff --git a/config-lib/src/test/resources/configdefinitions/test-nonstring.def b/config-lib/src/test/resources/configdefinitions/test-nonstring.def new file mode 100644 index 00000000000..3e54a3bb8bd --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/test-nonstring.def @@ -0,0 +1,9 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +# Test non-string config vars with defaults + +i int default=0 +b bool default=false +d double default=0.0 +e enum { AA, BB, CC } default=AA diff --git a/config-lib/src/test/resources/configdefinitions/test-reference.def b/config-lib/src/test/resources/configdefinitions/test-reference.def new file mode 100644 index 00000000000..96c5f62030a --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/test-reference.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +configId reference default=":parent:" diff --git a/config-lib/src/test/resources/configdefinitions/testnamespace.def b/config-lib/src/test/resources/configdefinitions/testnamespace.def new file mode 100644 index 00000000000..b77eb5d81da --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/testnamespace.def @@ -0,0 +1,3 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +basicStruct.stringVal string diff --git a/config-lib/src/test/resources/configdefinitions/unicode.def b/config-lib/src/test/resources/configdefinitions/unicode.def new file mode 100644 index 00000000000..52b2353e60e --- /dev/null +++ b/config-lib/src/test/resources/configdefinitions/unicode.def @@ -0,0 +1,5 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=test + +unicodestring1 string +unicodestring2 string default="abc æøå 囲碁 ÆØÅ ABC" |