From 2b1748599e4c820730eef2deb7f59489bab82f82 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 12 Apr 2016 10:13:18 +0200 Subject: HONEYCOMB-9: Remove references to VPP from translation layer Change-Id: I281db366a112edc08203e8cb392a212708d4552a Signed-off-by: Maros Marsalek --- .../fd/honeycomb/v3po/translate/util/RWUtils.java | 178 +++++++++++++++++++++ .../v3po/translate/util/ReflectionUtils.java | 79 +++++++++ .../util/read/DelegatingReaderRegistry.java | 113 +++++++++++++ .../translate/util/read/NoopReaderCustomizer.java | 32 ++++ .../util/read/ReflexiveChildReaderCustomizer.java | 57 +++++++ .../util/read/ReflexiveRootReaderCustomizer.java | 42 +++++ .../util/write/DelegatingWriterRegistry.java | 176 ++++++++++++++++++++ .../translate/util/write/NoopWriterCustomizer.java | 49 ++++++ .../util/write/ReflexiveChildWriterCustomizer.java | 59 +++++++ .../util/write/TransactionWriteContext.java | 101 ++++++++++++ 10 files changed, 886 insertions(+) create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/DelegatingReaderRegistry.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java create mode 100644 v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java (limited to 'v3po/translate-utils/src/main/java/io') diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java new file mode 100644 index 000000000..027d9bbb7 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java @@ -0,0 +1,178 @@ +/* + * 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.v3po.translate.util; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import io.fd.honeycomb.v3po.translate.read.ChildReader; +import io.fd.honeycomb.v3po.translate.write.ChildWriter; +import io.fd.honeycomb.v3po.translate.SubtreeManager; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.ChildOf; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class RWUtils { + + private RWUtils() {} + + /** + * Find next item in ID after provided type + */ + @Nonnull + public static InstanceIdentifier.PathArgument getNextId(@Nonnull final InstanceIdentifier id, + @Nonnull final InstanceIdentifier type) { + // TODO this is inefficient(maybe, depending on actual Iterable type) + final Iterable pathArguments = id.getPathArguments(); + final int i = Iterables.indexOf(pathArguments, new Predicate() { + @Override + public boolean apply(final InstanceIdentifier.PathArgument input) { + return input.getType().isAssignableFrom(type.getTargetType()); + } + }); + Preconditions.checkArgument(i >= 0, "Unable to find %s type in %s", type.getTargetType(), id); + return Iterables.get(pathArguments, i + 1); + } + + public static List>> emptyChildReaderList() { + return Collections.emptyList(); + } + + public static List>> emptyChildWriterList() { + return Collections.emptyList(); + } + + public static List>> emptyAugReaderList() { + return Collections.emptyList(); + } + + public static List>> emptyAugWriterList() { + return Collections.emptyList(); + } + + public static List>> singletonAugReaderList( + ChildReader> item) { + return Collections.>>singletonList(item); + } + + public static List>> singletonChildReaderList( + ChildReader> item) { + return Collections.>>singletonList(item); + } + + public static List>> singletonChildWriterList( + ChildWriter> item) { + return Collections.>>singletonList(item); + } + + /** + * Replace last item in ID with a provided IdentifiableItem of the same type + */ + @SuppressWarnings("unchecked") + @Nonnull + public static , K extends Identifier> InstanceIdentifier replaceLastInId( + @Nonnull final InstanceIdentifier id, final InstanceIdentifier.IdentifiableItem currentBdItem) { + + final Iterable pathArguments = id.getPathArguments(); + final Iterable withoutCurrent = + Iterables.limit(pathArguments, Iterables.size(pathArguments) - 1); + final Iterable concat = + Iterables.concat(withoutCurrent, Collections.singleton(currentBdItem)); + return (InstanceIdentifier) InstanceIdentifier.create(concat); + } + + /** + * Create IdentifiableItem from target type of provided ID with provided key + */ + @Nonnull + public static , K extends Identifier> InstanceIdentifier.IdentifiableItem getCurrentIdItem( + @Nonnull final InstanceIdentifier id, final K key) { + return new InstanceIdentifier.IdentifiableItem<>(id.getTargetType(), key); + } + + /** + * Trim InstanceIdentifier at indexOf(type) + */ + @SuppressWarnings("unchecked") + @Nonnull + public static InstanceIdentifier cutId(@Nonnull final InstanceIdentifier id, + @Nonnull final InstanceIdentifier type) { + final Iterable pathArguments = id.getPathArguments(); + final int i = Iterables.indexOf(pathArguments, new Predicate() { + @Override + public boolean apply(final InstanceIdentifier.PathArgument input) { + return input.getType().equals(type.getTargetType()); + } + }); + Preconditions.checkArgument(i >= 0, "ID %s does not contain %s", id, type); + return (InstanceIdentifier) InstanceIdentifier.create(Iterables.limit(pathArguments, i + 1)); + } + + /** + * Create a map from a collection, checking for duplicity in the process + */ + @Nonnull + public static Map uniqueLinkedIndex(@Nonnull final Collection values, @Nonnull final Function keyFunction) { + final Map objectObjectLinkedHashMap = Maps.newLinkedHashMap(); + for (V value : values) { + final K key = keyFunction.apply(value); + Preconditions.checkArgument(objectObjectLinkedHashMap.put(key, value) == null, + "Duplicate key detected : %s", key); + } + return objectObjectLinkedHashMap; + } + + public static final Function, Class> + MANAGER_CLASS_FUNCTION = new Function, Class>() { + @Override + public Class apply(final SubtreeManager input) { + return input.getManagedDataObjectType().getTargetType(); + } + }; + + public static final Function>, Class> + MANAGER_CLASS_AUG_FUNCTION = new Function>, Class>() { + + @Override + @SuppressWarnings("unchecked") + public Class apply(final SubtreeManager> input) { + final Class> targetType = input.getManagedDataObjectType().getTargetType(); + Preconditions.checkArgument(DataObject.class.isAssignableFrom(targetType)); + return (Class) targetType; + } + }; + + @SuppressWarnings("unchecked") + public static InstanceIdentifier appendTypeToId( + final InstanceIdentifier parentId, final InstanceIdentifier type) { + Preconditions.checkArgument(!parentId.contains(type), + "Unexpected InstanceIdentifier %s, already contains %s", parentId, type); + final InstanceIdentifier.PathArgument t = Iterables.getOnlyElement(type.getPathArguments()); + return (InstanceIdentifier) InstanceIdentifier.create(Iterables.concat( + parentId.getPathArguments(), Collections.singleton(t))); + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java new file mode 100644 index 000000000..ea0b3b2c4 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java @@ -0,0 +1,79 @@ +/* + * 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.v3po.translate.util; + +import com.google.common.base.Optional; +import java.lang.reflect.Method; +import java.util.List; +import javax.annotation.Nonnull; + +/** + * Reflection based utilities + */ +public final class ReflectionUtils { + + private ReflectionUtils() {} + + /** + * Find a specific method using reflection + * + * @param managedType Class object to find method in + * @param prefix Method name prefix used when finding the method. Case does not matter. + * @param paramTypes List of input argument types + * @param retType Return type + * + * @return Found method or Optional.absent() if there's no such method + */ + @Nonnull + public static Optional findMethodReflex(@Nonnull final Class managedType, + @Nonnull final String prefix, + @Nonnull final List> paramTypes, + @Nonnull final Class retType) { + for (Method method : managedType.getMethods()) { + if(isMethodMatch(prefix, paramTypes, retType, method)) { + return Optional.of(method); + } + } + + return Optional.absent(); + } + + private static boolean isMethodMatch(final @Nonnull String prefix, + final @Nonnull List> paramTypes, + final @Nonnull Class retType, final Method method) { + if (!method.getName().toLowerCase().startsWith(prefix.toLowerCase())) { + return false; + } + + final Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != paramTypes.size()) { + return false; + } + + for (int i = 0; i < parameterTypes.length; i++) { + if (!parameterTypes[i].isAssignableFrom(paramTypes.get(i))) { + return false; + } + } + + if (!method.getReturnType().equals(retType)) { + return false; + } + + return true; + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/DelegatingReaderRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/DelegatingReaderRegistry.java new file mode 100644 index 000000000..387f3cc7c --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/DelegatingReaderRegistry.java @@ -0,0 +1,113 @@ +/* + * 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.v3po.translate.util.read; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.v3po.translate.read.ListReader; +import io.fd.honeycomb.v3po.translate.read.ReadContext; +import io.fd.honeycomb.v3po.translate.read.ReadFailedException; +import io.fd.honeycomb.v3po.translate.read.ReaderRegistry; +import io.fd.honeycomb.v3po.translate.util.RWUtils; +import io.fd.honeycomb.v3po.translate.read.Reader; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple reader registry able to perform and aggregated read (ROOT read) on top of all provided readers. Also able to + * delegate a specific read to one of the delegate readers. + * + * This could serve as a utility to hold & hide all available readers in upper layers. + */ +public final class DelegatingReaderRegistry implements ReaderRegistry { + + private static final Logger LOG = LoggerFactory.getLogger(DelegatingReaderRegistry.class); + + private final Map, Reader> rootReaders; + + /** + * Create new {@link DelegatingReaderRegistry} + * + * @param rootReaders List of delegate readers + */ + public DelegatingReaderRegistry(@Nonnull final List> rootReaders) { + this.rootReaders = RWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), RWUtils.MANAGER_CLASS_FUNCTION); + } + + @Override + @Nonnull + public Multimap, ? extends DataObject> readAll( + @Nonnull final ReadContext ctx) throws ReadFailedException { + + LOG.debug("Reading from all delegates: {}", this); + LOG.trace("Reading from all delegates: {}", rootReaders.values()); + + final Multimap, DataObject> objects = LinkedListMultimap.create(); + for (Reader rootReader : rootReaders.values()) { + LOG.debug("Reading from delegate: {}", rootReader); + + if (rootReader instanceof ListReader) { + final List listEntries = + ((ListReader) rootReader).readList(rootReader.getManagedDataObjectType(), ctx); + if (!listEntries.isEmpty()) { + objects.putAll(rootReader.getManagedDataObjectType(), listEntries); + } + } else { + final Optional read = rootReader.read(rootReader.getManagedDataObjectType(), ctx); + if (read.isPresent()) { + objects.putAll(rootReader.getManagedDataObjectType(), Collections.singletonList(read.get())); + } + } + } + + return objects; + } + + @Nonnull + @Override + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + final InstanceIdentifier.PathArgument first = checkNotNull( + Iterables.getFirst(id.getPathArguments(), null), "Empty id"); + final Reader reader = rootReaders.get(first.getType()); + checkNotNull(reader, + "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet()); + LOG.debug("Reading from delegate: {}", reader); + return reader.read(id, ctx); + } + + /** + * @throws UnsupportedOperationException This getter is not supported for reader registry since it does not manage a + * specific node type + */ + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + throw new UnsupportedOperationException("Root registry has no type"); + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java new file mode 100644 index 000000000..5ed033755 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java @@ -0,0 +1,32 @@ +/* + * 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.v3po.translate.util.read; + +import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public abstract class NoopReaderCustomizer> implements + RootReaderCustomizer { + + @Override + public void readCurrentAttributes(InstanceIdentifier id, final B builder, final Context context) { + // Noop + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java new file mode 100644 index 000000000..3d5f9f4e8 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java @@ -0,0 +1,57 @@ +/* + * 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.v3po.translate.util.read; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; +import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; + +/** + * Might be slow ! + */ +public class ReflexiveChildReaderCustomizer> + extends ReflexiveRootReaderCustomizer + implements ChildReaderCustomizer { + + public ReflexiveChildReaderCustomizer(final Class builderClass) { + super(builderClass); + } + + // TODO Could be just a default implementation in interface (making this a mixin) + + @Override + public void merge(final Builder parentBuilder, final C readValue) { + final Optional method = + ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set", + Collections.>singletonList(readValue.getClass()), parentBuilder.getClass()); + + Preconditions.checkArgument(method.isPresent(), "Unable to set %s to %s", readValue, parentBuilder); + + try { + method.get().invoke(parentBuilder, readValue); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e); + } + } + +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java new file mode 100644 index 000000000..029f359bb --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java @@ -0,0 +1,42 @@ +/* + * 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.v3po.translate.util.read; + +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Might be slow ! + */ +public class ReflexiveRootReaderCustomizer> extends NoopReaderCustomizer { + + private final Class builderClass; + + public ReflexiveRootReaderCustomizer(final Class builderClass) { + this.builderClass = builderClass; + } + + @Override + public B getBuilder(InstanceIdentifier id) { + try { + return builderClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalStateException("Unable to instantiate " + builderClass, e); + } + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java new file mode 100644 index 000000000..fda289e2b --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java @@ -0,0 +1,176 @@ +/* + * 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.v3po.translate.util.write; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.fd.honeycomb.v3po.translate.TranslationException; +import io.fd.honeycomb.v3po.translate.util.RWUtils; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.write.Writer; +import io.fd.honeycomb.v3po.translate.write.WriterRegistry; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple writer registry able to perform and aggregated read (ROOT write) on top of all provided writers. Also able to + * delegate a specific read to one of the delegate writers. + * + * This could serve as a utility to hold & hide all available writers in upper layers. + */ +public final class DelegatingWriterRegistry implements WriterRegistry { + + private static final Logger LOG = LoggerFactory.getLogger(DelegatingWriterRegistry.class); + + private static final Function, Class> ID_TO_CLASS = + new Function, Class>() { + @Override + public Class apply(final InstanceIdentifier input) { + return input.getTargetType(); + } + }; + + private final Map, Writer> rootWriters; + + /** + * Create new {@link DelegatingWriterRegistry} + * + * @param rootWriters List of delegate writers + */ + public DelegatingWriterRegistry(@Nonnull final List> rootWriters) { + this.rootWriters = RWUtils.uniqueLinkedIndex(checkNotNull(rootWriters), RWUtils.MANAGER_CLASS_FUNCTION); + } + + /** + * @throws UnsupportedOperationException This getter is not supported for writer registry since it does not manage a + * specific node type + */ + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + throw new UnsupportedOperationException("Root registry has no type"); + } + + @Override + public void update(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, + @Nonnull final WriteContext ctx) throws TranslationException { + final InstanceIdentifier.PathArgument first = checkNotNull( + Iterables.getFirst(id.getPathArguments(), null), "Empty id"); + final Writer writer = rootWriters.get(first.getType()); + checkNotNull(writer, + "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet()); + writer.update(id, dataBefore, dataAfter, ctx); + } + + @Override + public void update(@Nonnull final Map, DataObject> nodesBefore, + @Nonnull final Map, DataObject> nodesAfter, + @Nonnull final WriteContext ctx) throws TranslationException { + checkAllWritersPresent(nodesBefore); + checkAllWritersPresent(nodesAfter); + + final List> processedNodes = Lists.newArrayList(); + + for (Map.Entry, Writer> rootWriterEntry : rootWriters + .entrySet()) { + + final InstanceIdentifier id = rootWriterEntry.getValue().getManagedDataObjectType(); + + final DataObject dataBefore = nodesBefore.get(id); + final DataObject dataAfter = nodesAfter.get(id); + + // No change to current writer + if (dataBefore == null && dataAfter == null) { + continue; + } + + LOG.debug("ChangesProcessor.applyChanges() processing dataBefore={}, dataAfter={}", dataBefore, dataAfter); + + try { + update(id, dataBefore, dataAfter, ctx); + processedNodes.add(id); + } catch (Exception e) { + LOG.error("Error while processing data change of: {} (before={}, after={})", + id, dataBefore, dataAfter, e); + throw new BulkUpdateException( + id, new ReverterImpl(this, processedNodes, nodesBefore, nodesAfter, ctx), e); + } + } + } + + private void checkAllWritersPresent(final @Nonnull Map, DataObject> nodesBefore) { + checkArgument(rootWriters.keySet().containsAll(Collections2.transform(nodesBefore.keySet(), ID_TO_CLASS)), + "Unable to handle all changes. Missing dedicated writers for: %s", + Sets.difference(nodesBefore.keySet(), rootWriters.keySet())); + } + + private static final class ReverterImpl implements Reverter { + private final WriterRegistry delegatingWriterRegistry; + private final List> processedNodes; + private final Map, DataObject> nodesBefore; + private final Map, DataObject> nodesAfter; + private final WriteContext ctx; + + ReverterImpl(final WriterRegistry delegatingWriterRegistry, + final List> processedNodes, + final Map, DataObject> nodesBefore, + final Map, DataObject> nodesAfter, final WriteContext ctx) { + this.delegatingWriterRegistry = delegatingWriterRegistry; + this.processedNodes = processedNodes; + this.nodesBefore = nodesBefore; + this.nodesAfter = nodesAfter; + this.ctx = ctx; + } + + @Override + public void revert() throws RevertFailedException { + final LinkedList> notReverted = new LinkedList<>(processedNodes); + + while (notReverted.size() > 0) { + final InstanceIdentifier node = notReverted.peekLast(); + LOG.debug("ChangesProcessor.revertChanges() processing node={}", node); + + final DataObject dataBefore = nodesBefore.get(node); + final DataObject dataAfter = nodesAfter.get(node); + + // revert a change by invoking writer with reordered arguments + try { + delegatingWriterRegistry.update(node, dataAfter, dataBefore, ctx); + notReverted.removeLast(); // change was successfully reverted + } catch (Exception e) { + throw new RevertFailedException(notReverted, e); + } + + } + } + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java new file mode 100644 index 000000000..266325815 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java @@ -0,0 +1,49 @@ +/* + * 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.v3po.translate.util.write; + +import io.fd.honeycomb.v3po.translate.spi.write.RootWriterCustomizer; +import io.fd.honeycomb.v3po.translate.Context; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Customizer not performing any changes on current level. Suitable for nodes that don't have any leaves and all of + * its child nodes are managed by dedicated writers + */ +public class NoopWriterCustomizer implements RootWriterCustomizer { + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataAfter, + @Nonnull final Context ctx) { + + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, + @Nonnull final D dataAfter, + @Nonnull final Context ctx) { + + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final D dataBefore, + @Nonnull final Context ctx) { + + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java new file mode 100644 index 000000000..ba67e560c --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java @@ -0,0 +1,59 @@ +/* + * 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.v3po.translate.util.write; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; +import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Might be slow ! + */ +public class ReflexiveChildWriterCustomizer extends NoopWriterCustomizer implements + ChildWriterCustomizer { + + @Nonnull + @Override + @SuppressWarnings("unchecked") + public Optional extract(@Nonnull final InstanceIdentifier currentId, @Nonnull final DataObject parentData) { + final Class currentType = currentId.getTargetType(); + final Optional method = ReflectionUtils.findMethodReflex(getParentType(currentId), + "get" + currentType.getSimpleName(), Collections.>emptyList(), currentType); + + Preconditions.checkArgument(method.isPresent(), "Unable to get %s from %s", currentType, parentData); + + try { + return method.isPresent() + ? Optional.of((C) method.get().invoke(parentData)) + : Optional.absent(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to get " + currentType + " from " + parentData, e); + } + } + + private Class getParentType(final @Nonnull InstanceIdentifier currentId) { + return Iterables.get(currentId.getPathArguments(), Iterables.size(currentId.getPathArguments()) - 2).getType(); + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java new file mode 100644 index 000000000..0bb68e3b2 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java @@ -0,0 +1,101 @@ +/* + * 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.v3po.translate.util.write; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.Context; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; +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; + +/** + * Transaction based WriteContext + */ +public final class TransactionWriteContext implements WriteContext { + + private final DOMDataReadOnlyTransaction beforeTx; + private final DOMDataReadOnlyTransaction afterTx; + private final Context ctx; + private final BindingNormalizedNodeSerializer serializer; + + public TransactionWriteContext(final BindingNormalizedNodeSerializer serializer, + final DOMDataReadOnlyTransaction beforeTx, + final DOMDataReadOnlyTransaction afterTx) { + this.serializer = serializer; + this.beforeTx = beforeTx; + this.afterTx = afterTx; + this.ctx = new Context(); + } + + // TODO make this asynchronous + + @Override + public Optional readBefore(@Nonnull final InstanceIdentifier currentId) { + return read(currentId, beforeTx); + } + + private Optional read(final InstanceIdentifier currentId, + final DOMDataReadOnlyTransaction tx) { + final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(currentId); + + final CheckedFuture>, ReadFailedException> read = + tx.read(LogicalDatastoreType.CONFIGURATION, path); + + try { + // TODO once the APIs are asynchronous use just Futures.transform + final Optional> optional = read.checkedGet(); + + if (!optional.isPresent()) { + return Optional.absent(); + } + + final NormalizedNode data = optional.get(); + final Map.Entry, DataObject> entry = serializer.fromNormalizedNode(path, data); + + return Optional.of(entry.getValue()); + } catch (ReadFailedException e) { + throw new IllegalStateException("Unable to perform read", e); + } + } + + @Override + public Optional readAfter(@Nonnull final InstanceIdentifier currentId) { + return read(currentId, afterTx); + } + + @Override + public Context getContext() { + return ctx; + } + + /** + * Does not close the transactions + */ + @Override + public void close() { + ctx.close(); + } +} -- cgit 1.2.3-korg