/* * Copyright (c) 2014 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.impl.util; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.Optional; 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.Collection; 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.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.util.repo.AdvancedSchemaSourceProvider; import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier; import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ThreadSafe public class URLSchemaContextResolver implements AdvancedSchemaSourceProvider { private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class); @GuardedBy("this") private final ConcurrentMap availableSources = new ConcurrentHashMap<>(); @GuardedBy("this") private YangSourceContext currentSourceContext; @GuardedBy("this") private Optional currentSchemaContext = Optional.absent(); /** * Register new yang schema when it appears. */ public synchronized ObjectRegistration registerSource(URL source) { checkArgument(source != null, "Supplied source must not be null"); InputStream yangStream = getInputStream(source); YangModelDependencyInfo modelInfo = YangModelDependencyInfo.fromInputStream(yangStream); SourceIdentifier identifier = SourceIdentifier.create(modelInfo.getName(), Optional.of(modelInfo.getFormattedRevision())); SourceContext sourceContext = new SourceContext(source, identifier, modelInfo); availableSources.putIfAbsent(identifier, sourceContext); return sourceContext; } public synchronized Optional getSchemaContext() { return currentSchemaContext; } @Override public synchronized Optional getSchemaSource(SourceIdentifier key) { SourceContext ctx = availableSources.get(key); if (ctx != null) { InputStream stream = getInputStream(ctx.getInstance()); return Optional.fromNullable(stream); } return Optional.absent(); } @Override public Optional getSchemaSource(String name, Optional version) { return getSchemaSource(SourceIdentifier.create(name, version)); } private static InputStream getInputStream(URL source) { InputStream stream; try { stream = source.openStream(); } catch (IOException e) { throw new IllegalArgumentException("Supplied stream: " + source + " is not available", e); } return stream; } private final class SourceContext extends AbstractObjectRegistration // implements Identifiable { final SourceIdentifier identifier; final YangModelDependencyInfo dependencyInfo; public SourceContext(URL instance, SourceIdentifier identifier, YangModelDependencyInfo modelInfo) { super(instance); this.identifier = identifier; this.dependencyInfo = modelInfo; } @Override public SourceIdentifier getIdentifier() { return identifier; } @Override protected void removeRegistration() { removeSource(this); } public YangModelDependencyInfo getDependencyInfo() { return dependencyInfo; } } private synchronized void removeSource(SourceContext sourceContext) { boolean removed = availableSources.remove(sourceContext.getIdentifier(), sourceContext); if (removed) { tryToUpdateSchemaContext(); } } /** * 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 synchronized Optional tryToUpdateSchemaContext() { if (availableSources.isEmpty()) { return Optional.absent(); } ImmutableMap actualSources = ImmutableMap.copyOf(availableSources); Builder builder = ImmutableMap.builder(); for (Entry entry : actualSources.entrySet()) { builder.put(entry.getKey(), entry.getValue().getDependencyInfo()); } ImmutableMap sourcesMap = builder.build(); YangSourceContext yangSourceContext = YangSourceContext.createFrom(sourcesMap, this); LOG.debug("Trying to create schema context from {}", sourcesMap.keySet()); if (yangSourceContext.getMissingDependencies().size() != 0) { LOG.debug("Omitting {} because of unresolved dependencies", yangSourceContext.getMissingDependencies().keySet()); LOG.debug("Missing model sources for {}", yangSourceContext.getMissingSources()); } if (currentSourceContext == null || !yangSourceContext.getValidSources().equals(currentSourceContext.getValidSources())) { try { Collection byteSources = yangSourceContext.getValidByteSources(); YangParserImpl parser = YangParserImpl.getInstance(); SchemaContext schemaContext = parser.parseSources(byteSources); currentSchemaContext = Optional.of(schemaContext); currentSourceContext = yangSourceContext; return Optional.of(schemaContext); } catch (Exception e) { LOG.error("Could not create schema context for {} ", yangSourceContext.getValidSources(), e); return Optional.absent(); } } else { currentSourceContext = yangSourceContext; return Optional.absent(); } } }