Fixed NPE in URLSchemaContextResolver.
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / util / URLSchemaContextResolver.java
1 package org.opendaylight.yangtools.yang.parser.impl.util;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.net.URL;
6 import java.util.List;
7 import java.util.Map.Entry;
8 import java.util.Set;
9 import java.util.concurrent.ConcurrentHashMap;
10 import java.util.concurrent.ConcurrentMap;
11
12 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
13 import org.opendaylight.yangtools.concepts.Identifiable;
14 import org.opendaylight.yangtools.concepts.Registration;
15 import org.opendaylight.yangtools.yang.model.api.Module;
16 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
17 import org.opendaylight.yangtools.yang.model.util.repo.AdvancedSchemaSourceProvider;
18 import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier;
19 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import com.google.common.base.Optional;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.ImmutableMap.Builder;
26 import com.google.common.collect.ImmutableSet;
27
28 import static com.google.common.base.Preconditions.checkArgument;
29
30 public class URLSchemaContextResolver implements AdvancedSchemaSourceProvider<InputStream> {
31
32     private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
33     private final ConcurrentMap<SourceIdentifier, SourceContext> availableSources = new ConcurrentHashMap<>();
34
35     private YangSourceContext currentSourceContext;
36     private Optional<SchemaContext> currentSchemaContext = Optional.absent();
37     
38     public Registration<URL> registerSource(URL source) {
39         checkArgument(source != null, "Supplied source must not be null");
40         InputStream yangStream = getInputStream(source);
41         YangModelDependencyInfo modelInfo = YangModelDependencyInfo.fromInputStream(yangStream);
42         SourceIdentifier identifier = SourceIdentifier.create(modelInfo.getName(),
43                 Optional.of(modelInfo.getFormattedRevision()));
44         SourceContext sourceContext = new SourceContext(source, identifier, modelInfo);
45         availableSources.putIfAbsent(identifier, sourceContext);
46         return sourceContext;
47     }
48
49     public Optional<SchemaContext> getSchemaContext() {
50         return currentSchemaContext;
51     }
52
53     @Override
54     public Optional<InputStream> getSchemaSource(SourceIdentifier key) {
55         SourceContext ctx = availableSources.get(key);
56         if (ctx != null) {
57             InputStream stream = getInputStream(ctx.getInstance());
58             return Optional.fromNullable(stream);
59         }
60         return Optional.absent();
61     }
62
63     @Override
64     public Optional<InputStream> getSchemaSource(String name, Optional<String> version) {
65         return getSchemaSource(SourceIdentifier.create(name, version));
66     }
67
68     private InputStream getInputStream(URL source) {
69         InputStream stream;
70         try {
71             stream = source.openStream();
72         } catch (IOException e) {
73             throw new IllegalArgumentException("Supplied stream: " + source + " is not available", e);
74         }
75         return stream;
76     }
77
78     private final class SourceContext extends AbstractObjectRegistration<URL> //
79             implements Identifiable<SourceIdentifier> {
80
81         final SourceIdentifier identifier;
82         final YangModelDependencyInfo dependencyInfo;
83
84         public SourceContext(URL instance, SourceIdentifier identifier, YangModelDependencyInfo modelInfo) {
85             super(instance);
86             this.identifier = identifier;
87             this.dependencyInfo = modelInfo;
88         }
89
90         public SourceIdentifier getIdentifier() {
91             return identifier;
92         }
93
94         @Override
95         protected void removeRegistration() {
96             removeSource(this);
97         }
98
99         public YangModelDependencyInfo getDependencyInfo() {
100             return dependencyInfo;
101         }
102     }
103
104     private void removeSource(SourceContext sourceContext) {
105         boolean removed = availableSources.remove(sourceContext.getIdentifier(), sourceContext);
106         if(removed) {
107             tryToUpdateSchemaContext();
108         }
109     }
110
111     public synchronized Optional<SchemaContext> tryToUpdateSchemaContext() {
112         if(availableSources.isEmpty()) {
113             return Optional.absent();
114         }
115         ImmutableMap<SourceIdentifier, SourceContext> actualSources = ImmutableMap.copyOf(availableSources);
116         Builder<SourceIdentifier, YangModelDependencyInfo> builder = ImmutableMap.<SourceIdentifier, YangModelDependencyInfo> builder();
117         for(Entry<SourceIdentifier, SourceContext> entry : actualSources.entrySet()) {
118             builder.put(entry.getKey(), entry.getValue().getDependencyInfo());
119         }
120         ImmutableMap<SourceIdentifier, YangModelDependencyInfo> sourcesMap = builder.build();
121         YangSourceContext context = YangSourceContext.createFrom(sourcesMap);
122         LOG.debug("Trying to create schema context from {}",sourcesMap.keySet());
123         LOG.debug("Ommiting {} because of unresolved dependencies",context.getMissingDependencies().keySet());
124         
125         try {
126             if(currentSourceContext == null || !context.getValidSources().equals(currentSourceContext.getValidSources())) {
127                 List<InputStream> streams = YangSourceContext.getValidInputStreams(context, this);
128                 YangParserImpl parser = new YangParserImpl();
129                 Set<Module> modules = parser.parseYangModelsFromStreams(streams);
130                 SchemaContext schemaContext = parser.resolveSchemaContext(modules);
131                 currentSchemaContext = Optional.of(schemaContext);
132                 currentSourceContext = context;
133                 return currentSchemaContext;
134             } 
135             currentSourceContext = context;
136         } catch (Exception e) {
137             LOG.error("Could not create schema context for {} ",context.getValidSources());
138         }
139         return Optional.absent();
140     }
141
142 }