org.opendaylight.jsonrpc.tool.test.GovernanceImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2020 Lumina Networks, Inc. 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.jsonrpc.tool.test;
import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer.transformText;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.opendaylight.jsonrpc.bus.messagelib.ResponderSession;
import org.opendaylight.jsonrpc.bus.messagelib.TransportFactory;
import org.opendaylight.jsonrpc.model.ModuleInfo;
import org.opendaylight.jsonrpc.model.RemoteGovernance;
import org.opendaylight.jsonrpc.model.StoreOperationArgument;
import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRSchemaSource;
import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class GovernanceImpl implements RemoteGovernance {
private static final Logger LOG = LoggerFactory.getLogger(GovernanceImpl.class);
private static final Set BUNDLED_MODULES = BindingReflections.loadModuleInfos();
private static final Pattern YANG_MODULE_RE = Pattern
.compile("(?[a-zA-Z_]?[\\w.-]+)(?@\\d{4}-\\d{2}-\\d{2})?\\.yang");
private final LoadingCache> yangFileImportCache = CacheBuilder.newBuilder()
.build(new CacheLoader>() {
@Override
public Set load(Path file) throws Exception {
final IRSchemaSource irSource = transformText(YangTextSchemaSource.forFile(file.toFile()));
return YangModelDependencyInfo.forIR(irSource)
.getDependencies()
.stream()
.map(m -> new ModuleInfo(m.getModuleName(), null))
.collect(Collectors.toSet());
}
});
private ResponderSession session;
private final Path yangDir;
GovernanceImpl(TransportFactory transportFactory, String endpoint, Path yangDir) throws URISyntaxException {
if (endpoint != null) {
session = transportFactory.endpointBuilder().responder().create(endpoint, this);
}
this.yangDir = yangDir;
}
@Override
public String governance(StoreOperationArgument arg) {
LOG.info("[governance] : {}", arg);
// NOOP in this impl.
return null;
}
@Override
public String source(ModuleInfo arg) {
LOG.info("[source] : {}", arg);
try {
final Optional bundled = findBundledModule(arg);
if (bundled.isPresent()) {
return bundled.get().getYangTextCharSource().read();
}
final Optional opt = findYangFile(arg);
if (opt.isPresent()) {
// TODO : java11 has Files.readString(), for now use Files.readAllBytes() for backward compatibility
return new String(Files.readAllBytes(opt.get()), StandardCharsets.UTF_8);
}
} catch (IOException e) {
throw new IllegalStateException(e);
}
return null;
}
@Override
public List depends(ModuleInfo moduleInfo) {
LOG.info("[depends] : {}", moduleInfo);
final Set resolved = new HashSet<>();
final Deque toResolve = new LinkedList<>();
toResolve.add(moduleInfo);
while (!toResolve.isEmpty()) {
LOG.debug("Resolved : {}", resolved);
LOG.trace("Remaining to resolve : {}", toResolve);
final ModuleInfo current = toResolve.pop();
resolved.add(current);
final Set currentImports = dependsInternal(current).stream()
.filter(m -> !resolved.contains(m))
.filter(m -> !toResolve.contains(m))
.collect(Collectors.toSet());
toResolve.addAll(currentImports);
}
return new ArrayList<>(resolved);
}
private Set parseDependencies(YangModuleInfo ymi) {
return ymi.getImportedModules()
.stream()
.map(mi -> new ModuleInfo(mi.getName().getLocalName(), null))
.collect(Collectors.toSet());
}
private Optional findYangFile(ModuleInfo mi) throws IOException {
final YangFileSearchResult result = new YangFileSearchResult(mi);
Files.walkFileTree(yangDir, result);
return result.getResult();
}
private Optional findBundledModule(ModuleInfo mi) {
return BUNDLED_MODULES.stream().filter(ymi -> ymi.getName().getLocalName().equals(mi.getModule())).findFirst();
}
private static class YangFileSearchResult extends SimpleFileVisitor {
private final ModuleInfo moduleInfo;
private Optional result = Optional.empty();
YangFileSearchResult(ModuleInfo moduleInfo) {
this.moduleInfo = moduleInfo;
}
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE",
justification = "Path#getFileName() won't return NULL")
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
LOG.trace("Considering file {}", file);
final Matcher matcher = YANG_MODULE_RE.matcher(file.getFileName().toString());
if (matcher.matches() && moduleInfo.getModule().equals(matcher.group("name"))) {
result = Optional.of(file.toAbsolutePath());
LOG.info("Found {}", file);
return FileVisitResult.TERMINATE;
}
return super.visitFile(file, attrs);
}
Optional getResult() {
return result;
}
}
private Set dependsInternal(ModuleInfo module) {
final Optional bundledOpt = findBundledModule(module);
if (bundledOpt.isPresent()) {
return parseDependencies(bundledOpt.get());
}
try {
final Optional optFile = findYangFile(module);
if (optFile.isPresent()) {
return yangFileImportCache.getUnchecked(optFile.get());
}
} catch (IOException e) {
throw new IllegalStateException(e);
}
throw new IllegalArgumentException("Module not found : " + module);
}
@Override
public void close() {
if (session != null) {
session.close();
}
}
@Override
public String toString() {
return "GovernanceImpl [session=" + session + ", yangDir=" + yangDir + "]";
}
}