All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.jsonrpc.tool.test.GovernanceImpl Maven / Gradle / Ivy

The newest version!
/*
 * 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 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.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 java.util.stream.Stream;
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.yangtools.binding.meta.YangModuleInfo;
import org.opendaylight.yangtools.binding.runtime.spi.BindingRuntimeHelpers;
import org.opendaylight.yangtools.yang.model.spi.source.FileYangTextSource;
import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangIRSourceInfoExtractor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

final class GovernanceImpl implements RemoteGovernance {
    private static final Logger LOG = LoggerFactory.getLogger(GovernanceImpl.class);
    private static final Set BUNDLED_MODULES = BindingRuntimeHelpers.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 var sourceInfo = YangIRSourceInfoExtractor.forYangText(new FileYangTextSource(file));
                    return Stream.concat(sourceInfo.imports().stream(), sourceInfo.includes().stream())
                        .map(m -> new ModuleInfo(m.name().getLocalName(), 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.orElseThrow().getYangTextCharSource().read();
            }
            final Optional opt = findYangFile(arg);
            if (opt.isPresent()) {
                return Files.readString(opt.orElseThrow());
            }
        } 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 static 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 static 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.orElseThrow());
        }
        try {
            final Optional optFile = findYangFile(module);
            if (optFile.isPresent()) {
                return yangFileImportCache.getUnchecked(optFile.orElseThrow());
            }
        } 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 + "]";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy