Fix for features - added missing dependency
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / util / URLSchemaContextResolver.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.parser.impl.util;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
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;
18 import java.net.URL;
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;
34
35 @ThreadSafe
36 public class URLSchemaContextResolver implements AdvancedSchemaSourceProvider<InputStream> {
37
38     private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
39
40     @GuardedBy("this")
41     private final ConcurrentMap<SourceIdentifier, SourceContext> availableSources = new ConcurrentHashMap<>();
42     @GuardedBy("this")
43     private YangSourceContext currentSourceContext;
44     @GuardedBy("this")
45     private Optional<SchemaContext> currentSchemaContext = Optional.absent();
46
47     /**
48      * Register new yang schema when it appears.
49      */
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);
58         return sourceContext;
59     }
60
61     public synchronized Optional<SchemaContext> getSchemaContext() {
62         return currentSchemaContext;
63     }
64
65     @Override
66     public synchronized Optional<InputStream> getSchemaSource(SourceIdentifier key) {
67         SourceContext ctx = availableSources.get(key);
68         if (ctx != null) {
69             InputStream stream = getInputStream(ctx.getInstance());
70             return Optional.fromNullable(stream);
71         }
72         return Optional.absent();
73     }
74
75     @Override
76     public Optional<InputStream> getSchemaSource(String name, Optional<String> version) {
77         return getSchemaSource(SourceIdentifier.create(name, version));
78     }
79
80     private static InputStream getInputStream(URL source) {
81         InputStream stream;
82         try {
83             stream = source.openStream();
84         } catch (IOException e) {
85             throw new IllegalArgumentException("Supplied stream: " + source + " is not available", e);
86         }
87         return stream;
88     }
89
90     private final class SourceContext extends AbstractObjectRegistration<URL> //
91             implements Identifiable<SourceIdentifier> {
92
93         final SourceIdentifier identifier;
94         final YangModelDependencyInfo dependencyInfo;
95
96         public SourceContext(URL instance, SourceIdentifier identifier, YangModelDependencyInfo modelInfo) {
97             super(instance);
98             this.identifier = identifier;
99             this.dependencyInfo = modelInfo;
100         }
101
102         @Override
103         public SourceIdentifier getIdentifier() {
104             return identifier;
105         }
106
107         @Override
108         protected void removeRegistration() {
109             removeSource(this);
110         }
111
112         public YangModelDependencyInfo getDependencyInfo() {
113             return dependencyInfo;
114         }
115     }
116
117     private synchronized void removeSource(SourceContext sourceContext) {
118         boolean removed = availableSources.remove(sourceContext.getIdentifier(), sourceContext);
119         if (removed) {
120             tryToUpdateSchemaContext();
121         }
122     }
123
124     /**
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.
127      */
128     public synchronized Optional<SchemaContext> tryToUpdateSchemaContext() {
129         if (availableSources.isEmpty()) {
130             return Optional.absent();
131         }
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());
136         }
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());
140
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());
144         }
145         if (currentSourceContext == null || !yangSourceContext.getValidSources().equals(currentSourceContext.getValidSources())) {
146             try {
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();
156             }
157         } else {
158             currentSourceContext = yangSourceContext;
159             return Optional.absent();
160         }
161     }
162 }