diff options
3 files changed, 119 insertions, 95 deletions
diff --git a/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java b/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java index b47bd17a5ba..43fb52dcd72 100644 --- a/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java +++ b/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java @@ -20,8 +20,6 @@ import com.yahoo.config.codegen.LeafCNode; */ public abstract class ConfigInstance extends InnerNode { - private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ConfigInstance.class.getName()); - public interface Builder extends ConfigBuilder { /** @@ -41,98 +39,6 @@ public abstract class ConfigInstance extends InnerNode { default void setApplyOnRestart(boolean applyOnRestart) { throw new java.lang.UnsupportedOperationException(); } - default ConfigInstance buildInstance(InnerCNode targetDef) { - try { - if (targetDef != null) applyDef(this, targetDef); - Class<? extends ConfigInstance> clazz = getConfigClass(getClass()); - return clazz.getConstructor(getClass()).newInstance(this); - } catch (Exception e) { - throw new ConfigurationRuntimeException(e); - } - } - - /** - * If some fields on the builder are null now, set them from the def. Do recursively. - * <p> - * If the targetDef has some schema incompatibilities, they are not handled here - * (except logging in some cases), but in ConfigInstance.serialize(). - * - * @param builder a {@link com.yahoo.config.ConfigBuilder} - * @param targetDef a config definition - * @throws Exception if applying values form config definitions fails - */ - static void applyDef(ConfigBuilder builder, InnerCNode targetDef) throws Exception { - for (Map.Entry<String, CNode> e: targetDef.children().entrySet()) { - CNode node = e.getValue(); - if (node instanceof LeafCNode) { - setLeafValueIfUnset(targetDef, builder, (LeafCNode)node); - } else if (node instanceof InnerCNode) { - // Is there a private field on the builder that matches this inner node in the def? - if (hasField(builder.getClass(), node.getName())) { - Field innerField = builder.getClass().getDeclaredField(node.getName()); - innerField.setAccessible(true); - Object innerFieldVal = innerField.get(builder); - if (innerFieldVal instanceof List) { - // inner array? Check that list elems are ConfigBuilder - List<?> innerList = (List<?>) innerFieldVal; - for (Object b : innerList) { - if (b instanceof ConfigBuilder) { - applyDef((ConfigBuilder) b, (InnerCNode) node); - } - } - } else if (innerFieldVal instanceof ConfigBuilder) { - // Struct perhaps - applyDef((ConfigBuilder) innerFieldVal, (InnerCNode) node); - } else { - // Likely a config value mismatch. That is handled in ConfigInstance.serialize() (error message, omit from response.) - } - } - } - } - } - - private static boolean hasField(Class<?> aClass, String name) { - for (Field field : aClass.getDeclaredFields()) { - if (name.equals(field.getName())) { - return true; - } - } - return false; - } - - private static void setLeafValueIfUnset(InnerCNode targetDef, Object builder, LeafCNode node) throws Exception { - if (hasField(builder.getClass(), node.getName())) { - Field field = builder.getClass().getDeclaredField(node.getName()); - field.setAccessible(true); - Object val = field.get(builder); - if (val==null) { - // Not set on builder, if the leaf node has a default value, try the private setter that takes String - try { - if (node.getDefaultValue()!=null) { - Method setter = builder.getClass().getDeclaredMethod(node.getName(), String.class); - setter.setAccessible(true); - setter.invoke(builder, node.getDefaultValue().getValue()); - } - } catch (Exception e) { - log.log(Level.SEVERE, - "For config '" + targetDef.getFullName() + "': " + - "Unable to apply the default value for field '" + node.getName() + - "' to config Builder (where it wasn't set)", - e); - } - } - } - } - - @SuppressWarnings("unchecked") - private static Class<? extends ConfigInstance> getConfigClass(Class<? extends ConfigInstance.Builder> builderClass) { - Class<?> configClass = builderClass.getEnclosingClass(); - if (configClass == null || ! ConfigInstance.class.isAssignableFrom(configClass)) { - throw new ConfigurationRuntimeException("Builder class " + builderClass + " has enclosing class " + configClass + ", which is not a ConfigInstance"); - } - return (Class<? extends ConfigInstance>) configClass; - } - } public interface Producer {} diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java index 4b61021bc38..c82431dc8cd 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java @@ -144,7 +144,7 @@ public class Application implements ModelResult { } else { try { - ConfigInstance instance = builder.buildInstance(def.getCNode()); + ConfigInstance instance = ConfigInstanceBuilder.buildInstance(builder, def.getCNode()); payload = ConfigPayload.fromInstance(instance); } catch (ConfigurationRuntimeException e) { // This can happen in cases where services ask for config that no longer exist before they have been able @@ -218,4 +218,5 @@ public class Application implements ModelResult { public Set<String> allConfigIds() { return model.allConfigIds(); } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigInstanceBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigInstanceBuilder.java new file mode 100644 index 00000000000..389e8394c9e --- /dev/null +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigInstanceBuilder.java @@ -0,0 +1,117 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.server.application; + +import com.yahoo.config.ConfigBuilder; +import com.yahoo.config.ConfigInstance; +import com.yahoo.config.ConfigurationRuntimeException; +import com.yahoo.config.codegen.CNode; +import com.yahoo.config.codegen.InnerCNode; +import com.yahoo.config.codegen.LeafCNode; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +/** + * Builds a ConfigInstance from a ConfigInstance.Builder. + * (Put here not in ConfigInstance.Builder temporarily to work around dependency problems.) + */ +class ConfigInstanceBuilder { + + private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ConfigInstanceBuilder.class.getName()); + + static ConfigInstance buildInstance(ConfigInstance.Builder builder, InnerCNode targetDef) { + try { + if (targetDef != null) applyDef(builder, targetDef); + Class<? extends ConfigInstance> clazz = getConfigClass(builder.getClass()); + return clazz.getConstructor(builder.getClass()).newInstance(builder); + } catch (Exception e) { + throw new ConfigurationRuntimeException(e); + } + } + + /** + * If some fields on the builder are null now, set them from the def. Do recursively. + * <p> + * If the targetDef has some schema incompatibilities, they are not handled here + * (except logging in some cases), but in ConfigInstance.serialize(). + * + * @param builder a {@link com.yahoo.config.ConfigBuilder} + * @param targetDef a config definition + * @throws Exception if applying values form config definitions fails + */ + static void applyDef(ConfigBuilder builder, InnerCNode targetDef) throws Exception { + for (Map.Entry<String, CNode> e: targetDef.children().entrySet()) { + CNode node = e.getValue(); + if (node instanceof LeafCNode) { + setLeafValueIfUnset(targetDef, builder, (LeafCNode)node); + } else if (node instanceof InnerCNode) { + // Is there a private field on the builder that matches this inner node in the def? + if (hasField(builder.getClass(), node.getName())) { + Field innerField = builder.getClass().getDeclaredField(node.getName()); + innerField.setAccessible(true); + Object innerFieldVal = innerField.get(builder); + if (innerFieldVal instanceof List) { + // inner array? Check that list elems are ConfigBuilder + List<?> innerList = (List<?>) innerFieldVal; + for (Object b : innerList) { + if (b instanceof ConfigBuilder) { + applyDef((ConfigBuilder) b, (InnerCNode) node); + } + } + } else if (innerFieldVal instanceof ConfigBuilder) { + // Struct perhaps + applyDef((ConfigBuilder) innerFieldVal, (InnerCNode) node); + } else { + // Likely a config value mismatch. That is handled in ConfigInstance.serialize() (error message, omit from response.) + } + } + } + } + } + + private static boolean hasField(Class<?> aClass, String name) { + for (Field field : aClass.getDeclaredFields()) { + if (name.equals(field.getName())) { + return true; + } + } + return false; + } + + private static void setLeafValueIfUnset(InnerCNode targetDef, Object builder, LeafCNode node) throws Exception { + if (hasField(builder.getClass(), node.getName())) { + Field field = builder.getClass().getDeclaredField(node.getName()); + field.setAccessible(true); + Object val = field.get(builder); + if (val==null) { + // Not set on builder, if the leaf node has a default value, try the private setter that takes String + try { + if (node.getDefaultValue()!=null) { + Method setter = builder.getClass().getDeclaredMethod(node.getName(), String.class); + setter.setAccessible(true); + setter.invoke(builder, node.getDefaultValue().getValue()); + } + } catch (Exception e) { + log.log(Level.SEVERE, + "For config '" + targetDef.getFullName() + "': " + + "Unable to apply the default value for field '" + node.getName() + + "' to config Builder (where it wasn't set)", + e); + } + } + } + } + + @SuppressWarnings("unchecked") + private static Class<? extends ConfigInstance> getConfigClass(Class<? extends ConfigInstance.Builder> builderClass) { + Class<?> configClass = builderClass.getEnclosingClass(); + if (configClass == null || ! ConfigInstance.class.isAssignableFrom(configClass)) { + throw new ConfigurationRuntimeException("Builder class " + builderClass + " has enclosing class " + configClass + ", which is not a ConfigInstance"); + } + return (Class<? extends ConfigInstance>) configClass; + } + +} |