import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final RootModificationApplyOperation strategyTree;
private final InMemoryDataTreeSnapshot snapshot;
private final ModifiedNode rootNode;
+ private final Version version;
@GuardedBy("this")
private boolean sealed = false;
this.snapshot = Preconditions.checkNotNull(snapshot);
this.strategyTree = Preconditions.checkNotNull(resolver).snapshot();
this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode());
+ /*
+ * We could allocate version beforehand, since Version contract
+ * states two allocated version must be allways different.
+ *
+ * Preallocating version simplifies scenarios such as
+ * chaining of modifications, since version for particular
+ * node in modification and in data tree (if successfully
+ * commited) will be same and will not change.
+ *
+ */
+ this.version = snapshot.getRootNode().getSubtreeVersion().next();
}
ModifiedNode getRootModification() {
try {
return resolveModificationStrategy(path).apply(modification, modification.getOriginal(),
- snapshot.getRootNode().getSubtreeVersion().next());
+ version);
} catch (Exception e) {
LOG.error("Could not create snapshot for {}:{}", path,modification,e);
throw e;
}
/*
- * FIXME: Add advanced transaction chaining for modification of not rebased
- * modification.
- *
- * Current computation of tempRoot may yeld incorrect subtree versions
- * if there are multiple concurrent transactions, which may break
- * versioning preconditions for modification of previously occured write,
- * directly nested under parent node, since node version is derived from
- * subtree version.
- *
- * For deeper nodes subtree version is derived from their respective metadata
- * nodes, so this incorrect root subtree version is not affecting us.
+ * We will use preallocated version, this means returned snapshot will
+ * have same version each time this method is called.
*/
TreeNode originalSnapshotRoot = snapshot.getRootNode();
- Optional<TreeNode> tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), originalSnapshotRoot.getSubtreeVersion().next());
+ Optional<TreeNode> tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), version);
InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(), tempRoot.get(), strategyTree);
return tempTree.newModification();
}
+
+ Version getVersion() {
+ return version;
+ }
}
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.io.ByteSource;
+
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
+
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.parser.repo.URLSchemaContextResolver}
+ * instead.
+ */
+@Deprecated
@ThreadSafe
public class URLSchemaContextResolver implements AdvancedSchemaSourceProvider<InputStream> {
/**
* Register new yang schema when it appears.
*/
- public synchronized ObjectRegistration<URL> registerSource(URL source) {
+ public synchronized ObjectRegistration<URL> registerSource(final URL source) {
checkArgument(source != null, "Supplied source must not be null");
InputStream yangStream = getInputStream(source);
YangModelDependencyInfo modelInfo = YangModelDependencyInfo.fromInputStream(yangStream);
}
@Override
- public synchronized Optional<InputStream> getSchemaSource(SourceIdentifier key) {
+ public synchronized Optional<InputStream> getSchemaSource(final SourceIdentifier key) {
SourceContext ctx = availableSources.get(key);
if (ctx != null) {
InputStream stream = getInputStream(ctx.getInstance());
}
@Override
- public Optional<InputStream> getSchemaSource(String name, Optional<String> version) {
+ public Optional<InputStream> getSchemaSource(final String name, final Optional<String> version) {
return getSchemaSource(SourceIdentifier.create(name, version));
}
- private static InputStream getInputStream(URL source) {
+ private static InputStream getInputStream(final URL source) {
InputStream stream;
try {
stream = source.openStream();
final SourceIdentifier identifier;
final YangModelDependencyInfo dependencyInfo;
- public SourceContext(URL instance, SourceIdentifier identifier, YangModelDependencyInfo modelInfo) {
+ public SourceContext(final URL instance, final SourceIdentifier identifier, final YangModelDependencyInfo modelInfo) {
super(instance);
this.identifier = identifier;
this.dependencyInfo = modelInfo;
}
}
- private synchronized void removeSource(SourceContext sourceContext) {
+ private synchronized void removeSource(final SourceContext sourceContext) {
boolean removed = availableSources.remove(sourceContext.getIdentifier(), sourceContext);
if (removed) {
tryToUpdateSchemaContext();
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
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.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.SchemaSourceRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Beta
-public class URLSchemaContextResolver implements SchemaSourceProvider<YangTextSchemaSource> {
+public class URLSchemaContextResolver implements AutoCloseable, SchemaSourceProvider<YangTextSchemaSource> {
private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
- private final Cache<SourceIdentifier, YangTextSchemaSource> sources = CacheBuilder.newBuilder().build();
private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
+ private final Multimap<SourceIdentifier, YangTextSchemaSource> texts = ArrayListMultimap.create();
private final AtomicReference<Optional<SchemaContext>> currentSchemaContext =
new AtomicReference<>(Optional.<SchemaContext>absent());
+ private final InMemorySchemaSourceCache<ASTSchemaSource> cache;
+ private final SchemaListenerRegistration transReg;
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);
+
+ final TextToASTTransformer t = TextToASTTransformer.create(repository, registry);
+ transReg = registry.registerSchemaSourceListener(t);
+
+ cache = InMemorySchemaSourceCache.createSoftCache(registry, ASTSchemaSource.class);
}
public static URLSchemaContextResolver create(final String name) {
LOG.trace("Resolved URL {} to source {}", url, ast);
final SourceIdentifier resolvedId = ast.getIdentifier();
- final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
- PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, 0));
-
- requiredSources.add(resolvedId);
- LOG.trace("Added source {} to schema context requirements", resolvedId);
- version = new Object();
- return new AbstractURLRegistration(text) {
- @Override
- protected void removeRegistration() {
- requiredSources.remove(resolvedId);
- LOG.trace("Removed source {} from schema context requirements", resolvedId);
- version = new Object();
- reg.close();
- sources.invalidate(resolvedId);
- }
- };
+ synchronized (this) {
+ texts.put(resolvedId, text);
+ LOG.debug("Populated {} with text", resolvedId);
+
+ final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
+ PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, Costs.IMMEDIATE.getValue()));
+ requiredSources.add(resolvedId);
+ cache.schemaSourceEncountered(ast);
+ LOG.debug("Added source {} to schema context requirements", resolvedId);
+ version = new Object();
+
+ return new AbstractURLRegistration(text) {
+ @Override
+ protected void removeRegistration() {
+ synchronized (URLSchemaContextResolver.this) {
+ requiredSources.remove(resolvedId);
+ LOG.trace("Removed source {} from schema context requirements", resolvedId);
+ version = new Object();
+ reg.close();
+ texts.remove(resolvedId, text);
+ }
+ }
+ };
+ }
}
/**
Collection<SourceIdentifier> sources;
do {
v = version;
- sources = ImmutableList.copyOf(requiredSources);
+ sources = ImmutableSet.copyOf(requiredSources);
} while (v != version);
while (true) {
sc = Optional.of(f.checkedGet());
break;
} catch (SchemaResolutionException e) {
- LOG.info("Failed to fully assemble schema context for {}", sources, e);
+ LOG.debug("Failed to fully assemble schema context for {}", sources, e);
sources = e.getResolvedSources();
}
}
+ LOG.debug("Resolved schema context for {}", sources);
+
synchronized (this) {
if (contextVersion == cv) {
currentSchemaContext.set(sc);
}
@Override
- public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
- final YangTextSchemaSource ret = sources.getIfPresent(sourceIdentifier);
- if (ret == null) {
+ public synchronized CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+ final Collection<YangTextSchemaSource> ret = texts.get(sourceIdentifier);
+
+ LOG.debug("Lookup {} result {}", sourceIdentifier, ret);
+ if (ret.isEmpty()) {
return Futures.<YangTextSchemaSource, SchemaSourceException>immediateFailedCheckedFuture(
new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered", sourceIdentifier));
}
- return Futures.immediateCheckedFuture(ret);
+ return Futures.immediateCheckedFuture(ret.iterator().next());
+ }
+
+ @Override
+ public void close() {
+ transReg.close();
}
}