From d9fdeeabb54138867c33e674215e1a04a4c21292 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 1 Aug 2014 16:19:29 +0200 Subject: [PATCH] BUG-997: Evolve the SchemaRegistry concepts This is the next step in SchemaRegistry evolution. Introduces SchemaSourceListener and abstract classes for cache and transformer patterns. Also completes implementation of the the abstract schema registry and converts URLSchemaContextResolver. Finally it marks the APIs as Beta, since they are being proven until we certify NETCONF completely. Change-Id: If82522c9a9a1913c818146da1247c8c5ca79b9c5 Signed-off-by: Robert Varga --- .../repo/api/AcceptingSchemaSourceFilter.java | 47 --- .../api/MissingSchemaSourceException.java | 26 ++ .../model/repo/api/SchemaContextFactory.java | 2 + .../yang/model/repo/api/SchemaRepository.java | 6 + .../repo/api/SchemaResolutionException.java | 44 ++- .../SchemaSourceException.java} | 14 +- .../model/repo/api/SchemaSourceFilter.java | 46 +++ .../repo/api/SchemaSourceRepresentation.java | 3 + .../yang/model/repo/api/SourceIdentifier.java | 5 +- .../model/repo/api/YangTextSchemaSource.java | 11 + .../yang/model/repo/api/YinSchemaSource.java | 3 + .../model/repo/spi/PotentialSchemaSource.java | 122 ++++++++ ...n.java => SchemaListenerRegistration.java} | 13 +- .../model/repo/spi/SchemaSourceListener.java | 48 +++ .../model/repo/spi/SchemaSourceProvider.java | 14 +- .../repo/spi/SchemaSourceRegistration.java | 10 +- .../model/repo/spi/SchemaSourceRegistry.java | 21 +- .../repo/spi/SchemaSourceTransformer.java | 54 ---- .../AbstractSchemaListenerRegistration.java | 18 ++ .../repo/util/AbstractSchemaRepository.java | 192 ++++++------ .../repo/util/AbstractSchemaSourceCache.java | 82 +++++ .../AbstractSchemaSourceRegistration.java | 11 +- ...AbstractSchemaTransformerRegistration.java | 19 -- .../repo/util/InMemorySchemaSourceCache.java | 68 +++++ .../repo/util/RefcountedRegistration.java | 36 +++ .../repo/util/SchemaSourceTransformer.java | 102 +++++++ .../parser/impl/YangParserListenerImpl.java | 22 ++ .../parser/repo/AbstractURLRegistration.java | 17 ++ .../yang/parser/repo/DependencyResolver.java | 2 + .../repo/SharedSchemaContextFactory.java | 138 +++++++++ .../parser/repo/SharedSchemaRepository.java | 60 ++++ .../yang/parser/repo/URLRegistration.java | 16 + .../parser/repo/URLSchemaContextResolver.java | 288 ++++++------------ .../yang/parser/util/ASTSchemaSource.java | 22 ++ .../parser/util/TextToASTTransformer.java | 100 +++--- 35 files changed, 1146 insertions(+), 536 deletions(-) delete mode 100644 yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java create mode 100644 yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java rename yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/{spi/SchemaSourceTransformationException.java => api/SchemaSourceException.java} (53%) create mode 100644 yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java rename yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/{SchemaTransformerRegistration.java => SchemaListenerRegistration.java} (58%) create mode 100644 yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java delete mode 100644 yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java create mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java create mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java delete mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java create mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java create mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java create mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java create mode 100644 yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java create mode 100644 yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java create mode 100644 yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java create mode 100644 yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java deleted file mode 100644 index e101d9de54..0000000000 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/eplv10.html - */ -package org.opendaylight.yangtools.yang.model.repo.api; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - -/** - * A {@link SchemaSourceFilter} which accepts any schema source it is presented with. - */ -public final class AcceptingSchemaSourceFilter implements SchemaSourceFilter { - private static final AcceptingSchemaSourceFilter INSTANCE = new AcceptingSchemaSourceFilter(); - - private final Iterable> representations; - - private AcceptingSchemaSourceFilter() { - final Builder> b = ImmutableList.builder(); - b.add(SchemaSourceRepresentation.class); - representations = b.build(); - } - - /** - * Return the singleton instance of this filter. - * - * @return Singleton shared instance. - */ - public static final AcceptingSchemaSourceFilter getSingletonInstance() { - return INSTANCE; - } - - @Override - public Iterable> supportedRepresentations() { - return representations; - } - - @Override - public ListenableFuture apply(final SchemaSourceRepresentation schemaSource) { - return Futures.immediateFuture(Boolean.TRUE); - } -} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java new file mode 100644 index 0000000000..17c9c90617 --- /dev/null +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/eplv10.html + */ +package org.opendaylight.yangtools.yang.model.repo.api; + +import com.google.common.annotations.Beta; + +/** + * Exception thrown when a the specified schema source is not available. + */ +@Beta +public class MissingSchemaSourceException extends SchemaSourceException { + private static final long serialVersionUID = 1L; + + public MissingSchemaSourceException(final String message) { + super(message); + } + + public MissingSchemaSourceException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java index 8869c9eb3d..2597e9c185 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java @@ -7,6 +7,7 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import com.google.common.annotations.Beta; import com.google.common.util.concurrent.CheckedFuture; import java.util.Collection; @@ -20,6 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; * based on a specification of what {@link SourceIdentifier}s are required * and dynamic recursive resolution. */ +@Beta public interface SchemaContextFactory { /** * Create a new schema context containing specified sources, pulling in diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java index 807bbf05de..73569d384d 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java @@ -7,6 +7,9 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import com.google.common.annotations.Beta; +import com.google.common.util.concurrent.CheckedFuture; + import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -15,6 +18,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; * Interface exposed by repository implementations. A schema repository is a logically * centralized place for model storage and creation of {@link SchemaContext} instances. */ +@Beta public interface SchemaRepository { /** * Instantiate a new {@link SchemaContextFactory}, which will filter available schema @@ -26,4 +30,6 @@ public interface SchemaRepository { * @return A new schema context factory. */ SchemaContextFactory createSchemaContextFactory(@Nonnull SchemaSourceFilter filter); + + CheckedFuture getSchemaSource(@Nonnull SourceIdentifier id, @Nonnull Class represetation); } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java index b47e366a79..e0be4adb79 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java @@ -7,38 +7,48 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import com.google.common.annotations.Beta; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; -import java.util.Map; +import java.util.Collection; +import java.util.Collections; import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.model.api.ModuleImport; + /** * Exception thrown when a Schema Source fails to resolve. */ -public class SchemaResolutionException extends Exception { +@Beta +public class SchemaResolutionException extends SchemaSourceException { private static final long serialVersionUID = 1L; - private final Map unresolvedSources; + private final Multimap unsatisfiedImports; + private final Collection resolvedSources; public SchemaResolutionException(final @Nonnull String message) { this(message, (Throwable)null); } public SchemaResolutionException(final @Nonnull String message, final Throwable cause) { - this(message, cause, ImmutableMap.of()); + this(message, cause, Collections.emptySet(), ImmutableMultimap.of()); } - public SchemaResolutionException(final @Nonnull String message, final @Nonnull Map unresolvedSources) { - super(Preconditions.checkNotNull(message)); - this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources); + public SchemaResolutionException(final @Nonnull String message, final Collection resolvedSources, + final @Nonnull Multimap unsatisfiedImports) { + this(message, null, Collections.emptySet(), unsatisfiedImports); } - public SchemaResolutionException(final @Nonnull String message, final Throwable cause, @Nonnull final Map unresolvedSources) { + public SchemaResolutionException(final @Nonnull String message, final Throwable cause, + @Nonnull final Collection resolvedSources, + @Nonnull final Multimap unsatisfiedImports) { super(message, cause); - this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources); + this.unsatisfiedImports = ImmutableMultimap.copyOf(unsatisfiedImports); + this.resolvedSources = ImmutableList.copyOf(resolvedSources); } /** @@ -47,13 +57,19 @@ public class SchemaResolutionException extends Exception { * * @return Source/reason map. */ - public final Map getUnresolvedSources() { - return unresolvedSources; + public final Multimap getUnsatisfiedImports() { + return unsatisfiedImports; + } + + + // FIXME: should be leak actual mapping? + public final Collection getResolvedSources() { + return resolvedSources; } @Override public final String toString() { - return addToStringAttributes(Objects.toStringHelper(this).add("unresolvedSources", unresolvedSources)).toString(); + return addToStringAttributes(Objects.toStringHelper(this).add("unsatisfiedImports", unsatisfiedImports)).toString(); } protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java similarity index 53% rename from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java rename to yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java index b5c08dee05..d31da32ac4 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java @@ -5,20 +5,22 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/eplv10.html */ -package org.opendaylight.yangtools.yang.model.repo.spi; +package org.opendaylight.yangtools.yang.model.repo.api; + +import com.google.common.annotations.Beta; /** - * Exception thrown when a failure to translate a schema source between - * representations. + * Exception thrown when a failure to acquire a schema source occurs. */ -public class SchemaSourceTransformationException extends Exception { +@Beta +public class SchemaSourceException extends Exception { private static final long serialVersionUID = 1L; - public SchemaSourceTransformationException(final String message) { + public SchemaSourceException(final String message) { super(message); } - public SchemaSourceTransformationException(final String message, final Throwable cause) { + public SchemaSourceException(final String message, final Throwable cause) { super(message, cause); } } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java index 188f83c0c5..f881900754 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java @@ -7,9 +7,55 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import com.google.common.annotations.Beta; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import java.util.Collections; + +/* + * A filter of schema sources. This is used to restrict which sources representation + * instances are allowed to participate in construction of a schema context. This + * allows, for example, to create an non-shared island, or require the sources to + * be certified before use. + */ +@Beta public interface SchemaSourceFilter { + /** + * A {@link SchemaSourceFilter} which accepts any schema source it is presented with. + */ + public static final SchemaSourceFilter ALWAYS_ACCEPT = new SchemaSourceFilter() { + private final Iterable> REPRESENTATIONS = + Collections.>singletonList(SchemaSourceRepresentation.class); + + @Override + public Iterable> supportedRepresentations() { + return REPRESENTATIONS; + } + + @Override + public ListenableFuture apply(final SchemaSourceRepresentation schemaSource) { + return Futures.immediateFuture(Boolean.TRUE); + } + }; + + /** + * Get the representations this filter supports. A schema source is translated + * into one of these representations before it is presented for filtering. + * + * @return Set of supported representations. + */ Iterable> supportedRepresentations(); + + /** + * Check if a particular schema source is acceptable to the filter. The process + * of checking may be asynchronous, but at some point it needs to produce an + * affirmative or negative answer before the schema context construction can + * proceed. + * + * @param schemaSource Schema source to be filtered + * @return Promise of a filtering decision. The result should be {@link Boolean#TRUE} + * if the source is acceptable. + */ ListenableFuture apply(SchemaSourceRepresentation schemaSource); } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java index b1261a9cd4..90fc5ae740 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java @@ -7,6 +7,8 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import com.google.common.annotations.Beta; + import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.concepts.Immutable; @@ -35,6 +37,7 @@ import org.opendaylight.yangtools.concepts.Immutable; * Implementations of this interface expected to comply with the {@link Immutable} * contract. */ +@Beta public interface SchemaSourceRepresentation extends Identifiable, Immutable { /** * {@inheritDoc} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java index cd3a0fbefc..58b011c368 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java @@ -7,6 +7,7 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -14,7 +15,6 @@ import org.opendaylight.yangtools.concepts.Identifier; import org.opendaylight.yangtools.concepts.Immutable; /** - * * YANG Schema source identifier * * Simple transfer object represents identifier of source for YANG schema (module or submodule), @@ -34,9 +34,8 @@ import org.opendaylight.yangtools.concepts.Immutable; *

* (For further reference see: http://tools.ietf.org/html/rfc6020#section-5.2 and * http://tools.ietf.org/html/rfc6022#section-3.1 ). - * - * */ +@Beta public final class SourceIdentifier implements Identifier, Immutable { private static final long serialVersionUID = 1L; private final String revision; diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java index be52a90f77..50ee12ef4d 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java @@ -7,8 +7,12 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.Beta; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.io.ByteSource; @@ -21,6 +25,7 @@ import org.opendaylight.yangtools.concepts.Delegator; * YANG text schema source representation. Exposes an RFC6020 text representation * as an {@link InputStream}. */ +@Beta public abstract class YangTextSchemaSource extends ByteSource implements SchemaSourceRepresentation { private final SourceIdentifier identifier; @@ -28,6 +33,12 @@ public abstract class YangTextSchemaSource extends ByteSource implements SchemaS this.identifier = Preconditions.checkNotNull(identifier); } + public static SourceIdentifier identifierFromFilename(final String name) { + checkArgument(name.endsWith(".yang"), "Filename %s does not have a .yang extension", name); + // FIXME: add revision-awareness + return SourceIdentifier.create(name.substring(0, name.length() - 5), Optional.absent()); + } + /** * {@inheritDoc} */ diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java index b67aae6289..f09f525533 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java @@ -7,12 +7,15 @@ */ package org.opendaylight.yangtools.yang.model.repo.api; +import com.google.common.annotations.Beta; + import org.w3c.dom.Document; /** * Yin schema source representation. Exposes an RFC6020 YIN XML representation * as an W3C {@link Document}. */ +@Beta public interface YinSchemaSource extends SchemaSourceRepresentation { /** * {@inheritDoc} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java new file mode 100644 index 0000000000..fae44679e2 --- /dev/null +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/eplv10.html + */ +package org.opendaylight.yangtools.yang.model.repo.spi; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; + +/** + * A potential schema source. Instances of this class track the various + * representations of a schema source and the cost attached to obtaining + * the source from them. + */ +@Beta +public final class PotentialSchemaSource { + /** + * Each registered source has a cost associated with it. Since a particular + * representation can be acquired by various means, here are general constants + * for common cases. + */ + public enum Costs { + /** + * The source is immediately available, via a lookup or similar. + */ + IMMEDIATE(0), + /** + * The source is available via a computation. For transformation-type + * computation, the cost of acquiring the cost needs to be added, too. + */ + COMPUTATION(1), + /** + * The source is available by performing local IO, such that reading + * from a disk. + */ + LOCAL_IO(4), + /** + * The source is available by performing remote IO, such as fetching + * from an HTTP server or similar. + */ + REMOTE_IO(8); + + private final int value; + + private Costs(final int value) { + this.value = value; + } + + /** + * The the cost value. + * + * @return Const constant. + */ + public int getValue() { + return value; + } + } + + private final Class representation; + private final SourceIdentifier sourceIdentifier; + private final int cost; + + private PotentialSchemaSource(final SourceIdentifier sourceIdentifier, final Class representation, final int cost) { + this.representation = Preconditions.checkNotNull(representation); + this.sourceIdentifier = Preconditions.checkNotNull(sourceIdentifier); + Preconditions.checkArgument(cost >= 0, "cost has to be non-negative"); + this.cost = cost; + } + + public static final PotentialSchemaSource create(final SourceIdentifier sourceIdentifier, final Class representation, final int cost) { + return new PotentialSchemaSource<>(sourceIdentifier, representation, cost); + } + + public SourceIdentifier getSourceIdentifier() { + return sourceIdentifier; + } + + public Class getRepresentation() { + return representation; + } + + public int getCost() { + return cost; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + cost; + result = prime * result + representation.hashCode(); + result = prime * result + sourceIdentifier.hashCode(); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PotentialSchemaSource)) { + return false; + } + final PotentialSchemaSource other = (PotentialSchemaSource) obj; + if (cost != other.cost) { + return false; + } + if (!representation.equals(other.representation)) { + return false; + } + if (!sourceIdentifier.equals(other.sourceIdentifier)) { + return false; + } + return true; + } +} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java similarity index 58% rename from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java rename to yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java index e64deee221..3e970949f5 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java @@ -7,9 +7,14 @@ */ package org.opendaylight.yangtools.yang.model.repo.spi; -import org.opendaylight.yangtools.concepts.ObjectRegistration; +import com.google.common.annotations.Beta; + +import org.opendaylight.yangtools.concepts.ListenerRegistration; + +/** + * Registration of a SchemaSourceListener. + */ +@Beta +public interface SchemaListenerRegistration extends ListenerRegistration { -public interface SchemaTransformerRegistration extends ObjectRegistration> { - @Override - void close(); } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java new file mode 100644 index 0000000000..b4f55a812e --- /dev/null +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/eplv10.html + */ +package org.opendaylight.yangtools.yang.model.repo.spi; + +import com.google.common.annotations.Beta; + +import java.util.EventListener; + +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; + +/** + * Listener for schema source lifecycle events. + */ +@Beta +public interface SchemaSourceListener extends EventListener { + /** + * Invoked when the registry sees a concrete source. This callback is typically + * used by cache-type listeners, who intercept the source, store it locally and + * announce themselves as a provider of that particular schema source. + * + * @param source Schema source + */ + void schemaSourceEncountered(SchemaSourceRepresentation source); + + /** + * Invoked when a new schema source is registered by a provider. This call + * callback, along with {@link #schemaSourceUnregistered(PotentialSchemaSource)} + * is typically used by transformer-type listeners, who intercept the registration + * if the advertised representation matches their input type and register + * themselves as a potential provider of the same source in their output + * representation type. + * + * @param sources Newly available sources + */ + void schemaSourceRegistered(Iterable> sources); + + /** + * Invoked when a schema source is unregistered. + * + * @param source Schema source representation + */ + void schemaSourceUnregistered(PotentialSchemaSource source); +} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java index a0a141bd37..70ad6813e8 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java @@ -8,9 +8,10 @@ package org.opendaylight.yangtools.yang.model.repo.spi; import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.CheckedFuture; +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; @@ -29,10 +30,9 @@ public interface SchemaSourceProvider { * *

    *
  • If the source identifier specifies a revision, this method returns either - * a representation of that particular revision, or report the identifier as absent - * by returning {@link Optional#absent()}. + * a representation of that particular revision or throw {@link MissingSchemaSourceException}. *
  • If the source identifier does not specify a revision, this method returns - * the newest available revision, or {@link Optional#absent()}. + * the newest available revision, or throws {@link MissingSchemaSourceException}. * * In either case the returned representation is required to report a non-null * revision in the {@link SourceIdentifier} returned from @@ -43,7 +43,7 @@ public interface SchemaSourceProvider { * * @param sourceIdentifier source identifier * @return source representation if supplied YANG module is available - * {@link Optional#absent()} otherwise. + * */ - ListenableFuture> getSource(SourceIdentifier sourceIdentifier); + CheckedFuture getSource(SourceIdentifier sourceIdentifier); } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java index 3c3c5fef77..6937c53e98 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java @@ -7,10 +7,16 @@ */ package org.opendaylight.yangtools.yang.model.repo.spi; +import com.google.common.annotations.Beta; + import org.opendaylight.yangtools.concepts.ObjectRegistration; -import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; -public interface SchemaSourceRegistration extends ObjectRegistration { +/** + * Registration of a schema source. + */ +@Beta +public interface SchemaSourceRegistration extends ObjectRegistration> { @Override void close(); } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java index f9acf3d456..075fcb5382 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java @@ -7,6 +7,8 @@ */ package org.opendaylight.yangtools.yang.model.repo.spi; +import com.google.common.annotations.Beta; + import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; @@ -17,29 +19,28 @@ import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; * {@link SchemaSourceProvider} instances which would then acquire the schema * source. */ +@Beta public interface SchemaSourceRegistry { /** * Register a new schema source which is potentially available from a provider. * A registration does not guarantee that a subsequent call to * {@link SchemaSourceProvider#getSource(SourceIdentifier)} will succeed. * - * @param identifier Schema source identifier * @param provider Resolver which can potentially resolve the identifier - * @param representation Schema source representation which the source may - * be available. + * @param source Schema source details * @return A registration handle. Invoking {@link SchemaSourceRegistration#close()} * will cancel the registration. */ - SchemaSourceRegistration registerSchemaSource( - SourceIdentifier identifier, SchemaSourceProvider provider, Class representation); + SchemaSourceRegistration registerSchemaSource(SchemaSourceProvider provider, PotentialSchemaSource source); /** - * Register a schema transformer. The registry can invoke it to transform between - * the various schema source formats. + * Register a schema source listener. The listener will be notified as new + * sources and their representations become available, subject to the provided + * filter. * - * @param transformer Schema source transformer - * @return A registration handle. Invoking {@link SchemaTransformerRegistration#close()} + * @param listener Schema source listener + * @return A registration handle. Invoking {@link SchemaListenerRegistration#close()} * will cancel the registration. */ - SchemaTransformerRegistration registerSchemaSourceTransformer(SchemaSourceTransformer transformer); + SchemaListenerRegistration registerSchemaSourceListener(SchemaSourceListener listener); } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java deleted file mode 100644 index 13d309ed3f..0000000000 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/eplv10.html - */ -package org.opendaylight.yangtools.yang.model.repo.spi; - -import com.google.common.util.concurrent.CheckedFuture; - -import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; - -/** - * An schema source representation transformation service. An instance can create - * some output schema source representation based on some input source representation. - * - * @param Input {@link SchemaSourceRepresentation} - * @param Output {@link SchemaSourceRepresentation} - */ -public interface SchemaSourceTransformer { - /** - * Return the {@link SchemaSourceRepresentation} which this transformer - * accepts on its input. - * - * @return The input source representation type. - */ - Class getInputRepresentation(); - - /** - * Return the {@link SchemeSourceRepresentation} which this transformer - * produces on its output. - * - * @return The output source representation type. - */ - Class getOutputRepresentation(); - - /** - * Transform a schema source representation from its input form to - * the transformers output form. - * - * @param source Schema source in its source representation - * @return A future which produces the output schema source representation. - */ - CheckedFuture transformSchemaSource(I source); - - /** - * Return the relative cost of performing the transformation. When in doubt, - * return 1. - * - * @return Relative cost. - */ - int getCost(); -} diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java new file mode 100644 index 0000000000..e720f8db25 --- /dev/null +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/eplv10.html + */ +package org.opendaylight.yangtools.yang.model.repo.util; + +import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; + +public abstract class AbstractSchemaListenerRegistration extends AbstractListenerRegistration implements SchemaListenerRegistration { + protected AbstractSchemaListenerRegistration(final SchemaSourceListener listener) { + super(listener); + } +} diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java index 4dd30b90a9..53563fde69 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java @@ -7,155 +7,122 @@ */ package org.opendaylight.yangtools.yang.model.repo.util; -import com.google.common.base.Optional; +import com.google.common.annotations.Beta; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureFallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import javax.annotation.concurrent.GuardedBy; + +import org.opendaylight.yangtools.util.concurrent.ExceptionMapper; +import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper; +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry { +/** + * Abstract base class for {@link SchemaRepository} implementations. It handles registration + * and lookup of schema sources, subclasses need only to provide their own + * {@link #createSchemaContextFactory(SchemaSourceFilter)} implementation. + */ +@Beta +public abstract class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry { private static final Logger LOG = LoggerFactory.getLogger(AbstractSchemaRepository.class); - private static final Comparator TRANSFORMER_COST_COMPARATOR = new Comparator() { - @Override - public int compare(final SchemaTransformerRegistration o1, final SchemaTransformerRegistration o2) { - return o1.getInstance().getCost() - o2.getInstance().getCost(); - } - }; + private static final ExceptionMapper FETCH_MAPPER = ReflectiveExceptionMapper.create("Schema source fetch", SchemaSourceException.class); /* - * Output-type -> transformer map. Our usage involves knowing the destination type, - * so we have to work backwards and find a transformer chain which will get us - * to that representation given our available sources. + * Source identifier -> representation -> provider map. We usually are looking for + * a specific representation of a source. */ - private final Multimap, SchemaTransformerRegistration> transformers = - HashMultimap.create(); + @GuardedBy("this") + private final Map, AbstractSchemaSourceRegistration>> sources = new HashMap<>(); /* - * Source identifier -> representation -> provider map. We usually are looking for - * a specific representation a source. + * Schema source listeners. */ - private final Map, AbstractSchemaSourceRegistration>> sources = new HashMap<>(); + @GuardedBy("this") + private final Collection listeners = new ArrayList<>(); + private static final CheckedFuture fetchSource(final SourceIdentifier id, final Iterator> it) { + final AbstractSchemaSourceRegistration reg = it.next(); - private static final ListenableFuture> fetchSource(final SourceIdentifier id, final Iterator it) { - if (!it.hasNext()) { - return Futures.immediateFuture(Optional.absent()); - } - - return Futures.transform(((SchemaSourceProvider)it.next().getProvider()).getSource(id), new AsyncFunction, Optional>() { + @SuppressWarnings("unchecked") + final CheckedFuture f = ((SchemaSourceProvider)reg.getProvider()).getSource(id); + return Futures.makeChecked(Futures.withFallback(f, new FutureFallback() { @Override - public ListenableFuture> apply(final Optional input) throws Exception { - if (input.isPresent()) { - return Futures.immediateFuture(input); - } else { + public ListenableFuture create(final Throwable t) throws SchemaSourceException { + LOG.debug("Failed to acquire source from {}", reg, t); + + if (it.hasNext()) { return fetchSource(id, it); } - } - }); - } - - private ListenableFuture> transformSchemaSource(final SourceIdentifier id, final Class representation) { - final Multimap, AbstractSchemaSourceRegistration> srcs = sources.get(id); - if (srcs.isEmpty()) { - return Futures.immediateFailedFuture(new SchemaSourceTransformationException( - String.format("No providers producing a representation of %s registered", id))); - } - - final Collection ts = transformers.get(representation); - if (ts.isEmpty()) { - return Futures.immediateFailedFuture(new SchemaSourceTransformationException( - String.format("No transformers producing representation %s registered", representation))); - } - // Build up the candidate list - final List candidates = new ArrayList<>(); - for (SchemaTransformerRegistration tr : ts) { - final SchemaSourceTransformer t = tr.getInstance(); - final Class i = t.getInputRepresentation(); - if (srcs.containsKey(i)) { - candidates.add(tr); - } else { - LOG.debug("Provider for {} in {} not found, skipping transfomer {}", id, i, t); + throw new MissingSchemaSourceException("All available providers exhausted"); } - } - - if (candidates.isEmpty()) { - return Futures.immediateFailedFuture(new SchemaSourceTransformationException( - String.format("No matching source/transformer pair for source %s representation %s found", id, representation))); - } - - Collections.sort(candidates, TRANSFORMER_COST_COMPARATOR); - // return transform(candidates.iterator(), id); - return null; + }), FETCH_MAPPER); } - /** - * Obtain a SchemaSource is selected representation - */ - protected ListenableFuture> getSchemaSource(final SourceIdentifier id, final Class representation) { - final Multimap, AbstractSchemaSourceRegistration> srcs = sources.get(id); + @Override + public CheckedFuture getSchemaSource(final SourceIdentifier id, final Class representation) { + final Multimap, AbstractSchemaSourceRegistration> srcs = sources.get(id); if (srcs == null) { - LOG.debug("No providers registered for source {}", id); - return Futures.immediateFuture(Optional.absent()); + return Futures.immediateFailedCheckedFuture(new MissingSchemaSourceException("No providers registered for source" + id)); } - final Collection candidates = srcs.get(representation); - return Futures.transform(AbstractSchemaRepository.fetchSource(id, candidates.iterator()), new AsyncFunction, Optional>() { - @Override - public ListenableFuture> apply(final Optional input) throws Exception { - if (input.isPresent()) { - return Futures.immediateFuture(input); - } - - return transformSchemaSource(id, representation); - } - }); - } + final Iterator> regs = srcs.get(representation).iterator(); + if (!regs.hasNext()) { + return Futures.immediateFailedCheckedFuture( + new MissingSchemaSourceException("No providers for source " + id + " representation " + representation + " available")); + } - @Override - public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) { - // TODO Auto-generated method stub - return null; + return fetchSource(id, regs); } - private void addSource(final SourceIdentifier id, final Class rep, final AbstractSchemaSourceRegistration reg) { - Multimap, AbstractSchemaSourceRegistration> m = sources.get(id); + private synchronized void addSource(final PotentialSchemaSource source, final AbstractSchemaSourceRegistration reg) { + Multimap, AbstractSchemaSourceRegistration> m = sources.get(source.getSourceIdentifier()); if (m == null) { m = HashMultimap.create(); - sources.put(id, m); + sources.put(source.getSourceIdentifier(), m); } - m.put(rep, reg); + m.put(source.getRepresentation(), reg); + + final Collection> reps = Collections.>singleton(source); + for (SchemaListenerRegistration l : listeners) { + l.getInstance().schemaSourceRegistered(reps); + } } - private void removeSource(final SourceIdentifier id, final Class rep, final SchemaSourceRegistration reg) { - final Multimap, AbstractSchemaSourceRegistration> m = sources.get(id); + private synchronized void removeSource(final PotentialSchemaSource source, final SchemaSourceRegistration reg) { + final Multimap, AbstractSchemaSourceRegistration> m = sources.get(source.getSourceIdentifier()); if (m != null) { - m.remove(rep, reg); + m.remove(source.getRepresentation(), reg); + + for (SchemaListenerRegistration l : listeners) { + l.getInstance().schemaSourceUnregistered(source); + } + if (m.isEmpty()) { sources.remove(m); } @@ -163,29 +130,40 @@ public class AbstractSchemaRepository implements SchemaRepository, SchemaSourceR } @Override - public SchemaSourceRegistration registerSchemaSource( - final SourceIdentifier identifier, final SchemaSourceProvider provider, final Class representation) { - final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(identifier, provider) { + public SchemaSourceRegistration registerSchemaSource(final SchemaSourceProvider provider, final PotentialSchemaSource source) { + final AbstractSchemaSourceRegistration ret = new AbstractSchemaSourceRegistration(provider, source) { @Override protected void removeRegistration() { - removeSource(identifier, representation, this); + removeSource(source, this); } }; - addSource(identifier, representation, ret); + addSource(source, ret); return ret; } @Override - public SchemaTransformerRegistration registerSchemaSourceTransformer(final SchemaSourceTransformer transformer) { - final SchemaTransformerRegistration ret = new AbstractSchemaTransformerRegistration(transformer) { + public SchemaListenerRegistration registerSchemaSourceListener(final SchemaSourceListener listener) { + final SchemaListenerRegistration ret = new AbstractSchemaListenerRegistration(listener) { @Override protected void removeRegistration() { - transformers.remove(transformer.getOutputRepresentation(), this); + listeners.remove(this); } }; - transformers.put(transformer.getOutputRepresentation(), ret); + synchronized (this) { + final Collection> col = new ArrayList<>(); + for (Multimap, AbstractSchemaSourceRegistration> m : sources.values()) { + for (AbstractSchemaSourceRegistration r : m.values()) { + col.add(r.getInstance()); + } + } + + // Notify first, so translator-type listeners, who react by registering a source + // do not cause infinite loop. + listener.schemaSourceRegistered(col); + listeners.add(ret); + } return ret; } } diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java new file mode 100644 index 0000000000..498f403f5d --- /dev/null +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.model.repo.util; + +import com.google.common.base.Preconditions; + +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; + +/** + * Abstract base class for cache-type SchemaSourceListeners. It needs to be + * registered with a {@link SchemaSourceRegistry}, where it gets notifications + * from. It performs filtering and {@link #offer(SchemaSourceRepresentation)}s + * conforming sources to the subclass. + * + * @param Cached schema source type. + */ +public abstract class AbstractSchemaSourceCache implements SchemaSourceListener, SchemaSourceProvider { + private final SchemaSourceRegistry consumer; + private final Class representation; + private final Costs cost; + + protected AbstractSchemaSourceCache(final SchemaSourceRegistry consumer, final Class representation, final Costs cost) { + this.consumer = Preconditions.checkNotNull(consumer); + this.representation = Preconditions.checkNotNull(representation); + this.cost = Preconditions.checkNotNull(cost); + } + + /** + * Offer a schema source in requested representation for caching. Subclasses + * need to implement this method to store the schema source. Once they have + * determined to cache the source, they should call {@link #register(SourceIdentifier)}. + * + * @param source schema source + */ + protected abstract void offer(T source); + + /** + * Register the presence of a cached schema source with the consumer. Subclasses + * need to call this method once they have cached a schema source representation, + * or when they have determined they have a schema source is available -- like + * when a persistent cache reads its cache index. + * + * @param sourceIdentifier Source identifier + * @return schema source registration, which the subclass needs to + * {@link SchemaSourceRegistration#close() once it expunges the source + * from the cache. + */ + protected final SchemaSourceRegistration register(final SourceIdentifier sourceIdentifier) { + final PotentialSchemaSource src = PotentialSchemaSource.create(sourceIdentifier, representation, cost.getValue()); + return consumer.registerSchemaSource(this, src); + } + + @Override + public void schemaSourceEncountered(final SchemaSourceRepresentation source) { + if (representation.isAssignableFrom(source.getType())) { + @SuppressWarnings("unchecked") + final T src = (T)source; + offer(src); + } + } + + @Override + public final void schemaSourceRegistered(final Iterable> sources) { + // Not interesting + } + + @Override + public final void schemaSourceUnregistered(final PotentialSchemaSource source) { + // Not interesting + } +} diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java index 9ed0afefc7..241cc15996 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java @@ -10,19 +10,20 @@ package org.opendaylight.yangtools.yang.model.repo.util; import com.google.common.base.Preconditions; import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; -import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; -public abstract class AbstractSchemaSourceRegistration extends AbstractObjectRegistration implements SchemaSourceRegistration { +public abstract class AbstractSchemaSourceRegistration extends AbstractObjectRegistration> implements SchemaSourceRegistration { private final SchemaSourceProvider provider; - protected AbstractSchemaSourceRegistration(final SourceIdentifier identifier, final SchemaSourceProvider provider) { - super(identifier); + protected AbstractSchemaSourceRegistration(final SchemaSourceProvider provider, final PotentialSchemaSource source) { + super(source); this.provider = Preconditions.checkNotNull(provider); } - protected SchemaSourceProvider getProvider() { + protected final SchemaSourceProvider getProvider() { return provider; } } diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java deleted file mode 100644 index d264ae0545..0000000000 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaTransformerRegistration.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/eplv10.html - */ -package org.opendaylight.yangtools.yang.model.repo.util; - -import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaTransformerRegistration; - -public abstract class AbstractSchemaTransformerRegistration extends AbstractObjectRegistration> implements SchemaTransformerRegistration { - protected AbstractSchemaTransformerRegistration( - final SchemaSourceTransformer transformer) { - super(transformer); - } -} diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java new file mode 100644 index 0000000000..ac820d28d7 --- /dev/null +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.model.repo.util; + +import com.google.common.base.Preconditions; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; + +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; + +public class InMemorySchemaSourceCache extends AbstractSchemaSourceCache { + private static final class CacheEntry { + private final SchemaSourceRegistration reg; + private final T source; + + public CacheEntry(final T source, final SchemaSourceRegistration reg) { + this.source = Preconditions.checkNotNull(source); + this.reg = Preconditions.checkNotNull(reg); + } + } + + private static final RemovalListener> LISTENER = new RemovalListener>() { + @Override + public void onRemoval(final RemovalNotification> notification) { + notification.getValue().reg.close(); + } + }; + + private final Cache> cache; + + protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class representation, final int maxSize) { + super(consumer, representation, Costs.IMMEDIATE); + cache = CacheBuilder.newBuilder().softValues().maximumSize(maxSize).removalListener(LISTENER).build(); + } + + @Override + public CheckedFuture getSource(final SourceIdentifier sourceIdentifier) { + final CacheEntry present = cache.getIfPresent(sourceIdentifier); + if (present != null) { + return Futures.immediateCheckedFuture(present.source); + } + + return Futures.immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found")); + } + + @Override + protected void offer(final T source) { + final CacheEntry present = cache.getIfPresent(source.getIdentifier()); + if (present == null) { + final SchemaSourceRegistration reg = register(source.getIdentifier()); + cache.put(source.getIdentifier(), new CacheEntry(source, reg)); + } + } +} diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java new file mode 100644 index 0000000000..3cd59b2cce --- /dev/null +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.model.repo.util; + +import com.google.common.base.Preconditions; + +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; + +final class RefcountedRegistration { + private final SchemaSourceRegistration reg; + private int refcount = 1; + + RefcountedRegistration(final SchemaSourceRegistration reg) { + this.reg = Preconditions.checkNotNull(reg); + } + + public void incRef() { + refcount++; + } + + public boolean decRef() { + Preconditions.checkState(refcount > 0, "Refcount underflow: %s", refcount); + + if (0 == --refcount) { + reg.close(); + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java new file mode 100644 index 0000000000..7b58006cf0 --- /dev/null +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.model.repo.util; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; + +import java.util.HashMap; +import java.util.Map; + +import org.opendaylight.yangtools.util.concurrent.ExceptionMapper; +import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; + +public class SchemaSourceTransformer implements SchemaSourceListener, SchemaSourceProvider { + private static final ExceptionMapper MAPPER = ReflectiveExceptionMapper.create("Source transformation", SchemaSourceException.class); + + public static interface Transformation extends AsyncFunction { + @Override + CheckedFuture apply(final S input) throws Exception; + } + + private final Map, RefcountedRegistration> sources = new HashMap<>(); + private final SchemaSourceRegistry consumer; + private final SchemaRepository provider; + private final AsyncFunction function; + private final Class srcClass; + private final Class dstClass; + + public SchemaSourceTransformer(final SchemaRepository provider, final Class srcClass, + final SchemaSourceRegistry consumer, final Class dstClass, final AsyncFunction function) { + this.provider = Preconditions.checkNotNull(provider); + this.consumer = Preconditions.checkNotNull(consumer); + this.function = Preconditions.checkNotNull(function); + this.srcClass = Preconditions.checkNotNull(srcClass); + this.dstClass = Preconditions.checkNotNull(dstClass); + } + + @Override + public CheckedFuture getSource(final SourceIdentifier sourceIdentifier) { + final CheckedFuture f = provider.getSchemaSource(sourceIdentifier, srcClass); + return Futures.makeChecked(Futures.transform(f, function), MAPPER); + } + + @Override + public final void schemaSourceEncountered(final SchemaSourceRepresentation source) { + // Not interesting + } + + @Override + public final void schemaSourceRegistered(final Iterable> sources) { + for (PotentialSchemaSource src : sources) { + final Class rep = src.getRepresentation(); + if (srcClass.isAssignableFrom(rep) && dstClass != rep) { + registerSource(src); + } + } + } + + @Override + public final void schemaSourceUnregistered(final PotentialSchemaSource source) { + final Class rep = source.getRepresentation(); + if (srcClass.isAssignableFrom(rep) && dstClass != rep) { + unregisterSource(source); + } + } + + private void registerSource(final PotentialSchemaSource src) { + RefcountedRegistration reg = sources.get(src); + if (reg != null) { + reg.incRef(); + return; + } + + final PotentialSchemaSource newSrc = PotentialSchemaSource.create(src.getSourceIdentifier(), dstClass, + src.getCost() + PotentialSchemaSource.Costs.COMPUTATION.getValue()); + + final SchemaSourceRegistration r = consumer.registerSchemaSource(this, newSrc); + sources.put(src, new RefcountedRegistration(r)); + } + + private void unregisterSource(final PotentialSchemaSource src) { + final RefcountedRegistration reg = sources.get(src); + if (reg != null && reg.decRef()) { + sources.remove(src); + } + } +} diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java index 50354f5ed8..45bedd8c3a 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java @@ -26,6 +26,7 @@ import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.st import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Iterables; + import java.net.URI; import java.text.DateFormat; import java.text.ParseException; @@ -33,7 +34,9 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.List; + import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Argument_stmtContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Base_stmtContext; @@ -96,6 +99,7 @@ import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuil import org.slf4j.Logger; import org.slf4j.LoggerFactory; + public final class YangParserListenerImpl extends YangParserBaseListener { private static final Logger LOG = LoggerFactory.getLogger(YangParserListenerImpl.class); private static final Splitter COLON_SPLITTER = Splitter.on(':'); @@ -113,6 +117,24 @@ public final class YangParserListenerImpl extends YangParserBaseListener { this.sourcePath = sourcePath; } + /** + * Create a new instance. + * + * FIXME: the resulting type needs to be extracted, such that we can reuse + * the "BaseListener" aspect, which need not be exposed to the user. + * Maybe factor out a base class into repo.spi? + * + * @param sourcePath + * @param walker + * @param tree + * @return + */ + public static YangParserListenerImpl create(final String sourcePath, final ParseTreeWalker walker, final ParseTree tree) { + final YangParserListenerImpl ret = new YangParserListenerImpl(sourcePath); + walker.walk(ret, tree); + return ret; + } + @Override public void enterModule_stmt(final YangParser.Module_stmtContext ctx) { moduleName = stringFromNode(ctx); diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java new file mode 100644 index 0000000000..8dee8367fa --- /dev/null +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.parser.repo; + +import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; + +public abstract class AbstractURLRegistration extends AbstractObjectRegistration implements URLRegistration{ + protected AbstractURLRegistration(final YangTextSchemaSource text) { + super(text); + } +} \ No newline at end of file diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java index 45ad366285..5bc6616c86 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java @@ -71,6 +71,8 @@ final class DependencyResolver { return rev == null && findWildcard(haystack, mi.getModuleName()) != null; } + + public static final DependencyResolver create(final Map depInfo) { final Collection resolved = new ArrayList<>(depInfo.size()); final Collection pending = new ArrayList<>(depInfo.keySet()); diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java new file mode 100644 index 0000000000..5873856f00 --- /dev/null +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.parser.repo; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Collections2; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.opendaylight.yangtools.util.concurrent.ExceptionMapper; +import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; +import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl; +import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo; +import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class SharedSchemaContextFactory implements SchemaContextFactory { + private static final ExceptionMapper MAPPER = ReflectiveExceptionMapper.create("resolve sources", SchemaResolutionException.class); + private static final Logger LOG = LoggerFactory.getLogger(SharedSchemaContextFactory.class); + + private final Function> requestSources = new Function>() { + @Override + public ListenableFuture apply(final SourceIdentifier input) { + return repository.getSchemaSource(input, ASTSchemaSource.class); + } + }; + private final Cache, SchemaContext> cache = CacheBuilder.newBuilder().softValues().build(); + + private final AsyncFunction, SchemaContext> assembleSources = new AsyncFunction, SchemaContext>() { + @Override + public ListenableFuture apply(final List sources) throws SchemaResolutionException { + final Map srcs = + Maps.uniqueIndex(sources, ASTSchemaSource.GET_IDENTIFIER); + final Map deps = + Maps.transformValues(srcs, ASTSchemaSource.GET_DEPINFO); + + LOG.debug("Resolving dependency reactor {}", deps); + + final DependencyResolver res = DependencyResolver.create(deps); + if (!res.getUnresolvedSources().isEmpty()) { + LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports()); + + // FIXME: push into DependencyResolver + + throw new SchemaResolutionException("Failed to resolve required models", + res.getResolvedSources(), res.getUnsatisfiedImports()); + } + + final Map asts = + Maps.transformValues(srcs, ASTSchemaSource.GET_AST); + + final ParseTreeWalker walker = new ParseTreeWalker(); + final Map sourceToBuilder = new LinkedHashMap<>(); + + for (Entry entry : asts.entrySet()) { + ModuleBuilder moduleBuilder = + YangParserListenerImpl.create(entry.getKey().getName(), walker, entry.getValue()).getModuleBuilder(); + + moduleBuilder.setSource(srcs.get(entry.getKey()).getYangText()); + sourceToBuilder.put(entry.getKey(), moduleBuilder); + } + LOG.debug("Modules ready for integration"); + + final YangParserImpl parser = YangParserImpl.getInstance(); + final Collection modules = parser.buildModules(sourceToBuilder.values()); + LOG.debug("Integrated cross-references modules"); + return Futures.immediateCheckedFuture(parser.assembleContext(modules)); + } + }; + + private final SharedSchemaRepository repository; + // FIXME: ignored right now + private final SchemaSourceFilter filter; + + public SharedSchemaContextFactory(final SharedSchemaRepository repository, final SchemaSourceFilter filter) { + this.repository = Preconditions.checkNotNull(repository); + this.filter = Preconditions.checkNotNull(filter); + } + + @Override + public CheckedFuture createSchemaContext(final Collection requiredSources) { + final SchemaContext existing = cache.getIfPresent(requiredSources); + if (existing != null) { + LOG.debug("Returning cached context {}", existing); + return Futures.immediateCheckedFuture(existing); + } + + // Request all sources be loaded + final ListenableFuture> sf = Futures.allAsList(Collections2.transform(requiredSources, requestSources)); + + // Assemble sources into a schemacontext + final ListenableFuture cf = Futures.transform(sf, assembleSources); + + // Populate cache when successful + Futures.addCallback(cf, new FutureCallback() { + @Override + public void onSuccess(final SchemaContext result) { + cache.put(requiredSources, result); + } + + @Override + public void onFailure(final Throwable t) { + LOG.info("Failed to assemble sources", t); + } + }); + + return Futures.makeChecked(cf, MAPPER); + } +} \ No newline at end of file diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java new file mode 100644 index 0000000000..1ff9f32908 --- /dev/null +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.parser.repo; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.util.AbstractSchemaRepository; + +/** + * A {@link SchemaRepository} which allows sharing of {@link SchemaContext} as + * long as their specification is the same. + * + * Note: for current implementation, "same" means the same filter and the same + * set of {@link SourceIdentifier}s. + */ +@Beta +public final class SharedSchemaRepository extends AbstractSchemaRepository implements Identifiable { + private final LoadingCache cache = + CacheBuilder.newBuilder().softValues().build(new CacheLoader() { + @Override + public SchemaContextFactory load(final SchemaSourceFilter key) { + return new SharedSchemaContextFactory(SharedSchemaRepository.this, key); + } + }); + private final String id; + + public SharedSchemaRepository(final String id) { + this.id = Preconditions.checkNotNull(id); + } + + @Override + public String getIdentifier() { + return id; + } + + @Override + public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) { + return cache.getUnchecked(filter); + } + + @Override + public String toString() { + return "SchemaRepository: " + id; + } +} diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java new file mode 100644 index 0000000000..d49ecc9733 --- /dev/null +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.parser.repo; + +import org.opendaylight.yangtools.concepts.ObjectRegistration; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; + +public interface URLRegistration extends ObjectRegistration { + @Override + void close(); +} \ No newline at end of file diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java index 8ee18e58a5..47de6b1a54 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java @@ -9,155 +9,78 @@ package org.opendaylight.yangtools.yang.parser.repo; import static com.google.common.base.Preconditions.checkArgument; -import com.google.common.base.Function; +import com.google.common.annotations.Beta; import com.google.common.base.Objects.ToStringHelper; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Maps; -import com.google.common.collect.Maps.EntryTransformer; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.ThreadSafe; - -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ParseTreeWalker; -import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; -import org.opendaylight.yangtools.concepts.ObjectRegistration; -import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException; +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException; -import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder; -import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; -import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl; -import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource; import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@ThreadSafe -public class URLSchemaContextResolver { +@Beta +public class URLSchemaContextResolver implements SchemaSourceProvider { private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class); - private static final Function EXTRACT_DEPINFO = new Function() { - @Override - public YangModelDependencyInfo apply(final ASTSchemaSource input) { - return input.getDependencyInformation(); - } - }; - private static final EntryTransformer, YangModelDependencyInfo> SQUASH_DEPINFO = - new EntryTransformer, YangModelDependencyInfo>() { - @Override - public YangModelDependencyInfo transformEntry(final SourceIdentifier key, final Collection value) { - // FIXME: validate that all the info objects are the same - return value.iterator().next(); - } - }; - private static final Function EXTRACT_AST = new Function() { - @Override - public ParserRuleContext apply(final ASTSchemaSource input) { - return input.getAST(); - } - }; - private static final EntryTransformer, ParserRuleContext> SQUASH_AST = - new EntryTransformer, ParserRuleContext>() { - @Override - public ParserRuleContext transformEntry(final SourceIdentifier key, final Collection value) { - // FIXME: validate that all the info objects are the same - return value.iterator().next(); - } - }; - - @GuardedBy("this") - private final Multimap resolvedRegs = ArrayListMultimap.create(); - private final AtomicReference> currentSchemaContext = new AtomicReference<>(Optional.absent()); - private final Queue outstandingRegs = new ConcurrentLinkedQueue<>(); - private final TextToASTTransformer transformer; - @GuardedBy("this") - private Object version = new Object(); - @GuardedBy("this") - private Object contextVersion = version; - - private final class URLRegistration extends AbstractObjectRegistration { - @GuardedBy("this") - private CheckedFuture future; - @GuardedBy("this") - private ASTSchemaSource result; - - protected URLRegistration(final URL url, final CheckedFuture future) { - super(url); - this.future = Preconditions.checkNotNull(future); - } - - private synchronized boolean setResult(final ASTSchemaSource result) { - if (future != null) { - this.result = result; - return true; - } else { - return false; - } - } - @Override - protected void removeRegistration() { - // Cancel the future, but it may already be completing - future.cancel(false); - - synchronized (this) { - future = null; - outstandingRegs.remove(this); - if (result != null) { - removeSchemaSource(result); - } - } - } - } - - private URLSchemaContextResolver(final TextToASTTransformer transformer) { - this.transformer = Preconditions.checkNotNull(transformer); + private final Cache sources = CacheBuilder.newBuilder().build(); + private final Collection requiredSources = new ConcurrentLinkedDeque<>(); + private final AtomicReference> currentSchemaContext = + new AtomicReference<>(Optional.absent()); + private final SchemaSourceRegistry registry; + private final SchemaRepository repository; + private volatile Object version = new Object(); + private volatile Object contextVersion = version; + + private URLSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) { + this.repository = Preconditions.checkNotNull(repository); + this.registry = Preconditions.checkNotNull(registry); } public static URLSchemaContextResolver create(final String name) { - final ThreadFactory f = new ThreadFactoryBuilder().setDaemon(true).setNameFormat(name + "yangparser-%d").build(); - final ListeningExecutorService s = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(f)); - - return new URLSchemaContextResolver(TextToASTTransformer.create(s)); + final SharedSchemaRepository sharedRepo = new SharedSchemaRepository(name); + return new URLSchemaContextResolver(sharedRepo, sharedRepo); } /** * Register a URL hosting a YANG Text file. * * @param url URL + * @throws YangSyntaxErrorException When the YANG file is syntactically invalid + * @throws IOException when the URL is not readable + * @throws SchemaSourceException When parsing encounters general error */ - public ObjectRegistration registerSource(final URL url) { + public URLRegistration registerSource(final URL url) throws SchemaSourceException, IOException, YangSyntaxErrorException { checkArgument(url != null, "Supplied URL must not be null"); - final SourceIdentifier id = SourceIdentifier.create(url.getFile().toString(), Optional.absent()); - final YangTextSchemaSource text = new YangTextSchemaSource(id) { + final SourceIdentifier guessedId = new SourceIdentifier(url.getFile(), Optional.absent()); + final YangTextSchemaSource text = new YangTextSchemaSource(guessedId) { @Override public InputStream openStream() throws IOException { return url.openStream(); @@ -169,39 +92,27 @@ public class URLSchemaContextResolver { } }; - final CheckedFuture ast = transformer.transformSchemaSource(text); - final URLRegistration reg = new URLRegistration(url, ast); - outstandingRegs.add(reg); + final ASTSchemaSource ast = TextToASTTransformer.TRANSFORMATION.apply(text).checkedGet(); + LOG.trace("Resolved URL {} to source {}", url, ast); - Futures.addCallback(ast, new FutureCallback() { - @Override - public void onSuccess(final ASTSchemaSource result) { - LOG.trace("Resolved URL {} to source {}", url, result); + final SourceIdentifier resolvedId = ast.getIdentifier(); + final SchemaSourceRegistration reg = registry.registerSchemaSource(this, + PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, 0)); - outstandingRegs.remove(reg); - if (reg.setResult(result)) { - addSchemaSource(result); - } - } + requiredSources.add(resolvedId); + LOG.trace("Added source {} to schema context requirements", resolvedId); + version = new Object(); + return new AbstractURLRegistration(text) { @Override - public void onFailure(final Throwable t) { - LOG.warn("Failed to parse YANG text from {}, ignoring it", url, t); - outstandingRegs.remove(reg); + protected void removeRegistration() { + requiredSources.remove(resolvedId); + LOG.trace("Removed source {} from schema context requirements", resolvedId); + version = new Object(); + reg.close(); + sources.invalidate(resolvedId); } - }); - - return reg; - } - - private synchronized void addSchemaSource(final ASTSchemaSource src) { - resolvedRegs.put(src.getIdentifier(), src); - version = new Object(); - } - - private synchronized void removeSchemaSource(final ASTSchemaSource src) { - resolvedRegs.put(src.getIdentifier(), src); - version = new Object(); + }; } /** @@ -210,64 +121,57 @@ public class URLSchemaContextResolver { * new schema context was successfully built. */ public Optional getSchemaContext() { - while (true) { - Optional result; - final Multimap sources; - final Object v; - synchronized (this) { - result = currentSchemaContext.get(); - if (version == contextVersion) { - return result; + final SchemaContextFactory factory = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT); + Optional sc; + Object v; + do { + // Spin get stable context version + Object cv; + do { + cv = contextVersion; + sc = currentSchemaContext.get(); + if (version == cv) { + return sc; } + } while (cv != contextVersion); - sources = ImmutableMultimap.copyOf(resolvedRegs); + // Version has been updated + Collection sources; + do { v = version; - } - - if (!sources.isEmpty()) { - final Map deps = - Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_DEPINFO).asMap(), SQUASH_DEPINFO); - - LOG.debug("Resolving dependency reactor {}", deps); - final DependencyResolver res = DependencyResolver.create(deps); - if (!res.getUnresolvedSources().isEmpty()) { - LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports()); + sources = ImmutableList.copyOf(requiredSources); + } while (v != version); + + while (true) { + final CheckedFuture f = factory.createSchemaContext(sources); + try { + sc = Optional.of(f.checkedGet()); + break; + } catch (SchemaResolutionException e) { + LOG.info("Failed to fully assemble schema context for {}", sources, e); + sources = e.getResolvedSources(); } - - final Map asts = - Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_AST).asMap(), SQUASH_AST); - - final ParseTreeWalker walker = new ParseTreeWalker(); - final Map sourceToBuilder = new LinkedHashMap<>(); - - for (Entry entry : asts.entrySet()) { - final YangParserListenerImpl yangModelParser = new YangParserListenerImpl(entry.getKey().getName()); - walker.walk(yangModelParser, entry.getValue()); - ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder(); - - moduleBuilder.setSource(sources.get(entry.getKey()).iterator().next().getYangText()); - sourceToBuilder.put(entry.getKey(), moduleBuilder); - } - LOG.debug("Modules ready for integration"); - - final YangParserImpl parser = YangParserImpl.getInstance(); - final Collection modules = parser.buildModules(sourceToBuilder.values()); - LOG.debug("Integrated cross-references modules"); - - result = Optional.of(parser.assembleContext(modules)); - } else { - result = Optional.absent(); } synchronized (this) { - if (v == version) { - currentSchemaContext.set(result); - contextVersion = version; - return result; + if (contextVersion == cv) { + currentSchemaContext.set(sc); + contextVersion = v; } - - LOG.debug("Context version {} expected {}, retry", version, v); } + } while (version == v); + + return sc; + } + + @Override + public CheckedFuture getSource(final SourceIdentifier sourceIdentifier) { + final YangTextSchemaSource ret = sources.getIfPresent(sourceIdentifier); + if (ret == null) { + return Futures.immediateFailedCheckedFuture( + new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered")); } + + return Futures.immediateCheckedFuture(ret); } } diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java index ab353a04ad..5ff245962e 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java @@ -6,6 +6,8 @@ */ package org.opendaylight.yangtools.yang.parser.util; +import com.google.common.annotations.Beta; +import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -27,7 +29,27 @@ import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo; * passes basic semantic validation and we were able to extract dependency * information. */ +@Beta public final class ASTSchemaSource implements SchemaSourceRepresentation { + public static final Function GET_IDENTIFIER = new Function() { + @Override + public SourceIdentifier apply(final ASTSchemaSource input) { + return input.getIdentifier(); + } + }; + public static final Function GET_DEPINFO = new Function() { + @Override + public YangModelDependencyInfo apply(final ASTSchemaSource input) { + return input.getDependencyInformation(); + } + }; + public static final Function GET_AST = new Function() { + @Override + public ParserRuleContext apply(final ASTSchemaSource input) { + return input.getAST(); + } + }; + private final YangModelDependencyInfo depInfo; private final ParserRuleContext tree; private final SourceIdentifier id; diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java index ce9d6a8ce9..211e6a2174 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java @@ -6,26 +6,24 @@ */ package org.opendaylight.yangtools.yang.parser.util; +import com.google.common.annotations.Beta; import com.google.common.base.Charsets; -import com.google.common.base.Function; -import com.google.common.base.Preconditions; import com.google.common.io.CharStreams; import com.google.common.io.InputSupplier; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListeningExecutorService; import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.Callable; import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext; -import org.opendaylight.yangtools.util.concurrent.ExceptionMapper; import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; +import org.opendaylight.yangtools.yang.model.repo.util.SchemaSourceTransformer; import org.opendaylight.yangtools.yang.parser.impl.YangModelBasicValidationListener; import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; import org.slf4j.Logger; @@ -34,73 +32,43 @@ import org.slf4j.LoggerFactory; /** * A {@link SchemaSourceTransformer} which handles translation of models from * {@link YangTextSchemaSource} representation into {@link ASTSchemaSource}. - * - * While this class is currently used explicitly, its long-term purpose is to - * be registered with a {@link org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry} - * and be invoked on demand when the processing pipeline requests the - * ASTSchemaSource representation. */ -public final class TextToASTTransformer implements SchemaSourceTransformer { - private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class); - private static final Function MAPPER = new ExceptionMapper("Source transformation", SchemaSourceTransformationException.class) { +@Beta +public final class TextToASTTransformer extends SchemaSourceTransformer { + public static final class TextToASTTransformation implements Transformation { @Override - protected SchemaSourceTransformationException newWithCause(final String message, final Throwable cause) { - return new SchemaSourceTransformationException(message, cause); - } - }; - - private final ListeningExecutorService executor; - - private TextToASTTransformer(final ListeningExecutorService executor) { - this.executor = Preconditions.checkNotNull(executor); - } - - public static final TextToASTTransformer create(final ListeningExecutorService executor) { - return new TextToASTTransformer(executor); - } + public CheckedFuture apply(final YangTextSchemaSource input) throws IOException, YangSyntaxErrorException { + try (InputStream is = input.openStream()) { + final YangContext ctx = YangParserImpl.parseYangSource(is); + LOG.debug("Model {} parsed successfully", input); - @Override - public Class getInputRepresentation() { - return YangTextSchemaSource.class; - } - - @Override - public Class getOutputRepresentation() { - return ASTSchemaSource.class; - } + final ParseTreeWalker walker = new ParseTreeWalker(); + final YangModelBasicValidationListener validator = new YangModelBasicValidationListener(); + walker.walk(validator, ctx); + LOG.debug("Model {} validated successfully", input); - @Override - public CheckedFuture transformSchemaSource(final YangTextSchemaSource source) { - return Futures.makeChecked(executor.submit(new Callable() { - @Override - public ASTSchemaSource call() throws IOException, YangSyntaxErrorException { - try (InputStream is = source.openStream()) { - final YangContext ctx = YangParserImpl.parseYangSource(is); - LOG.debug("Model {} parsed successfully", source); + // Backwards compatibility + final String text = CharStreams.toString( + CharStreams.newReaderSupplier(new InputSupplier() { + @Override + public InputStream getInput() throws IOException { + return input.openStream(); + } + }, Charsets.UTF_8)); - final ParseTreeWalker walker = new ParseTreeWalker(); - final YangModelBasicValidationListener validator = new YangModelBasicValidationListener(); - walker.walk(validator, ctx); - LOG.debug("Model {} validated successfully", source); + return Futures.immediateCheckedFuture(ASTSchemaSource.create(input.getIdentifier().getName(), ctx, text)); + } + } + }; - // Backwards compatibility - final String text = CharStreams.toString( - CharStreams.newReaderSupplier(new InputSupplier() { - @Override - public InputStream getInput() throws IOException { - return source.openStream(); - } - }, Charsets.UTF_8)); + public static final TextToASTTransformation TRANSFORMATION = new TextToASTTransformation(); + private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class); - return ASTSchemaSource.create(source.getIdentifier().getName(), ctx, text); - } - } - }), MAPPER); + private TextToASTTransformer(final SchemaRepository provider, final SchemaSourceRegistry consumer) { + super(provider, YangTextSchemaSource.class, consumer, ASTSchemaSource.class, TRANSFORMATION); } - @Override - public int getCost() { - // We perform a direct translation, so the cost is 1. - return 1; + public static final TextToASTTransformer create(final SchemaRepository provider, final SchemaSourceRegistry consumer) { + return new TextToASTTransformer(provider, consumer); } } -- 2.36.6