Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2015 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 static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
import com.google.common.base.Verify;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.FluentFuture;
import java.io.IOException;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import org.checkerframework.checker.lock.qual.GuardedBy;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.concepts.AbstractRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.util.concurrent.FluentFutures;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
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.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
import org.opendaylight.yangtools.yang.model.repo.api.YangIRSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.spi.GuavaSchemaSourceCache;
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.SchemaListenerRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
import org.opendaylight.yangtools.yang.parser.api.YangParserFactory;
import org.opendaylight.yangtools.yang.parser.api.YangSyntaxErrorException;
import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class YangTextSchemaContextResolver implements AutoCloseable, SchemaSourceProvider {
private static final Logger LOG = LoggerFactory.getLogger(YangTextSchemaContextResolver.class);
private static final Duration SOURCE_LIFETIME = Duration.ofSeconds(60);
private final Collection requiredSources = new ConcurrentLinkedDeque<>();
private final Multimap texts = ArrayListMultimap.create();
@GuardedBy("this")
private final Map>> registeredFeatures = new HashMap<>();
private final AtomicReference> currentSchemaContext =
new AtomicReference<>(Optional.empty());
private final GuavaSchemaSourceCache cache;
private final SchemaListenerRegistration transReg;
private final SchemaSourceRegistry registry;
private final SchemaRepository repository;
private volatile Object version = new Object();
private volatile Object contextVersion = version;
@GuardedBy("this")
private FeatureSet supportedFeatures = null;
private YangTextSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
this.repository = requireNonNull(repository);
this.registry = requireNonNull(registry);
transReg = registry.registerSchemaSourceListener(TextToIRTransformer.create(repository, registry));
cache = GuavaSchemaSourceCache.createSoftCache(registry, YangIRSchemaSource.class, SOURCE_LIFETIME);
}
public static @NonNull YangTextSchemaContextResolver create(final String name) {
final var sharedRepo = new SharedSchemaRepository(name);
return new YangTextSchemaContextResolver(sharedRepo, sharedRepo);
}
public static @NonNull YangTextSchemaContextResolver create(final String name, final YangParserFactory factory) {
final var sharedRepo = new SharedSchemaRepository(name, factory);
return new YangTextSchemaContextResolver(sharedRepo, sharedRepo);
}
/**
* Register a {@link YangTextSchemaSource}.
*
* @param source YANG text source
* @return a YangTextSchemaSourceRegistration
* @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 @NonNull YangTextSchemaSourceRegistration registerSource(final @NonNull YangTextSchemaSource source)
throws SchemaSourceException, IOException, YangSyntaxErrorException {
checkArgument(source != null);
final var ast = TextToIRTransformer.transformText(source);
LOG.trace("Resolved source {} to source {}", source, ast);
// AST carries an accurate identifier, check if it matches the one supplied by the source. If it
// does not, check how much it differs and emit a warning.
final var providedId = source.getIdentifier();
final var parsedId = ast.getIdentifier();
final YangTextSchemaSource text;
if (!parsedId.equals(providedId)) {
if (!parsedId.name().equals(providedId.name())) {
LOG.info("Provided module name {} does not match actual text {}, corrected",
providedId.toYangFilename(), parsedId.toYangFilename());
} else {
final var sourceRev = providedId.revision();
if (sourceRev != null) {
if (!sourceRev.equals(parsedId.revision())) {
LOG.info("Provided module revision {} does not match actual text {}, corrected",
providedId.toYangFilename(), parsedId.toYangFilename());
}
} else {
LOG.debug("Expanded module {} to {}", providedId.toYangFilename(), parsedId.toYangFilename());
}
}
text = YangTextSchemaSource.delegateForCharSource(parsedId, source);
} else {
text = source;
}
synchronized (this) {
texts.put(parsedId, text);
LOG.debug("Populated {} with text", parsedId);
final var reg = registry.registerSchemaSource(this,
PotentialSchemaSource.create(parsedId, YangTextSchemaSource.class, Costs.IMMEDIATE.getValue()));
requiredSources.add(parsedId);
cache.schemaSourceEncountered(ast);
LOG.debug("Added source {} to schema context requirements", parsedId);
version = new Object();
return new AbstractYangTextSchemaSourceRegistration(text) {
@Override
protected void removeRegistration() {
synchronized (YangTextSchemaContextResolver.this) {
requiredSources.remove(parsedId);
LOG.trace("Removed source {} from schema context requirements", parsedId);
version = new Object();
reg.close();
texts.remove(parsedId, text);
}
}
};
}
}
/**
* Register a URL containing a YANG text.
*
* @param url YANG text source URL
* @return a YangTextSchemaSourceRegistration for this 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 @NonNull YangTextSchemaSourceRegistration registerSource(final @NonNull URL url)
throws SchemaSourceException, IOException, YangSyntaxErrorException {
checkArgument(url != null, "Supplied URL must not be null");
final String path = url.getPath();
final String fileName = path.substring(path.lastIndexOf('/') + 1);
return registerSource(YangTextSchemaSource.forURL(url, guessSourceIdentifier(fileName)));
}
/**
* Register a {@link QNameModule} as a known module namespace with a set of supported features. Union of these
* registrations is forwarded to {@link FeatureSet} and this is then used in {@link #getEffectiveModelContext()} and
* related methods.
*
* @param module Module namespace
* @param features Features supported for that module namespace
* @return a {@link Registration}, use {@link Registration#close()} to revert the effects of this method
* @throws NullPointerException if any argument is {@code null}
*/
public @NonNull Registration registerSupportedFeatures(final QNameModule module, final Set features) {
final var checked = requireNonNull(module);
final var copy = ImmutableSet.copyOf(features);
synchronized (this) {
version = new Object();
supportedFeatures = null;
registeredFeatures.computeIfAbsent(module, ignored -> new ArrayList<>()).add(copy);
}
return new AbstractRegistration() {
@Override
protected void removeRegistration() {
removeFeatures(checked, copy);
}
};
}
private synchronized void removeFeatures(final QNameModule module, final ImmutableSet features) {
final var moduleFeatures = registeredFeatures.get(module);
if (moduleFeatures != null && moduleFeatures.remove(features)) {
if (moduleFeatures.isEmpty()) {
registeredFeatures.remove(module);
}
supportedFeatures = null;
version = new Object();
}
}
private synchronized @Nullable FeatureSet getSupportedFeatures() {
var local = supportedFeatures;
if (local == null && !registeredFeatures.isEmpty()) {
final var builder = FeatureSet.builder();
for (var entry : registeredFeatures.entrySet()) {
for (var features : entry.getValue()) {
builder.addModuleFeatures(entry.getKey(), features);
}
}
supportedFeatures = local = builder.build();
}
return local;
}
private static SourceIdentifier guessSourceIdentifier(final @NonNull String fileName) {
try {
return YangTextSchemaSource.identifierFromFilename(fileName);
} catch (IllegalArgumentException e) {
LOG.warn("Invalid file name format in '{}'", fileName, e);
return new SourceIdentifier(fileName);
}
}
/**
* Try to parse all currently available yang files and build new schema context.
*
* @return new schema context iif there is at least 1 yang file registered and
* new schema context was successfully built.
*/
public Optional extends EffectiveModelContext> getEffectiveModelContext() {
return getEffectiveModelContext(StatementParserMode.DEFAULT_MODE);
}
/**
* Try to parse all currently available yang files and build new schema context depending on specified parsing mode.
*
* @param statementParserMode mode of statement parser
* @return new schema context iif there is at least 1 yang file registered and
* new schema context was successfully built.
*/
public Optional extends EffectiveModelContext> getEffectiveModelContext(
final StatementParserMode statementParserMode) {
Optional sc;
Object ver;
do {
// Spin get stable context version
Object cv;
do {
cv = contextVersion;
sc = currentSchemaContext.get();
if (version == cv) {
return sc;
}
} while (cv != contextVersion);
// Version has been updated
Collection sources;
do {
ver = version;
sources = ImmutableSet.copyOf(requiredSources);
} while (ver != version);
final var factory = repository.createEffectiveModelContextFactory(
config(statementParserMode, getSupportedFeatures()));
while (true) {
final var f = factory.createEffectiveModelContext(sources);
try {
sc = Optional.of(f.get());
break;
} catch (InterruptedException e) {
throw new IllegalStateException("Interrupted while assembling schema context", e);
} catch (ExecutionException e) {
LOG.info("Failed to fully assemble schema context for {}", sources, e);
final var cause = e.getCause();
Verify.verify(cause instanceof SchemaResolutionException);
sources = ((SchemaResolutionException) cause).getResolvedSources();
}
}
LOG.debug("Resolved schema context for {}", sources);
synchronized (this) {
if (contextVersion == cv) {
currentSchemaContext.set(sc);
contextVersion = ver;
}
}
} while (version == ver);
return sc;
}
@Override
public synchronized @NonNull FluentFuture getSource(
final SourceIdentifier sourceIdentifier) {
final var ret = texts.get(sourceIdentifier);
LOG.debug("Lookup {} result {}", sourceIdentifier, ret);
if (ret.isEmpty()) {
return FluentFutures.immediateFailedFluentFuture(
new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered", sourceIdentifier));
}
return FluentFutures.immediateFluentFuture(ret.iterator().next());
}
/**
* Return the set of sources currently available in this resolved.
*
* @return An immutable point-in-time view of available sources.
*/
public synchronized Set getAvailableSources() {
return ImmutableSet.copyOf(texts.keySet());
}
@Beta
public synchronized Collection getSourceTexts(final SourceIdentifier sourceIdentifier) {
return ImmutableSet.copyOf(texts.get(sourceIdentifier));
}
@Beta
public EffectiveModelContext trySchemaContext() throws SchemaResolutionException {
return trySchemaContext(StatementParserMode.DEFAULT_MODE);
}
@Beta
@SuppressWarnings("checkstyle:avoidHidingCauseException")
public EffectiveModelContext trySchemaContext(final StatementParserMode statementParserMode)
throws SchemaResolutionException {
final var future = repository
.createEffectiveModelContextFactory(config(statementParserMode, getSupportedFeatures()))
.createEffectiveModelContext(ImmutableSet.copyOf(requiredSources));
try {
return future.get();
} catch (InterruptedException e) {
throw new IllegalStateException("Interrupted while waiting for SchemaContext assembly", e);
} catch (ExecutionException e) {
final var cause = e.getCause();
if (cause instanceof SchemaResolutionException resolutionException) {
throw resolutionException;
}
throw new SchemaResolutionException("Failed to assemble SchemaContext", e);
}
}
@Override
public void close() {
transReg.close();
}
private static @NonNull SchemaContextFactoryConfiguration config(
final StatementParserMode statementParserMode, final @Nullable FeatureSet supportedFeatures) {
final var builder = SchemaContextFactoryConfiguration.builder().setStatementParserMode(statementParserMode);
if (supportedFeatures != null) {
builder.setSupportedFeatures(supportedFeatures);
}
return builder.build();
}
}