diff options
author | Jan Srnicek <jsrnicek@cisco.com> | 2017-02-10 08:55:55 +0100 |
---|---|---|
committer | Marek Gradzki <mgradzki@cisco.com> | 2017-02-10 08:53:27 +0000 |
commit | de55d1e7c1fa5517ee6eabcd3fa23e5b5136d64b (patch) | |
tree | 057cd5f38c18da91852c2d79168ae41c0c6bfed6 /infra/test-utils/test-tools/src/main/java | |
parent | 8450b69800c827ba221351eb0c374fcbd9146593 (diff) |
HONEYCOMB-334 - List entry injection for yang data
Major changes
- mechanism to inject list entries by key
- provided processor registry to hide explicit implementations
Minor changes
- general refactoring
Test cases
- list in root of model
- list under container
- list under nested container
- list in augmentation
Change-Id: I9abe1ce5f9176c132ad88627b135516574e40e06
Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
Diffstat (limited to 'infra/test-utils/test-tools/src/main/java')
11 files changed, 448 insertions, 288 deletions
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java new file mode 100644 index 000000000..93360d25a --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +import javax.annotation.Nonnull; + +abstract class AbstractYangContextHolder { + + private final SchemaContext schemaContext; + private final BindingToNormalizedNodeCodec serializer; + + AbstractYangContextHolder(@Nonnull final SchemaContext schemaContext, + @Nonnull final BindingToNormalizedNodeCodec serializer){ + this.schemaContext=schemaContext; + this.serializer=serializer; + } + + SchemaContext schemaContext() { + return schemaContext; + } + + BindingToNormalizedNodeCodec serializer() { + return serializer; + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java new file mode 100644 index 000000000..089bb43cd --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.io.InputStream; + +import static com.google.common.base.Preconditions.checkState; +import static io.fd.honeycomb.translate.util.JsonUtils.readContainerEntryJson; +import static io.fd.honeycomb.translate.util.JsonUtils.readJson; + +final class ContainerNodeDataProcessor extends AbstractYangContextHolder implements YangDataProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(ContainerNodeDataProcessor.class); + + ContainerNodeDataProcessor(@Nonnull SchemaContext schemaContext, @Nonnull BindingToNormalizedNodeCodec serializer) { + super(schemaContext, serializer); + } + + @Nonnull + @Override + public DataObject getNodeData(@Nonnull YangInstanceIdentifier yangInstanceIdentifier, @Nonnull String resourcePath) { + + final InputStream resourceStream = this.getClass().getResourceAsStream(resourcePath); + final YangInstanceIdentifier nodeParent = getNodeParent(yangInstanceIdentifier).orElse(null); + final SchemaNode parentSchema = parentSchema(schemaContext(), serializer(), nodeParent, () -> LOG); + + // to be able to process containers in root of model + if (isRoot(yangInstanceIdentifier)) { + // if root ,read as root + final ContainerNode data = readJson(schemaContext(), resourceStream, parentSchema); + checkState(data.getValue().size() == 1, "Single root expected in %s", resourcePath); + //then extracts first child + final QName rootNodeType = data.getValue().iterator().next().getNodeType(); + final YangInstanceIdentifier realIdentifier = YangInstanceIdentifier.of(rootNodeType); + return nodeBinding(serializer(), realIdentifier, data).getValue(); + } else { + // reads just container + final YangInstanceIdentifier.NodeIdentifier nodeIdentifier = containerNodeIdentifier(yangInstanceIdentifier); + final ContainerNode data = readContainerEntryJson(schemaContext(), resourceStream, parentSchema, nodeIdentifier); + return nodeBinding(serializer(), yangInstanceIdentifier, data.getValue().iterator().next()).getValue(); + } + } + + @Override + public boolean canProcess(@Nonnull YangInstanceIdentifier identifier) { + return isRoot(identifier) || + isContainer(identifierBinding(serializer(), identifier).getTargetType()); + } + + private static boolean isContainer(final Class targetType) { + return !Identifiable.class.isAssignableFrom(targetType) + && !Augmentation.class.isAssignableFrom(targetType); + } + + private static YangInstanceIdentifier.NodeIdentifier containerNodeIdentifier(@Nonnull final YangInstanceIdentifier nodeIdentifier) { + return java.util.Optional.ofNullable(nodeIdentifier.getLastPathArgument()) + .filter(pathArgument -> pathArgument instanceof YangInstanceIdentifier.NodeIdentifier) + .map(YangInstanceIdentifier.NodeIdentifier.class::cast) + .orElseThrow(() -> new IllegalArgumentException( + String.format("Unable to create container node identifier from %s", nodeIdentifier))); + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java index 906a5fc87..0f8b1b9af 100644 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java @@ -17,29 +17,23 @@ package io.fd.honeycomb.test.tools; import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor; -import io.fd.honeycomb.test.tools.factories.ChildNodeDataFactory; -import io.fd.honeycomb.test.tools.factories.RootNodeDataFactory; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Parameter; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.Nullable; -import org.apache.commons.lang3.StringUtils; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.annotation.Annotation; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangContextProducer, InjectablesProcessor { private static final Logger LOG = LoggerFactory.getLogger(HoneycombTestRunner.class); @@ -48,8 +42,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC private BindingToNormalizedNodeCodec serializer; private AbstractModuleStringInstanceIdentifierCodec iidParser; - private ChildNodeDataFactory childNodeDataFactory; - private RootNodeDataFactory rootNodeDataFactory; + private YangDataProcessorRegistry processorRegistry; public HoneycombTestRunner(final Class<?> klass) throws InitializationError { super(klass); @@ -64,12 +57,11 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC final ModuleInfoBackedContext ctx = getCheckedModuleInfoContext(test); schemaContext = ctx.getSchemaContext(); // Create serializer from it in order to later transform NormalizedNodes into BA - serializer = createSerializer(ctx, schemaContext); + serializer = createSerializer(ctx); // Create InstanceIdentifier Codec in order to later transform string represented IID into YangInstanceIdentifier iidParser = getIIDCodec(ctx); - childNodeDataFactory = new ChildNodeDataFactory(schemaContext, serializer, iidParser); - rootNodeDataFactory = new RootNodeDataFactory(schemaContext, serializer, iidParser); + processorRegistry = YangDataProcessorRegistry.create(schemaContext, serializer); injectFields(test); return test; @@ -83,7 +75,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC /** * Allows method parameters injection - * */ + */ @Override protected Statement methodInvoker(final FrameworkMethod method, final Object test) { return new InjectableTestMethodInvoker(method, test, Arrays.stream(method.getMethod().getParameters()) @@ -94,7 +86,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC private Object injectValueOrNull(final Parameter parameter) { return isInjectable(parameter) - ? getData(resourcePath(parameter), instanceIdentifier(parameter).orNull(), parameter.getType()) + ? processorRegistry.getNodeData(instanceIdentifier(iidParser, parameter), resourcePath(parameter)) : null; } @@ -107,22 +99,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC injectableFields(testInstance.getClass()).forEach(field -> { LOG.debug("Processing field {}", field); injectField(field, testInstance, - getData(resourcePath(field), instanceIdentifier(field).orNull(), field.getType())); + processorRegistry.getNodeData(instanceIdentifier(iidParser, field), resourcePath(field))); }); } - - private DataObject getData(final String resourcePath, @Nullable final String identifier, - final Class<?> injectedType) { - try { - if (StringUtils.isNotEmpty(identifier)) { - LOG.debug("Processing {} as child node {}", injectedType, identifier); - return childNodeDataFactory.getChildNodeData(identifier, resourcePath); - } else { - LOG.debug("Processing {} as root node", injectedType); - return rootNodeDataFactory.getRootNodeData(getRootInstanceIdentifier(injectedType), resourcePath); - } - } catch (DeserializationException | IOException e) { - throw new IllegalStateException(e); - } - } } diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java new file mode 100644 index 000000000..375f55307 --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.io.InputStream; + +import static com.google.common.base.Preconditions.checkArgument; +import static io.fd.honeycomb.translate.util.JsonUtils.readListEntryFromJson; + +/** + * json --> BA processor for list entry data + */ +final class ListNodeDataProcessor extends AbstractYangContextHolder implements YangDataProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(ListNodeDataProcessor.class); + + ListNodeDataProcessor(@Nonnull final SchemaContext schemaContext, + @Nonnull final BindingToNormalizedNodeCodec serializer) { + super(schemaContext, serializer); + } + + @Nonnull + @Override + public DataObject getNodeData(@Nonnull final YangInstanceIdentifier nodeIdentifier, + @Nonnull final String resourcePath) { + checkArgument(canProcess(nodeIdentifier), "Cannot process identifier %s", nodeIdentifier); + final YangInstanceIdentifier listParent = listNodeParent(nodeIdentifier); + final YangInstanceIdentifier.NodeIdentifierWithPredicates keyedNodeIdentifier = listNodeIdentifier(nodeIdentifier); + final InputStream resourceStream = this.getClass().getResourceAsStream(resourcePath); + final SchemaNode parentSchemaNode = parentSchema(schemaContext(), serializer(), listParent, () -> LOG); + final MapEntryNode data = readListEntryFromJson(schemaContext(), resourceStream, parentSchemaNode, keyedNodeIdentifier); + + return nodeBinding(serializer(), nodeIdentifier, data).getValue(); + } + + @Override + public boolean canProcess(@Nonnull final YangInstanceIdentifier identifier) { + return !isRoot(identifier) && + Identifiable.class.isAssignableFrom(identifierBinding(serializer(), identifier).getTargetType()); + } + + private YangInstanceIdentifier listNodeParent(@Nonnull final YangInstanceIdentifier nodeIdentifier) { + // if it is list, real parent is second to last + return getNodeParent(nodeIdentifier).map(parent -> getNodeParent(parent).orElse(null)) + .orElseThrow(() -> new IllegalArgumentException( + String.format("Unable to get parent for list node from %s", nodeIdentifier))); + } + + private static YangInstanceIdentifier.NodeIdentifierWithPredicates listNodeIdentifier(@Nonnull final YangInstanceIdentifier nodeIdentifier) { + return java.util.Optional.ofNullable(nodeIdentifier.getLastPathArgument()) + .filter(pathArgument -> pathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) + .map(YangInstanceIdentifier.NodeIdentifierWithPredicates.class::cast) + .orElseThrow(() -> new IllegalArgumentException( + String.format("Unable to create list node identifier from %s", nodeIdentifier))); + } +} + + + diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java index 23af9733d..017a68c9f 100644 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java @@ -16,12 +16,7 @@ package io.fd.honeycomb.test.tools; -import static com.google.common.base.Preconditions.checkState; - import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import javassist.ClassPool; import org.apache.commons.lang3.reflect.MethodUtils; import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; @@ -34,6 +29,12 @@ import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory; import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static com.google.common.base.Preconditions.checkState; + /** * Common logic to initialize serializers/deserializers/etc while working with yang based data */ @@ -67,14 +68,13 @@ interface YangContextProducer { return (AbstractModuleStringInstanceIdentifierCodec) cstr.newInstance(ctx.getSchemaContext(), jsonCodecFactory); } - default BindingToNormalizedNodeCodec createSerializer(final ModuleInfoBackedContext moduleInfoBackedContext, - final SchemaContext schemaContexts) { + default BindingToNormalizedNodeCodec createSerializer(final ModuleInfoBackedContext moduleInfoBackedContext) { final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry( StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault()))); codecRegistry - .onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, schemaContexts)); + .onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, moduleInfoBackedContext.getSchemaContext())); return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry); } } diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java new file mode 100644 index 000000000..a353e4b3b --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.slf4j.Logger; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.AbstractMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +interface YangDataProcessor { + + /** + * Attempts to find data in file specified by <b>resourcePath</b>,<br> + * and translate it to BA object + * + * @param yangInstanceIdentifier identifier of path to read + * @param resourcePath path of resource file to load + */ + @Nonnull + DataObject getNodeData(@Nonnull final YangInstanceIdentifier yangInstanceIdentifier, + @Nonnull final String resourcePath); + + /** + * Verifies if provided identifier is identifying node processed by this processor + * + * @param identifier node identifier + */ + boolean canProcess(@Nonnull final YangInstanceIdentifier identifier); + + default boolean isRoot(@Nonnull final YangInstanceIdentifier identifier) { + return identifier.getPathArguments().isEmpty(); + } + + @Nonnull + default Optional<YangInstanceIdentifier> getNodeParent(@Nonnull final YangInstanceIdentifier identifier) { + return Optional.ofNullable(identifier.getParent()); + } + + @Nonnull + default SchemaNode parentSchema(@Nonnull final SchemaContext schemaContext, + @Nonnull final BindingToNormalizedNodeCodec serializer, + @Nullable final YangInstanceIdentifier parentYangId, + @Nonnull final Supplier<Logger> logProvider) { + // null or root + if (parentYangId == null || parentYangId.getPathArguments().size() == 0) { + // no parent == use schema context as root context + logProvider.get().info("Parent is null, providing schema context as parent node"); + return schemaContext; + } + + final com.google.common.base.Optional<InstanceIdentifier<? extends DataObject>> parentInstanceId; + try { + parentInstanceId = serializer.toBinding(parentYangId); + } catch (DeserializationException e) { + throw new IllegalArgumentException(String.format("Unable to deserialize %s", parentYangId)); + } + + if (!parentInstanceId.isPresent()) { + throw new IllegalStateException(String.format("Unable to resolve %s to instance identifier", parentYangId)); + } + + final com.google.common.base.Optional<DataNodeContainer> dataNodeContainerOptional = + BindingSchemaContextUtils.findDataNodeContainer(schemaContext, parentInstanceId.get()); + + + if (!dataNodeContainerOptional.isPresent()) { + throw new IllegalArgumentException(String.format("Error finding DataNodeContainer for %s", parentInstanceId.get())); + } + + final DataNodeContainer parentNode = dataNodeContainerOptional.get(); + logProvider.get().info("Parent schema node resolved as {}", parentNode); + return (SchemaNode) parentNode; + } + + @Nonnull + default Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> nodeBinding(@Nonnull final BindingToNormalizedNodeCodec serializer, + @Nonnull final YangInstanceIdentifier identifier, + @Nonnull final NormalizedNode<?, ?> data) { + try { + return serializer.toBinding(new AbstractMap.SimpleImmutableEntry<>(identifier, data)) + .or(() -> { + throw new IllegalArgumentException(String.format("Unable to create node binding for %s|%s", identifier, data)); + }); + } catch (DeserializationException e) { + throw new IllegalArgumentException(String.format("Unable to deserialize node %s|%s", identifier, data)); + } + } + + @Nonnull + default InstanceIdentifier<? extends DataObject> identifierBinding(@Nonnull final BindingToNormalizedNodeCodec serializer, + @Nonnull final YangInstanceIdentifier identifier) { + try { + return serializer.toBinding(identifier) + .or(() -> { + throw new IllegalArgumentException(String.format("Unable convert %s to binding", identifier)); + }); + } catch (DeserializationException e) { + throw new IllegalArgumentException(String.format("Unable to deserialize %s", identifier)); + } + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java new file mode 100644 index 000000000..d23cd59c1 --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +import javax.annotation.Nonnull; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Process yang data from json to BA Objects + */ +final class YangDataProcessorRegistry { + + private final List<YangDataProcessor> processors; + + private YangDataProcessorRegistry(@Nonnull final SchemaContext context, + @Nonnull final BindingToNormalizedNodeCodec codec) { + // linked should be faster for iteration + processors = new LinkedList<>(); + processors.add(new ListNodeDataProcessor(context, codec)); + processors.add(new ContainerNodeDataProcessor(context, codec)); + } + + static YangDataProcessorRegistry create(@Nonnull final SchemaContext context, + @Nonnull final BindingToNormalizedNodeCodec codec) { + return new YangDataProcessorRegistry(context, codec); + } + + @Nonnull + DataObject getNodeData(@Nonnull final YangInstanceIdentifier yangInstanceIdentifier, + @Nonnull final String resourcePath) { + return pickProcessor(yangInstanceIdentifier).getNodeData(yangInstanceIdentifier, resourcePath); + } + + private YangDataProcessor pickProcessor(final YangInstanceIdentifier yangInstanceIdentifier) { + final List<YangDataProcessor> eligibleProcessors = processors.stream() + .filter(processors -> processors.canProcess(yangInstanceIdentifier)) + .collect(Collectors.toList()); + + // canProcess should be exclusive for node type, but just in case + checkState(eligibleProcessors.size() == 1, + "No single eligible processor found, matches=[%s]", eligibleProcessors); + + return eligibleProcessors.get(0); + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java index c364938ed..216d8353b 100644 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java @@ -16,22 +16,21 @@ package io.fd.honeycomb.test.tools.annotations; -import static io.fd.honeycomb.test.tools.annotations.InjectTestData.NO_ID; - -import com.google.common.base.Optional; -import java.lang.reflect.Field; -import java.lang.reflect.Parameter; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; import org.apache.commons.lang3.reflect.FieldUtils; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.YangModuleInfo; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; + +import javax.annotation.Nonnull; +import java.lang.reflect.Field; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.Set; + +import static io.fd.honeycomb.test.tools.annotations.InjectTestData.NO_ID; /** * Common logic for @InjectTestData @@ -54,23 +53,23 @@ public interface InjectablesProcessor { return parameter.getAnnotation(InjectTestData.class).resourcePath(); } - default Optional<String> instanceIdentifier(final Field field) { + default YangInstanceIdentifier instanceIdentifier(@Nonnull final AbstractModuleStringInstanceIdentifierCodec parser, @Nonnull final Field field) { final String identifier = field.getAnnotation(InjectTestData.class).id(); // == used instead of equals to ensure constant was used if (NO_ID.equals(identifier)) { - return Optional.absent(); + return getRootInstanceIdentifier(field.getType()); } else { - return Optional.of(identifier); + return parser.deserialize(identifier); } } - default Optional<String> instanceIdentifier(final Parameter parameter) { + default YangInstanceIdentifier instanceIdentifier(@Nonnull final AbstractModuleStringInstanceIdentifierCodec parser, @Nonnull final Parameter parameter) { final String identifier = parameter.getAnnotation(InjectTestData.class).id(); // == used instead of equals to ensure constant was used if (NO_ID.equals(identifier)) { - return Optional.absent(); + return getRootInstanceIdentifier(parameter.getType()); } else { - return Optional.of(identifier); + return parser.deserialize(identifier); } } @@ -83,7 +82,7 @@ public interface InjectablesProcessor { } } - default YangInstanceIdentifier getRootInstanceIdentifier(final Class type) { + static YangInstanceIdentifier getRootInstanceIdentifier(final Class type) { try { return YangInstanceIdentifier.of(QName.class.cast(type.getField("QNAME").get(null))); } catch (IllegalAccessException e) { diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java deleted file mode 100644 index 05812123e..000000000 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.test.tools.factories; - - -import com.google.common.base.Optional; -import java.io.IOException; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; -import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ChildNodeDataFactory extends YangDataFactory { - - private static final Logger LOG = LoggerFactory.getLogger(ChildNodeDataFactory.class); - - public ChildNodeDataFactory(@Nonnull final SchemaContext schemaContext, - @Nonnull final BindingToNormalizedNodeCodec serializer, - @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) { - super(schemaContext, serializer, iidParser); - } - - public DataObject getChildNodeData(final String instanceIdentifier, - final String resourcePath) throws DeserializationException, IOException { - // Parse string ID into YangId - final YangInstanceIdentifier nodeYid = iidParser.deserialize(instanceIdentifier); - // Look for parent YangId - final YangInstanceIdentifier parentYid = nodeYid.getParent(); - - if (parentYid.isEmpty()) { - throw new IllegalArgumentException( - "Attempt to process root node as children has been detected,to process root nodes just don't use id in @InjectTestData"); - } - // Find Schema node for parent of data that's currently being parsed (needed when parsing the data into NormalizedNodes) - return getDataForNode(nodeYid, resourcePath, getNonRootParentSchema(parentYid)); - } - - private DataNodeContainer getNonRootParentSchema(final YangInstanceIdentifier parentYangId) - throws DeserializationException { - LOG.debug("Processing parent identifier {}", parentYangId); - final Optional<InstanceIdentifier<? extends DataObject>> parentInstanceId = serializer.toBinding(parentYangId); - if (!parentInstanceId.isPresent()) { - throw new IllegalStateException("Unable to resolve " + parentYangId + " to instance identifier"); - } - - final Optional<DataNodeContainer> dataNodeContainerOptional = - BindingSchemaContextUtils.findDataNodeContainer(schemaContext, parentInstanceId.get()); - - if (!dataNodeContainerOptional.isPresent()) { - throw new IllegalArgumentException("Error finding DataNodeContainer for " + parentInstanceId.get()); - } - - return dataNodeContainerOptional.get(); - } -} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java deleted file mode 100644 index 3aacea016..000000000 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.test.tools.factories; - - -import java.io.IOException; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; -import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - -public class RootNodeDataFactory extends YangDataFactory { - - public RootNodeDataFactory(@Nonnull final SchemaContext schemaContext, - @Nonnull final BindingToNormalizedNodeCodec serializer, - @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) { - super(schemaContext, serializer, iidParser); - } - - public DataObject getRootNodeData(final YangInstanceIdentifier rootInstanceIdentifier, - final String resourcePath) throws DeserializationException, IOException { - //entire schema context is parent schema in this case - return getDataForNode(rootInstanceIdentifier, resourcePath, schemaContext); - } -} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java deleted file mode 100644 index ecf556cd2..000000000 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.test.tools.factories; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.base.Optional; -import com.google.common.collect.Iterables; -import io.fd.honeycomb.translate.util.JsonUtils; -import java.io.IOException; -import java.util.AbstractMap; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; -import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; - -/** - * Common logic for reading of yang data - */ -abstract class YangDataFactory { - - final SchemaContext schemaContext; - final BindingToNormalizedNodeCodec serializer; - final AbstractModuleStringInstanceIdentifierCodec iidParser; - - YangDataFactory(@Nonnull final SchemaContext schemaContext, - @Nonnull final BindingToNormalizedNodeCodec serializer, - @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) { - this.schemaContext = checkNotNull(schemaContext, "SchemaContext cannot be null"); - this.serializer = checkNotNull(serializer, "Serializer cannot be null"); - this.iidParser = checkNotNull(iidParser, "Instance identifier parser cannot be null"); - } - - DataObject getDataForNode(final YangInstanceIdentifier nodeYangIdentifier, - final String resourcePath, - final DataNodeContainer parentSchema) - throws DeserializationException, IOException { - - // Reads resources from provided resource path - final ContainerNode rootData = getCheckedRootData(resourcePath, parentSchema); - - // Now transform the single child from JSON into BA format - final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData = - extractCheckedSingleChild(rootData); - - return getCheckedBinding(nodeYangIdentifier, actualData).getValue(); - } - - private ContainerNode getCheckedRootData(final String resourcePath, final DataNodeContainer parentSchema) - throws IOException { - // TODO the cast to SchemaNode is dangerous and would not work for Augments, Choices and some other nodes maybe. At least check - // TODO not sure if this is true, while testing this code was working fine event while processing choices/cases, - // TODO only problem is to find suitable codec that can process cases,etc - // Transform JSON into NormalizedNode - - final ContainerNode rootData = JsonUtils.readJson(schemaContext, - checkNotNull(this.getClass().getResource(resourcePath), "Unable to find resource %s", resourcePath) - .openStream(), ((SchemaNode) parentSchema)); - - checkArgument(rootData.getValue().size() == 1, "Only a single data node is expected in %s, but there were: %s", - resourcePath, rootData.getValue()); - return rootData; - } - - private DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> extractCheckedSingleChild( - final ContainerNode rootData) { - // Now transform the single child from JSON into BA format - final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData = - Iterables.getFirst(rootData.getValue(), null); - - checkNotNull(actualData, "Unable to extract single child from %s", rootData); - return actualData; - } - - private Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> getCheckedBinding( - final YangInstanceIdentifier nodeYangIdentifier, - final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData) - throws DeserializationException { - final Optional<Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject>> ba = - serializer.toBinding(new AbstractMap.SimpleImmutableEntry<>(nodeYangIdentifier, actualData)); - - checkArgument(ba.isPresent(), "Unable to convert to binding %s", nodeYangIdentifier); - return ba.get(); - } -} |