diff options
author | Jon Bratseth <bratseth@gmail.com> | 2022-08-31 22:50:14 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@gmail.com> | 2022-08-31 22:50:14 +0200 |
commit | adcb1d4d55e71d78c662f798b033d3abea0d4b9e (patch) | |
tree | 5867c3ac85792c1578d6ce463e8e24dd2aea7fb0 /config/src | |
parent | 2b83da619a3ee2f38a1a3b05576f44d7451b3daf (diff) |
Add 'model' config type
Diffstat (limited to 'config/src')
5 files changed, 120 insertions, 194 deletions
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java index 0d9feb42996..9d9e43de130 100644 --- a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java +++ b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinition.java @@ -701,6 +701,10 @@ public class ConfigDefinition { public Map<String, PathDef> getPathDefs() { return pathDefs; } + public Map<String, UrlDef> getUrlDefs() { return urlDefs; } + + public Map<String, ModelDef> getModelDefs() { return modelDefs; } + public Map<String, InnerArrayDef> getInnerArrayDefs() { return innerArrayDefs; } diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java index 8e1135b5684..da5048a99e8 100644 --- a/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java +++ b/config/src/main/java/com/yahoo/vespa/config/ConfigDefinitionBuilder.java @@ -69,41 +69,37 @@ public class ConfigDefinitionBuilder { addNode(def, (LeafCNode.PathLeaf) node); } else if (node instanceof LeafCNode.UrlLeaf) { addNode(def, (LeafCNode.UrlLeaf) node); + } else if (node instanceof LeafCNode.ModelLeaf) { + addNode(def, (LeafCNode.ModelLeaf) node); } else if (node instanceof LeafCNode.StringLeaf) { addNode(def, (LeafCNode.StringLeaf) node); } else if (node instanceof LeafCNode.EnumLeaf) { addNode(def, (LeafCNode.EnumLeaf) node); } else { - System.err.println("Unknown node type for node with name " + name); + System.err.println("Unknown node type for node with name '" + name + "'"); } } } else { ConfigDefinition newDef; if (node.isArray) { if (node.getChildren() != null && node.getChildren().length > 0) { - //System.out.println("\tAdding inner array node " + name); newDef = def.innerArrayDef(name); for (CNode childNode : node.getChildren()) { - //System.out.println("\tChild node " + childNode.getName()); addNode(newDef, childNode); } } } else if (node.isMap) { - //System.out.println("Adding struct map node " + name); newDef = def.structMapDef(name); if (node.getChildren() != null && node.getChildren().length > 0) { for (CNode childNode : node.getChildren()) { - //System.out.println("\tChild node " + childNode.getName()); addNode(newDef, childNode); } } } else { - //System.out.println("Adding struct node " + name); newDef = def.structDef(name); if (node.getChildren() != null && node.getChildren().length > 0) { for (CNode childNode : node.getChildren()) { - //System.out.println("\tChild node " + childNode.getName()); addNode(newDef, childNode); } } @@ -183,15 +179,11 @@ public class ConfigDefinitionBuilder { def.addUrlDef(leaf.getName(), null); } } -/* + private static void addNode(ConfigDefinition def, LeafCNode.ModelLeaf leaf) { - if (leaf.getDefaultValue() != null) { - def.addUrlDef(leaf.getName(), leaf.getDefaultValue().getValue()); - } else { - def.addUrlDef(leaf.getName(), null); - } + def.addModelDef(leaf.getName()); } -*/ + private static void addNode(ConfigDefinition def, LeafCNode.EnumLeaf leaf) { if (leaf.getDefaultValue() != null) { def.addEnumDef(leaf.getName(), Arrays.asList(leaf.getLegalValues()), leaf.getDefaultValue().getValue()); @@ -210,4 +202,5 @@ public class ConfigDefinitionBuilder { sb.delete(length - 2, length); return sb.toString(); } + } diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java index 5c0e9539f4f..e68e996fc97 100644 --- a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java +++ b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java @@ -4,12 +4,12 @@ package com.yahoo.vespa.config; import com.yahoo.config.ConfigBuilder; import com.yahoo.config.ConfigInstance; import com.yahoo.config.FileReference; +import com.yahoo.config.ModelReference; import com.yahoo.config.UrlReference; import com.yahoo.slime.ArrayTraverser; import com.yahoo.slime.Inspector; import com.yahoo.slime.ObjectTraverser; import com.yahoo.slime.Type; -import com.yahoo.text.Utf8; import com.yahoo.yolean.Exceptions; import java.io.File; @@ -22,12 +22,11 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.Stack; import java.util.logging.Logger; -import static java.util.logging.Level.FINE; -import static java.util.logging.Level.FINEST; import static java.util.logging.Level.INFO; /** @@ -35,7 +34,9 @@ import static java.util.logging.Level.INFO; * * TODO: This can be refactored a lot, since many of the reflection methods are duplicated * - * @author Ulf Lilleengen, hmusum, Tony Vaagenes + * @author Ulf Lilleengen + * @author hmusum + * @author Tony Vaagenes */ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { @@ -54,7 +55,6 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { this.rootBuilder = builder; this.pathAcquirer = pathAcquirer; this.urlDownloader = urlDownloader; - debug("rootBuilder=" + rootBuilder); } public void applyPayload(ConfigPayload payload) { @@ -63,7 +63,7 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { handleValue(payload.getSlime().get()); } catch (Exception e) { throw new RuntimeException("Not able to create config builder for payload:" + payload.toString() + - ", " + Exceptions.toMessageString(e), e); + ", " + Exceptions.toMessageString(e), e); } } @@ -89,27 +89,19 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { } private void handleARRAY(Inspector inspector) { - trace("Array"); - inspector.traverse(new ArrayTraverser() { - @Override - public void entry(int idx, Inspector inspector) { - handleArrayEntry(idx, inspector); - } - }); + inspector.traverse((ArrayTraverser)(int index, Inspector value) -> handleArrayEntry(index, value)); } private void handleArrayEntry(int idx, Inspector inspector) { try { - trace("entry, idx=" + idx); - trace("top of stack=" + stack.peek().toString()); String name = stack.peek().nameStack().peek(); - if (inspector.type().equals(Type.OBJECT)) { + if (inspector.type() == Type.OBJECT) { NamedBuilder builder = createBuilder(stack.peek(), name); if (builder == null) return; // Ignore non-existent struct array class stack.push(builder); } handleValue(inspector); - if (inspector.type().equals(Type.OBJECT)) { + if (inspector.type() == Type.OBJECT) { stack.peek().nameStack().pop(); } } catch (Exception e) { @@ -118,37 +110,24 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { } private void handleOBJECT(Inspector inspector) { - trace("Object"); - printStack(); - - inspector.traverse(new ObjectTraverser() { - @Override - public void field(String name, Inspector inspector) { - handleObjectEntry(name, inspector); - } - }); - - trace("Should pop a builder from stack"); + inspector.traverse((String name, Inspector value) -> handleObjectEntry(name, value)); NamedBuilder builder = stack.pop(); - printStack(); // Need to set e.g struct(Struct.Builder) here - if (!stack.empty()) { - trace("builder= " + builder); + if ( ! stack.empty()) { try { invokeSetter(stack.peek().builder, builder.peekName(), builder.builder); } catch (Exception e) { throw new RuntimeException("Could not set '" + builder.peekName() + - "' for value '" + builder.builder() + "'", e); + "' for value '" + builder.builder() + "'", e); } } } private void handleObjectEntry(String name, Inspector inspector) { try { - trace("field, name=" + name); NamedBuilder parentBuilder = stack.peek(); - if (inspector.type().equals(Type.OBJECT)) { + if (inspector.type() == Type.OBJECT) { if (isMapField(parentBuilder, name)) { parentBuilder.nameStack().push(name); handleMap(inspector); @@ -159,9 +138,8 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { if (builder == null) return; // Ignore non-existent struct class stack.push(builder); } - } else if (inspector.type().equals(Type.ARRAY)) { + } else if (inspector.type() == Type.ARRAY) { for (int i = 0; i < inspector.children(); i++) { - trace("Pushing " + name); parentBuilder.nameStack().push(name); } } else { // leaf @@ -174,19 +152,11 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { } private void handleMap(Inspector inspector) { - inspector.traverse(new ObjectTraverser() { - @Override - public void field(String name, Inspector inspector) { - switch (inspector.type()) { - case OBJECT: - handleInnerMap(name, inspector); - break; - case ARRAY: - throw new IllegalArgumentException("Never herd of array inside maps before"); - default: - setMapLeafValue(name, getValueFromInspector(inspector)); - break; - } + inspector.traverse((String name, Inspector value) -> { + switch (value.type()) { + case OBJECT -> handleInnerMap(name, value); + case ARRAY -> throw new IllegalArgumentException("Never heard of array inside maps before"); + default -> setMapLeafValue(name, value); } }); } @@ -197,12 +167,7 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { throw new RuntimeException("Missing map builder (this should never happen): " + stack.peek()); setMapLeafValue(name, builder.builder()); stack.push(builder); - inspector.traverse(new ObjectTraverser() { - @Override - public void field(String name, Inspector inspector) { - handleObjectEntry(name, inspector); - } - }); + inspector.traverse((ObjectTraverser) (key, value) -> handleObjectEntry(key, value)); stack.pop(); } @@ -210,21 +175,8 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { NamedBuilder parent = stack.peek(); ConfigBuilder builder = parent.builder(); String methodName = parent.peekName(); - //trace("class to obtain method from: " + builder.getClass().getName()); try { - // Need to convert reference into actual path if 'path' type is used - if (isPathField(builder, methodName)) { - FileReference wrappedPath = resolvePath((String)value); - invokeSetter(builder, methodName, key, wrappedPath); - - // Need to convert url into actual file if 'url' type is used - } else if (isUrlField(builder, methodName)) { - UrlReference url = resolveUrl((String)value); - invokeSetter(builder, methodName, key, url); - - } else { - invokeSetter(builder, methodName, key, value); - } + invokeSetter(builder, methodName, key, resolveValue(builder, methodName, value)); } catch (InvocationTargetException | IllegalAccessException e) { throw new RuntimeException("Name: " + methodName + ", value '" + value + "'", e); } catch (NoSuchMethodException e) { @@ -246,46 +198,20 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { Object builder = parentBuilder.builder(); Object newBuilder = getBuilderForStruct(name, builder.getClass().getDeclaringClass()); if (newBuilder == null) return null; - trace("New builder for " + name + "=" + newBuilder); - trace("Pushing builder for " + name + "=" + newBuilder + " onto stack"); return new NamedBuilder((ConfigBuilder) newBuilder, name); } private void handleLeafValue(Inspector value) { - trace("String "); - printStack(); NamedBuilder peek = stack.peek(); - trace("popping name stack"); String name = peek.nameStack().pop(); - printStack(); ConfigBuilder builder = peek.builder(); - trace("name=" + name + ",builder=" + builder + ",value=" + value.toString()); setValueForLeafNode(builder, name, value); } // Sets values for leaf nodes (uses private accessors that take string as argument) private void setValueForLeafNode(Object builder, String methodName, Inspector value) { try { - // Need to convert reference into actual path if 'path' type is used - if (isPathField(builder, methodName)) { - FileReference wrappedPath = resolvePath(Utf8.toString(value.asUtf8())); - invokeSetter(builder, methodName, wrappedPath); - - // Need to convert url into actual file if 'url' type is used - } else if (isUrlField(builder, methodName)) { - String url = Utf8.toString(value.asUtf8()); - if (url == null || url.length() == 0) { - invokeSetter(builder, methodName, ""); - } else { - UrlReference urlref = resolveUrl(Utf8.toString(value.asUtf8())); - invokeSetter(builder, methodName, urlref); - } - - - } else { - Object object = getValueFromInspector(value); - invokeSetter(builder, methodName, object); - } + invokeSetter(builder, methodName, resolveValue(builder, methodName, value)); } catch (InvocationTargetException | IllegalAccessException e) { throw new RuntimeException("Name: " + methodName + ", value '" + value + "'", e); } catch (NoSuchMethodException e) { @@ -293,27 +219,44 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { } } + private Object resolveValue(Object builder, String methodName, Object rawValue) { + if (rawValue instanceof ConfigBuilder) // Value in a map + return rawValue; + Inspector value = (Inspector)rawValue; + if (isPathField(builder, methodName)) + return resolvePath(value.asString()); + else if (isUrlField(builder, methodName)) + return value.asString().isEmpty() ? "" : resolveUrl(value.asString()); + else if (isModelField(builder, methodName)) + return value.asString().isEmpty() ? "" : resolveModel(value.asString()); + else + return getValueFromInspector(value); + } + private FileReference resolvePath(String value) { - Path path = pathAcquirer.getPath(newFileReference(value)); - return newFileReference(path.toString()); + Path path = pathAcquirer.getPath(new FileReference(value)); + return new FileReference(path.toString()); } private UrlReference resolveUrl(String url) { - if (urlDownloader == null) { - return new UrlReference(url); // assuming config server - just return the actual url. - } + if (! canResolveUrls()) // assuming config server - keep the url + return new UrlReference(url); File file = urlDownloader.waitFor(new UrlReference(url), 60 * 60); return new UrlReference(file.getAbsolutePath()); } - private FileReference newFileReference(String fileReference) { - try { - Constructor<FileReference> constructor = FileReference.class.getDeclaredConstructor(String.class); - constructor.setAccessible(true); - return constructor.newInstance(fileReference); - } catch (Exception e) { - throw new RuntimeException("Failed invoking FileReference constructor.", e); - } + private boolean canResolveUrls() { + return urlDownloader != null && urlDownloader.isValid(); + } + + private ModelReference resolveModel(String modelStringValue) { + var model = ModelReference.valueOf(modelStringValue); + // Resolve any of url and path present, in priority order + if (model.url().isPresent() && canResolveUrls()) + model = model.withUrl(Optional.of(resolveUrl(model.url().get().value()))); + else if (model.path().isPresent()) + model = model.withPath(Optional.of(resolvePath(model.path().get().value()))); + return model; } private final Map<String, Method> methodCache = new HashMap<>(); @@ -335,7 +278,6 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { } Method method = builder.getClass().getDeclaredMethod(methodName, parameterTypes); method.setAccessible(true); - trace("method=" + method + ",params=" + params); return method; } @@ -353,7 +295,7 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { private Object getValueFromInspector(Inspector inspector) { switch (inspector.type()) { case STRING: - return Utf8.toString(inspector.asUtf8()); + return inspector.asString(); case LONG: return String.valueOf(inspector.asLong()); case DOUBLE: @@ -385,6 +327,12 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { return isFieldType(urlFieldSet, builder, methodName, UrlReference.class); } + private final Set<String> modelFieldSet = new HashSet<>(); + private boolean isModelField(Object builder, String methodName) { + // Models are stored as ModelReference in Builder. + return isFieldType(modelFieldSet, builder, methodName, ModelReference.class); + } + private boolean isFieldType(Set<String> fieldSet, Object builder, String methodName, java.lang.reflect.Type type) { String key = fieldKey(builder, methodName); if (fieldSet.contains(key)) { @@ -419,14 +367,11 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { } private String capitalize(String name) { - StringBuilder sb = new StringBuilder(); - sb.append(name.substring(0, 1).toUpperCase()).append(name.substring(1)); - return sb.toString(); + return name.substring(0, 1).toUpperCase() + name.substring(1); } private Constructor<?> lookupBuilderForStruct(String structName, String name, Class<?> currentClass) { - final String currentClassName = currentClass.getName(); - trace("structName=" + structName + ", name=" + name + ",current class=" + currentClassName); + String currentClassName = currentClass.getName(); Class<?> structClass = getInnerClass(currentClass, currentClassName + "$" + structName); if (structClass == null) { log.info("Could not find nested class '" + currentClassName + "$" + structName + @@ -449,17 +394,16 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { } /** - * Finds a nested class with the given <code>name</code>name in <code>clazz</code> + * Finds a nested class with the given <code>name</code>name in <code>clazz</code>. + * * @param clazz a Class * @param name a name * @return class found, or null if no class is found */ private Class<?> getInnerClass(Class<?> clazz, String name) { for (Class<?> cls : clazz.getDeclaredClasses()) { - if (cls.getName().equals(name)) { - trace("Found class " + cls.getName()); + if (cls.getName().equals(name)) return cls; - } } return null; } @@ -472,37 +416,24 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { private Object getBuilderForStruct(String name, Class<?> currentClass) { String structName = capitalize(name); String key = constructorCacheKey(structName, name, currentClass); - Constructor<?> ctor = constructorCache.get(key); - if (ctor == null) { - ctor = lookupBuilderForStruct(structName, name, currentClass); - if (ctor == null) return null; - constructorCache.put(key, ctor); + Constructor<?> constructor = constructorCache.get(key); + if (constructor == null) { + constructor = lookupBuilderForStruct(structName, name, currentClass); + if (constructor == null) return null; + constructorCache.put(key, constructor); } - Object builder; try { - builder = ctor.newInstance(); + return constructor.newInstance(); } catch (Exception e) { - throw new RuntimeException("Could not create class '" + "'" + ctor.getDeclaringClass().getName() + "'"); + throw new RuntimeException("Could not create class '" + "'" + constructor.getDeclaringClass().getName() + "'"); } - return builder; - } - - private void debug(String message) { - log.log(FINE, () -> message); - } - - private void trace(String message) { - log.log(FINEST, () -> message); - } - - private void printStack() { - trace("stack=" + stack.toString()); } /** * A class that holds a builder and a stack of names */ private static class NamedBuilder { + private final ConfigBuilder builder; private final Stack<String> names = new Stack<>(); // if empty, the builder is the root builder @@ -529,9 +460,7 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(builder() == null ? "null" : builder.toString()).append(" names=").append(names); - return sb.toString(); + return builder() == null ? "null" : builder.toString() + " names=" + names; } } diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadBuilder.java b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadBuilder.java index 48cdbe36745..74ee29f08ed 100644 --- a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadBuilder.java +++ b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadBuilder.java @@ -1,16 +1,21 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config; -import com.yahoo.slime.*; +import com.yahoo.slime.ArrayTraverser; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Inspector; +import com.yahoo.slime.ObjectTraverser; +import com.yahoo.slime.Slime; -import java.util.*; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; /** * Helper class for building Slime config payloads, while supporting referring to payloads with their indices. The * builder does not care about config field types. This is resolved by the actual config type consumer created * from the Slime tree. * - * TODO: Add toString * @author Ulf Lilleengen */ public class ConfigPayloadBuilder { @@ -33,9 +38,9 @@ public class ConfigPayloadBuilder { } /** - * Construct a payload builder with a leaf value + * Construct a payload builder with a leaf value. * - * @param value The value of this leaf. + * @param value the value of this leaf */ private ConfigPayloadBuilder(String value) { this(null, value); @@ -172,11 +177,9 @@ public class ConfigPayloadBuilder { /** * Get the value of this field, if any. * - * @return value of field, null if this is not a leaf. + * @return value of field, null if this is not a leaf */ - public String getValue() { - return value; - } + public String getValue() { return value; } public void setValue(String value) { this.value = value; @@ -209,6 +212,7 @@ public class ConfigPayloadBuilder { } public class MapBuilder { + private final Map<String, ConfigPayloadBuilder> elements = new LinkedHashMap<>(); private final ConfigDefinition configDefinition; private final String name; @@ -268,6 +272,11 @@ public class ConfigPayloadBuilder { INDEX, APPEND } + @Override + public String toString() { + return "config builder of " + getConfigDefinition(); + } + /** * Representation of a config array, which supports both INDEX and APPEND modes. */ @@ -450,26 +459,12 @@ public class ConfigPayloadBuilder { private static void decode(Slime slime, String name, Inspector inspector, ConfigPayloadBuilder builder) { switch (inspector.type()) { - case STRING: - builder.setField(name, inspector.asString()); - break; - case LONG: - builder.setField(name, String.valueOf(inspector.asLong())); - break; - case DOUBLE: - builder.setField(name, String.valueOf(inspector.asDouble())); - break; - case BOOL: - builder.setField(name, String.valueOf(inspector.asBool())); - break; - case OBJECT: - ConfigPayloadBuilder objectBuilder = builder.getObject(name); - decodeObject(slime, objectBuilder, inspector); - break; - case ARRAY: - ConfigPayloadBuilder.Array array = builder.getArray(name); - decodeArray(slime, array, inspector); - break; + case STRING -> builder.setField(name, inspector.asString()); + case LONG -> builder.setField(name, String.valueOf(inspector.asLong())); + case DOUBLE -> builder.setField(name, String.valueOf(inspector.asDouble())); + case BOOL -> builder.setField(name, String.valueOf(inspector.asBool())); + case OBJECT -> decodeObject(slime, builder.getObject(name), inspector); + case ARRAY -> decodeArray(slime, builder.getArray(name), inspector); } } @@ -479,8 +474,10 @@ public class ConfigPayloadBuilder { } private static class BuilderObjectTraverser implements ObjectTraverser { + private final ConfigPayloadBuilder builder; private final Slime slime; + public BuilderObjectTraverser(Slime slime, ConfigPayloadBuilder builder) { this.slime = slime; this.builder = builder; @@ -490,11 +487,14 @@ public class ConfigPayloadBuilder { public void field(String name, Inspector inspector) { decode(slime, name, inspector, builder); } + } private static class BuilderArrayTraverser implements ArrayTraverser { + private final Array array; private final Slime slime; + public BuilderArrayTraverser(Slime slime, Array array) { this.array = array; this.slime = slime; @@ -503,14 +503,13 @@ public class ConfigPayloadBuilder { @Override public void entry(int idx, Inspector inspector) { switch (inspector.type()) { - case STRING: - array.append(inspector.asString()); - break; - case OBJECT: - decodeObject(slime, array.append(), inspector); - break; + case STRING -> array.append(inspector.asString()); + case OBJECT -> decodeObject(slime, array.append(), inspector); } } + } + } + } diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java index 873df75ce23..5104475d836 100644 --- a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java @@ -15,7 +15,8 @@ import static org.junit.Assert.fail; * @author gjoranv */ public class ConfigInstanceTest { - private ConfigSourceSet sourceSet = new ConfigSourceSet("config-instance-test"); + + private final ConfigSourceSet sourceSet = new ConfigSourceSet("config-instance-test"); /** * Verifies that the subscriber's configure() method is only |