2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.parser.impl.util;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.base.Optional;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableMap.Builder;
15 import com.google.common.io.ByteSource;
16 import java.io.IOException;
17 import java.io.InputStream;
19 import java.util.Collection;
20 import java.util.Map.Entry;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import javax.annotation.concurrent.GuardedBy;
24 import javax.annotation.concurrent.ThreadSafe;
25 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
26 import org.opendaylight.yangtools.concepts.Identifiable;
27 import org.opendaylight.yangtools.concepts.ObjectRegistration;
28 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
29 import org.opendaylight.yangtools.yang.model.util.repo.AdvancedSchemaSourceProvider;
30 import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier;
31 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 public class URLSchemaContextResolver implements AdvancedSchemaSourceProvider<InputStream> {
38 private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
41 private final ConcurrentMap<SourceIdentifier, SourceContext> availableSources = new ConcurrentHashMap<>();
43 private YangSourceContext currentSourceContext;
45 private Optional<SchemaContext> currentSchemaContext = Optional.absent();
48 * Register new yang schema when it appears.
50 public synchronized ObjectRegistration<URL> registerSource(URL source) {
51 checkArgument(source != null, "Supplied source must not be null");
52 InputStream yangStream = getInputStream(source);
53 YangModelDependencyInfo modelInfo = YangModelDependencyInfo.fromInputStream(yangStream);
54 SourceIdentifier identifier = SourceIdentifier.create(modelInfo.getName(),
55 Optional.of(modelInfo.getFormattedRevision()));
56 SourceContext sourceContext = new SourceContext(source, identifier, modelInfo);
57 availableSources.putIfAbsent(identifier, sourceContext);
61 public synchronized Optional<SchemaContext> getSchemaContext() {
62 return currentSchemaContext;
66 public synchronized Optional<InputStream> getSchemaSource(SourceIdentifier key) {
67 SourceContext ctx = availableSources.get(key);
69 InputStream stream = getInputStream(ctx.getInstance());
70 return Optional.fromNullable(stream);
72 return Optional.absent();
76 public Optional<InputStream> getSchemaSource(String name, Optional<String> version) {
77 return getSchemaSource(SourceIdentifier.create(name, version));
80 private static InputStream getInputStream(URL source) {
83 stream = source.openStream();
84 } catch (IOException e) {
85 throw new IllegalArgumentException("Supplied stream: " + source + " is not available", e);
90 private final class SourceContext extends AbstractObjectRegistration<URL> //
91 implements Identifiable<SourceIdentifier> {
93 final SourceIdentifier identifier;
94 final YangModelDependencyInfo dependencyInfo;
96 public SourceContext(URL instance, SourceIdentifier identifier, YangModelDependencyInfo modelInfo) {
98 this.identifier = identifier;
99 this.dependencyInfo = modelInfo;
103 public SourceIdentifier getIdentifier() {
108 protected void removeRegistration() {
112 public YangModelDependencyInfo getDependencyInfo() {
113 return dependencyInfo;
117 private synchronized void removeSource(SourceContext sourceContext) {
118 boolean removed = availableSources.remove(sourceContext.getIdentifier(), sourceContext);
120 tryToUpdateSchemaContext();
125 * Try to parse all currently available yang files and build new schema context.
126 * @return new schema context iif there is at least 1 yang file registered and new schema context was successfully built.
128 public synchronized Optional<SchemaContext> tryToUpdateSchemaContext() {
129 if (availableSources.isEmpty()) {
130 return Optional.absent();
132 ImmutableMap<SourceIdentifier, SourceContext> actualSources = ImmutableMap.copyOf(availableSources);
133 Builder<SourceIdentifier, YangModelDependencyInfo> builder = ImmutableMap.<SourceIdentifier, YangModelDependencyInfo>builder();
134 for (Entry<SourceIdentifier, SourceContext> entry : actualSources.entrySet()) {
135 builder.put(entry.getKey(), entry.getValue().getDependencyInfo());
137 ImmutableMap<SourceIdentifier, YangModelDependencyInfo> sourcesMap = builder.build();
138 YangSourceContext yangSourceContext = YangSourceContext.createFrom(sourcesMap, this);
139 LOG.debug("Trying to create schema context from {}", sourcesMap.keySet());
141 if (yangSourceContext.getMissingDependencies().size() != 0) {
142 LOG.debug("Omitting {} because of unresolved dependencies", yangSourceContext.getMissingDependencies().keySet());
143 LOG.debug("Missing model sources for {}", yangSourceContext.getMissingSources());
145 if (currentSourceContext == null || !yangSourceContext.getValidSources().equals(currentSourceContext.getValidSources())) {
147 Collection<ByteSource> byteSources = yangSourceContext.getValidByteSources();
148 YangParserImpl parser = YangParserImpl.getInstance();
149 SchemaContext schemaContext = parser.parseSources(byteSources);
150 currentSchemaContext = Optional.of(schemaContext);
151 currentSourceContext = yangSourceContext;
152 return Optional.of(schemaContext);
153 } catch (Exception e) {
154 LOG.error("Could not create schema context for {} ", yangSourceContext.getValidSources(), e);
155 return Optional.absent();
158 currentSourceContext = yangSourceContext;
159 return Optional.absent();