2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.runtime.spi;
10 import static com.google.common.base.Preconditions.checkState;
12 import com.google.common.annotations.Beta;
13 import com.google.common.collect.ImmutableSet;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.ServiceLoader;
17 import java.util.stream.Collectors;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
20 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeGenerator;
21 import org.opendaylight.mdsal.binding.runtime.api.DefaultBindingRuntimeContext;
22 import org.opendaylight.mdsal.binding.runtime.api.ModuleInfoSnapshot;
23 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
24 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
25 import org.opendaylight.yangtools.yang.binding.contract.Naming;
26 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
27 import org.opendaylight.yangtools.yang.parser.api.YangParserException;
28 import org.opendaylight.yangtools.yang.parser.api.YangParserFactory;
31 * Simple helpers to help with reconstruction of BindingRuntimeContext from generated binding classes. These involve
32 * reflection and YANG model assembly, hence should not be used without any caching whatsoever or any support for
33 * dynamic schema updates.
36 public final class BindingRuntimeHelpers {
37 private BindingRuntimeHelpers() {
41 public static @NonNull EffectiveModelContext createEffectiveModel(final Class<?>... classes) {
42 return createEffectiveModel(Arrays.stream(classes)
43 .map(BindingRuntimeHelpers::getYangModuleInfo)
44 .collect(Collectors.toList()));
47 public static @NonNull EffectiveModelContext createEffectiveModel(
48 final Iterable<? extends YangModuleInfo> moduleInfos) {
50 return createEffectiveModel(ServiceLoaderState.ParserFactory.INSTANCE, moduleInfos);
51 } catch (YangParserException e) {
52 throw new IllegalStateException("Failed to parse models", e);
56 public static @NonNull EffectiveModelContext createEffectiveModel(final YangParserFactory parserFactory,
57 final Iterable<? extends YangModuleInfo> moduleInfos) throws YangParserException {
58 return prepareContext(parserFactory, moduleInfos).modelContext();
61 public static @NonNull BindingRuntimeContext createRuntimeContext() {
62 final ModuleInfoSnapshot infos;
64 infos = prepareContext(ServiceLoaderState.ParserFactory.INSTANCE, loadModuleInfos());
65 } catch (YangParserException e) {
66 throw new IllegalStateException("Failed to parse models", e);
68 return new DefaultBindingRuntimeContext(ServiceLoaderState.Generator.INSTANCE.generateTypeMapping(
69 infos.modelContext()), infos);
72 public static @NonNull BindingRuntimeContext createRuntimeContext(final Class<?>... classes) {
74 return createRuntimeContext(ServiceLoaderState.ParserFactory.INSTANCE,
75 ServiceLoaderState.Generator.INSTANCE, classes);
76 } catch (YangParserException e) {
77 throw new IllegalStateException("Failed to parse models", e);
81 public static @NonNull BindingRuntimeContext createRuntimeContext(
82 final Collection<? extends YangModuleInfo> infos) {
83 final ModuleInfoSnapshot snapshot;
86 snapshot = prepareContext(ServiceLoaderState.ParserFactory.INSTANCE, infos);
87 } catch (YangParserException e) {
88 throw new IllegalStateException("Failed to parse models", e);
91 return new DefaultBindingRuntimeContext(
92 ServiceLoaderState.Generator.INSTANCE.generateTypeMapping(snapshot.modelContext()), snapshot);
95 public static @NonNull BindingRuntimeContext createRuntimeContext(final YangParserFactory parserFactory,
96 final BindingRuntimeGenerator generator, final Class<?>... classes) throws YangParserException {
97 return createRuntimeContext(parserFactory, generator, Arrays.asList(classes));
100 public static @NonNull BindingRuntimeContext createRuntimeContext(final YangParserFactory parserFactory,
101 final BindingRuntimeGenerator generator, final Collection<Class<?>> classes) throws YangParserException {
102 final var infos = prepareContext(parserFactory, classes.stream()
103 .map(BindingRuntimeHelpers::getYangModuleInfo)
104 .collect(Collectors.toList()));
105 return new DefaultBindingRuntimeContext(generator.generateTypeMapping(infos.modelContext()), infos);
108 public static @NonNull YangModuleInfo getYangModuleInfo(final Class<?> clazz) {
109 // Module info resides in the root package we will use that to ascertain identity
110 final var modelPackage = Naming.getModelRootPackageName(clazz.getPackage().getName());
112 for (var bindingProvider : ServiceLoader.load(YangModelBindingProvider.class, clazz.getClassLoader())) {
113 var moduleInfo = bindingProvider.getModuleInfo();
114 if (modelPackage.equals(moduleInfo.getClass().getPackage().getName())) {
118 throw new IllegalStateException("Failed to find YangModuleInfo in package " + modelPackage + " for " + clazz);
121 public static @NonNull ImmutableSet<YangModuleInfo> loadModuleInfos() {
122 return loadModuleInfos(Thread.currentThread().getContextClassLoader());
126 * Loads {@link YangModuleInfo} infos available on supplied classloader.
129 * {@link YangModuleInfo} are discovered using {@link ServiceLoader} for {@link YangModelBindingProvider}.
130 * {@link YangModelBindingProvider} are simple classes which holds only pointers to actual instance
131 * {@link YangModuleInfo}.
134 * When {@link YangModuleInfo} is available, all dependencies are recursively collected into returning set by
135 * collecting results of {@link YangModuleInfo#getImportedModules()}.
137 * @param classLoader Classloader for which {@link YangModuleInfo} should be retrieved.
138 * @return Set of {@link YangModuleInfo} available for supplied classloader.
140 public static @NonNull ImmutableSet<YangModuleInfo> loadModuleInfos(final ClassLoader classLoader) {
141 final var moduleInfoSet = ImmutableSet.<YangModuleInfo>builder();
142 for (var bindingProvider : ServiceLoader.load(YangModelBindingProvider.class, classLoader)) {
143 var moduleInfo = bindingProvider.getModuleInfo();
144 checkState(moduleInfo != null, "Module Info for %s is not available.", bindingProvider.getClass());
145 collectYangModuleInfo(bindingProvider.getModuleInfo(), moduleInfoSet);
147 return moduleInfoSet.build();
150 private static void collectYangModuleInfo(final YangModuleInfo moduleInfo,
151 final ImmutableSet.Builder<YangModuleInfo> moduleInfoSet) {
152 moduleInfoSet.add(moduleInfo);
153 for (var dependency : moduleInfo.getImportedModules()) {
154 collectYangModuleInfo(dependency, moduleInfoSet);
158 private static @NonNull ModuleInfoSnapshot prepareContext(final YangParserFactory parserFactory,
159 final Iterable<? extends YangModuleInfo> moduleInfos) throws YangParserException {
160 return new ModuleInfoSnapshotBuilder(parserFactory).add(moduleInfos).build();