From 9f6b16d6e8ade6dfa40e9bbf4196d55adf8f2fec Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Wed, 29 Jun 2016 09:14:51 +0200 Subject: HONEYCOMB-94 Reimplement writer registry with better ordering options Now the registry is flat and allows for full control of writer execution order Change-Id: I864e1d676588ffe59b596145e0829e81b1a1ed2f Signed-off-by: Maros Marsalek --- .../v3po/translate/write/ChildWriter.java | 66 ----------- .../v3po/translate/write/DataObjectUpdate.java | 114 ++++++++++++++++++ .../translate/write/ModifiableWriterRegistry.java | 78 ++++++++++++ .../v3po/translate/write/WriterFactory.java | 28 +++++ .../v3po/translate/write/WriterRegistry.java | 131 +++++++++++++++++---- .../translate/write/WriterRegistryBuilder.java | 25 ++++ .../translate-api/src/main/yang/translate-api.yang | 11 +- 7 files changed, 362 insertions(+), 91 deletions(-) delete mode 100644 v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java create mode 100644 v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java create mode 100644 v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java create mode 100644 v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java create mode 100644 v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java (limited to 'v3po/translate-api/src/main') diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java deleted file mode 100644 index b38f26983..000000000 --- a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java +++ /dev/null @@ -1,66 +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.v3po.translate.write; - -import com.google.common.annotations.Beta; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Child writer allowing its parent to pass the builder object - * - * @param Specific DataObject derived type, that is handled by this writer - */ -@Beta -public interface ChildWriter extends Writer { - - /** - * Extract data object managed by this writer from parent data and perform write. - * - * @param parentId Id of parent node - * @param parentDataAfter Parent data from modification to extract data object from - * @param ctx Write context for current modification - */ - void writeChild(@Nonnull final InstanceIdentifier parentId, - @Nonnull final DataObject parentDataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException; - - /** - * Extract data object managed by this writer(if necessary) from parent data and perform delete. - * - * @param parentId Id of parent node - * @param parentDataBefore Parent data before modification to extract data object from - * @param ctx Write context for current modification - */ - void deleteChild(@Nonnull final InstanceIdentifier parentId, - @Nonnull final DataObject parentDataBefore, - @Nonnull final WriteContext ctx) throws WriteFailedException; - - /** - * Extract data object managed by this writer(if necessary) from parent data and perform delete. - * - * @param parentId Id of parent node - * @param parentDataBefore Parent data before modification to extract data object from - * @param parentDataAfter Parent data from modification to extract data object from - * @param ctx Write context for current modification - */ - void updateChild(@Nonnull final InstanceIdentifier parentId, - @Nonnull final DataObject parentDataBefore, - @Nonnull final DataObject parentDataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException; -} diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java new file mode 100644 index 000000000..0d891ecba --- /dev/null +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java @@ -0,0 +1,114 @@ +/* + * 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.write; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Simple wrapper for BA id + data before and after state. Does not allow both before and after to be null. + */ +public class DataObjectUpdate { + + @Nonnull + private final InstanceIdentifier id; + @Nullable + private final DataObject dataBefore; + @Nullable + private final DataObject dataAfter; + + private DataObjectUpdate(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter) { + this.id = checkNotNull(id); + this.dataAfter = dataAfter; + this.dataBefore = dataBefore; + } + + public DataObject getDataBefore() { + return dataBefore; + } + + public DataObject getDataAfter() { + return dataAfter; + } + + public InstanceIdentifier getId() { + return id; + } + + public static DataObjectUpdate create(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter) { + checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null"); + if (dataBefore != null) { + checkArgument(id.getTargetType().isAssignableFrom(dataBefore.getClass())); + } + if (dataAfter != null) { + checkArgument(id.getTargetType().isAssignableFrom(dataAfter.getClass())); + } + + return dataAfter == null + ? new DataObjectDelete(id, dataBefore) + : new DataObjectUpdate(id, dataBefore, dataAfter); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final DataObjectUpdate that = (DataObjectUpdate) o; + + return id.equals(that.id); + + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String toString() { + return "DataObjectUpdate{" + "id=" + id + + ", dataBefore=" + dataBefore + + ", dataAfter=" + dataAfter + + '}'; + } + + public DataObjectUpdate reverse() { + return DataObjectUpdate.create(id, dataAfter, dataBefore); + } + + public static class DataObjectDelete extends DataObjectUpdate { + + private DataObjectDelete(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore) { + super(id, dataBefore, null); + } + } +} diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java new file mode 100644 index 000000000..71ecbb806 --- /dev/null +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java @@ -0,0 +1,78 @@ +/* + * 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.write; + +import com.google.common.annotations.Beta; +import java.util.Collection; +import java.util.Set; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Mutable registry that allows adding new writers. + */ +@Beta +public interface ModifiableWriterRegistry { + + /** + * Add a writer responsible for writing only a single complex node. + */ + ModifiableWriterRegistry addWriter(@Nonnull Writer writer); + + /** + * Add a writer responsible for writing multiple complex nodes within a subtree its responsible for. + * Identifiers for subtree nodes handled by a single writer have to be relative from {@link DataObject} that + * represents subtree root. + */ + ModifiableWriterRegistry addSubtreeWriter(@Nonnull Set> handledChildren, + @Nonnull Writer writer); + + /** + * Add a writer and make sure it will be executed before writer identifier by relatedType is executed. + */ + ModifiableWriterRegistry addWriterBefore(@Nonnull Writer writer, + @Nonnull InstanceIdentifier relatedType); + + ModifiableWriterRegistry addSubtreeWriterBefore(@Nonnull Set> handledChildren, + @Nonnull Writer writer, + @Nonnull InstanceIdentifier relatedType); + + ModifiableWriterRegistry addWriterBefore(@Nonnull Writer writer, + @Nonnull Collection> relatedTypes); + + ModifiableWriterRegistry addSubtreeWriterBefore(@Nonnull Set> handledChildren, + @Nonnull Writer writer, + @Nonnull Collection> relatedTypes); + + /** + * Add a writer and make sure it will be executed after writer identifier by relatedType is executed. + */ + ModifiableWriterRegistry addWriterAfter(@Nonnull Writer writer, + @Nonnull InstanceIdentifier relatedType); + + ModifiableWriterRegistry addSubtreeWriterAfter(@Nonnull Set> handledChildren, + @Nonnull Writer writer, + @Nonnull InstanceIdentifier relatedType); + + ModifiableWriterRegistry addWriterAfter(@Nonnull Writer writer, + @Nonnull Collection> relatedTypes); + + ModifiableWriterRegistry addSubtreeWriterAfter(@Nonnull Set> handledChildren, + @Nonnull Writer writer, + @Nonnull Collection> relatedTypes); +} diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java new file mode 100644 index 000000000..4287964db --- /dev/null +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java @@ -0,0 +1,28 @@ +/* + * 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.write; + +import com.google.common.annotations.Beta; + +@Beta +public interface WriterFactory { + + /** + * Initialize 1 or more writers and add them to provided registry. + */ + void init(ModifiableWriterRegistry registry); +} diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java index d30f06d13..64735017f 100644 --- a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java @@ -19,47 +19,131 @@ package io.fd.honeycomb.v3po.translate.write; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import io.fd.honeycomb.v3po.translate.TranslationException; -import java.util.List; -import java.util.Map; +import java.util.Set; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; /** - * Special {@link Writer} capable of performing bulk updates + * Special {@link Writer} capable of performing bulk updates. */ @Beta public interface WriterRegistry extends Writer { /** - * Performs bulk update + * Performs bulk update. * * @throws BulkUpdateException in case bulk update fails - * @throws TranslationException in case some other error occurs while processing update request + * @throws TranslationException in case some other error occurs while processing update request */ - void update(@Nonnull final Map, DataObject> dataBefore, - @Nonnull final Map, DataObject> dataAfter, - @Nonnull final WriteContext ctx) throws TranslationException; + void update(@Nonnull DataObjectUpdates updates, + @Nonnull WriteContext ctx) throws TranslationException; + + /** + * Simple DTO containing updates for {@link WriterRegistry}. Currently only deletes and updates (create + update) + * are distinguished. + */ + @Beta + final class DataObjectUpdates { + + private final Multimap, DataObjectUpdate> updates; + private final Multimap, DataObjectUpdate.DataObjectDelete> deletes; + + /** + * Create new instance. + * + * @param updates All updates indexed by their unkeyed {@link InstanceIdentifier} + * @param deletes All deletes indexed by their unkeyed {@link InstanceIdentifier} + */ + public DataObjectUpdates(@Nonnull final Multimap, DataObjectUpdate> updates, + @Nonnull final Multimap, DataObjectUpdate.DataObjectDelete> deletes) { + this.deletes = deletes; + this.updates = updates; + } + + public Multimap, DataObjectUpdate> getUpdates() { + return updates; + } + + public Multimap, DataObjectUpdate.DataObjectDelete> getDeletes() { + return deletes; + } + + public boolean isEmpty() { + return updates.isEmpty() && deletes.isEmpty(); + } + + @Override + public String toString() { + return "DataObjectUpdates{" + "updates=" + updates + ", deletes=" + deletes + '}'; + } + + /** + * Get a {@link Set} containing all update types from both updates as well as deletes. + */ + public Set> getTypeIntersection() { + return Sets.union(deletes.keySet(), updates.keySet()); + } + + /** + * Check whether there is only a single type of data object to be updated. + * + * @return true if there is only a single type of updates (update + delete) + */ + public boolean containsOnlySingleType() { + return getTypeIntersection().size() == 1; + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + + final DataObjectUpdates that = (DataObjectUpdates) other; + + if (!updates.equals(that.updates)) { + return false; + } + return deletes.equals(that.deletes); + + } + + @Override + public int hashCode() { + int result = updates.hashCode(); + result = 31 * result + deletes.hashCode(); + return result; + } + + } /** * Thrown when bulk update failed. */ @Beta - class BulkUpdateException extends WriteFailedException { + class BulkUpdateException extends TranslationException { private final Reverter reverter; + private final Set> failedIds; /** * Constructs an BulkUpdateException. - * - * @param failedId instance identifier of the data object that caused bulk update to fail. - * @param cause the cause of bulk update failure + * @param failedIds instance identifiers of the data objects that were not processed during bulk update. + * @param cause the cause of bulk update failure */ - public BulkUpdateException(@Nonnull final InstanceIdentifier failedId, @Nonnull final Reverter reverter, - final Throwable cause) { - super(failedId, "Bulk update failed at " + failedId, cause); + public BulkUpdateException(@Nonnull final Set> failedIds, + @Nonnull final Reverter reverter, + @Nonnull final Throwable cause) { + super("Bulk update failed at: " + failedIds, cause); + this.failedIds = failedIds; this.reverter = checkNotNull(reverter, "reverter should not be null"); } @@ -72,10 +156,13 @@ public interface WriterRegistry extends Writer { reverter.revert(); } + public Set> getFailedIds() { + return failedIds; + } } /** - * Abstraction over revert mechanism in case of a bulk update failure + * Abstraction over revert mechanism in case of a bulk update failure. */ @Beta interface Reverter { @@ -95,19 +182,19 @@ public interface WriterRegistry extends Writer { class RevertFailedException extends TranslationException { // TODO change to list of VppDataModifications to make debugging easier - private final List> notRevertedChanges; + private final Set> notRevertedChanges; /** - * Constructs an RevertFailedException with the list of changes that were not reverted. + * Constructs a RevertFailedException with the list of changes that were not reverted. * * @param notRevertedChanges list of changes that were not reverted * @param cause the cause of revert failure */ - public RevertFailedException(@Nonnull final List> notRevertedChanges, + public RevertFailedException(@Nonnull final Set> notRevertedChanges, final Throwable cause) { super(cause); checkNotNull(notRevertedChanges, "notRevertedChanges should not be null"); - this.notRevertedChanges = ImmutableList.copyOf(notRevertedChanges); + this.notRevertedChanges = ImmutableSet.copyOf(notRevertedChanges); } /** @@ -116,7 +203,7 @@ public interface WriterRegistry extends Writer { * @return list of changes that were not reverted */ @Nonnull - public List> getNotRevertedChanges() { + public Set> getNotRevertedChanges() { return notRevertedChanges; } } diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java new file mode 100644 index 000000000..55ef66ec6 --- /dev/null +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java @@ -0,0 +1,25 @@ +/* + * 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.write; + +/** + * Builder for writer registries. + */ +public interface WriterRegistryBuilder { + + WriterRegistry build(); +} diff --git a/v3po/translate-api/src/main/yang/translate-api.yang b/v3po/translate-api/src/main/yang/translate-api.yang index 4a8093ab8..414ee20e1 100644 --- a/v3po/translate-api/src/main/yang/translate-api.yang +++ b/v3po/translate-api/src/main/yang/translate-api.yang @@ -24,14 +24,19 @@ module translate-api { config:java-class io.fd.honeycomb.v3po.translate.read.ReaderRegistry; } - identity honeycomb-writer { + identity honeycomb-writer-factory { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.write.Writer; + config:java-class io.fd.honeycomb.v3po.translate.write.WriterFactory; } identity honeycomb-writer-registry { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.translate.write.WriterRegistry; + config:java-class io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry; + } + + identity honeycomb-writer-registry-builder { + base "config:service-type"; + config:java-class io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder; } identity honeycomb-mapping-context { -- cgit 1.2.3-korg