Added support for discovering YangModuleInfo via ServiceLoader. 57/5157/5
authorTony Tkacik <ttkacik@cisco.com>
Tue, 28 Jan 2014 16:43:08 +0000 (17:43 +0100)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 7 Feb 2014 15:07:53 +0000 (15:07 +0000)
  - Added ModuleInfoBackedContext which provides
     -  GeneratedClassLoadingStrategy which supports proactive search
     -  Creation of SchemaContext based on registered YangModuleInfo

Change-Id: I890c275031151b874b42b4e46bee4edcfce595ec
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
12 files changed:
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/GeneratedClassLoadingStrategy.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleInfoBackedContext.java [new file with mode: 0644]
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/YangModuleInfoTemplate.xtend
code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/maven/sal/api/gen/plugin/CodeGeneratorImpl.java
integration-test/pom.xml [new file with mode: 0644]
integration-test/yang-runtime-tests/pom.xml [new file with mode: 0644]
integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/tests/ModelDiscoveryTest.java [new file with mode: 0644]
integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/tests/MultipleRevisionsSupportTest.java [new file with mode: 0644]
pom.xml
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingMapping.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/YangModelBindingProvider.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java

index 89c9fa239625bff1a4144a765bbe89241aa1bd04..104245a235ea50a60355575f57e2d484f38c5101 100644 (file)
@@ -14,6 +14,14 @@ public abstract class GeneratedClassLoadingStrategy {
 
     private static final GeneratedClassLoadingStrategy TCCL_STRATEGY = new TCCLClassLoadingStrategy();
 
+    private static final GeneratedClassLoadingStrategy ALWAYS_FAIL_STRATEGY = new GeneratedClassLoadingStrategy() {
+
+        @Override
+        public Class<?> loadClass(String fullyQualifiedName) throws ClassNotFoundException {
+            throw new ClassNotFoundException(fullyQualifiedName);
+        }
+    };
+
     public Class<?> loadClass(Type type) throws ClassNotFoundException {
         return loadClass(type.getFullyQualifiedName());
     }
@@ -24,6 +32,10 @@ public abstract class GeneratedClassLoadingStrategy {
         return TCCL_STRATEGY;
     }
 
+    public static final GeneratedClassLoadingStrategy getAlwaysFailClassLoadingStrategy() {
+        return ALWAYS_FAIL_STRATEGY;
+    }
+
     private static final class TCCLClassLoadingStrategy extends GeneratedClassLoadingStrategy {
 
         @Override
diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleInfoBackedContext.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleInfoBackedContext.java
new file mode 100644 (file)
index 0000000..0a8c2dc
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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.sal.binding.generator.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.Module;
+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;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy //
+        implements //
+        AdvancedSchemaSourceProvider<InputStream> {
+
+    private ModuleInfoBackedContext(GeneratedClassLoadingStrategy loadingStrategy) {
+        this.backingLoadingStrategy = loadingStrategy;
+    }
+
+    public static ModuleInfoBackedContext create() {
+        return new ModuleInfoBackedContext(getTCCLClassLoadingStrategy());
+    }
+
+    public static ModuleInfoBackedContext create(GeneratedClassLoadingStrategy loadingStrategy) {
+        return new ModuleInfoBackedContext(loadingStrategy);
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(ModuleInfoBackedContext.class);
+
+    private final ConcurrentMap<String, WeakReference<ClassLoader>> packageNameToClassLoader = new ConcurrentHashMap<>();
+    private final ConcurrentMap<SourceIdentifier, YangModuleInfo> sourceIdentifierToModuleInfo = new ConcurrentHashMap<>();
+
+    private final GeneratedClassLoadingStrategy backingLoadingStrategy;
+
+    @Override
+    public Class<?> loadClass(String fullyQualifiedName) throws ClassNotFoundException {
+        String modulePackageName = BindingReflections.getModelRootPackageName(fullyQualifiedName);
+
+        WeakReference<ClassLoader> classLoaderRef = packageNameToClassLoader.get(modulePackageName);
+        ClassLoader classloader = null;
+        if (classLoaderRef != null && (classloader = classLoaderRef.get()) != null) {
+            return ClassLoaderUtils.loadClass(classloader, fullyQualifiedName);
+        }
+        if (backingLoadingStrategy == null) {
+            throw new ClassNotFoundException(fullyQualifiedName);
+        }
+        Class<?> cls = backingLoadingStrategy.loadClass(fullyQualifiedName);
+        if (BindingReflections.isBindingClass(cls)) {
+            boolean newModule = resolveModuleInfo(cls);
+            if (newModule) {
+                recreateSchemaContext();
+            }
+        }
+        return cls;
+    }
+
+    private synchronized Optional<SchemaContext> recreateSchemaContext() {
+        try {
+            ImmutableList<InputStream> streams = getAvailableStreams();
+            YangParserImpl parser = new YangParserImpl();
+            Set<Module> modules = parser.parseYangModelsFromStreams(streams);
+            SchemaContext schemaContext = parser.resolveSchemaContext(modules);
+            return Optional.of(schemaContext);
+        } catch (IOException e) {
+            LOG.error("Schema was not recreated.",e);
+        }
+        return Optional.absent();
+    }
+
+    public synchronized Optional<SchemaContext> tryToCreateSchemaContext() {
+        return recreateSchemaContext();
+    }
+
+    private ImmutableList<InputStream> getAvailableStreams() throws IOException {
+        ImmutableSet<YangModuleInfo> moduleInfos = ImmutableSet.copyOf(sourceIdentifierToModuleInfo.values());
+
+        ImmutableList.Builder<InputStream> sourceStreams = ImmutableList.<InputStream> builder();
+        for (YangModuleInfo moduleInfo : moduleInfos) {
+            sourceStreams.add(moduleInfo.getModuleSourceStream());
+        }
+        return sourceStreams.build();
+    }
+
+    private boolean resolveModuleInfo(Class<?> cls) {
+        try {
+            return resolveModuleInfo(BindingReflections.getModuleInfo(cls));
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private boolean resolveModuleInfo(YangModuleInfo moduleInfo) {
+
+        SourceIdentifier identifier = sourceIdentifierFrom(moduleInfo);
+        YangModuleInfo previous = sourceIdentifierToModuleInfo.putIfAbsent(identifier, moduleInfo);
+        ClassLoader moduleClassLoader = moduleInfo.getClass().getClassLoader();
+        if (previous == null) {
+            String modulePackageName = moduleInfo.getClass().getPackage().getName();
+            packageNameToClassLoader.putIfAbsent(modulePackageName, new WeakReference<ClassLoader>(moduleClassLoader));
+
+            for (YangModuleInfo importedInfo : moduleInfo.getImportedModules()) {
+                resolveModuleInfo(importedInfo);
+            }
+        } else {
+            return false;
+        }
+        return true;
+    }
+
+    private SourceIdentifier sourceIdentifierFrom(YangModuleInfo moduleInfo) {
+        return SourceIdentifier.create(moduleInfo.getName(), Optional.of(moduleInfo.getRevision()));
+    }
+
+    public void addModuleInfos(Iterable<? extends YangModuleInfo> moduleInfos) {
+        for (YangModuleInfo yangModuleInfo : moduleInfos) {
+            registerModuleInfo(yangModuleInfo);
+        }
+    }
+
+    private Registration<YangModuleInfo> registerModuleInfo(YangModuleInfo yangModuleInfo) {
+        YangModuleInfoRegistration registration = new YangModuleInfoRegistration(yangModuleInfo, this);
+
+        resolveModuleInfo(yangModuleInfo);
+
+        return registration;
+    }
+
+    @Override
+    public Optional<InputStream> getSchemaSource(SourceIdentifier sourceIdentifier) {
+        YangModuleInfo info = sourceIdentifierToModuleInfo.get(sourceIdentifier);
+        if (info == null) {
+            return Optional.absent();
+        }
+        try {
+            return Optional.of(info.getModuleSourceStream());
+        } catch (IOException e) {
+            return Optional.absent();
+        }
+    }
+
+    @Override
+    public Optional<InputStream> getSchemaSource(String moduleName, Optional<String> revision) {
+        return getSchemaSource(SourceIdentifier.create(moduleName, revision));
+    }
+
+    private static class YangModuleInfoRegistration extends AbstractObjectRegistration<YangModuleInfo> {
+
+        private final ModuleInfoBackedContext context;
+
+        public YangModuleInfoRegistration(YangModuleInfo instance, ModuleInfoBackedContext context) {
+            super(instance);
+            this.context = context;
+        }
+
+        @Override
+        protected void removeRegistration() {
+            context.remove(this);
+        }
+
+    }
+
+    private void remove(YangModuleInfoRegistration registration) {
+
+    }
+}
index 7c19f20a9db35d6b6263b8ce49c7050031fb2b4a..57bcc3bfa79905e55214554f36f81fdca18fd2e3 100644 (file)
@@ -30,50 +30,69 @@ import org.opendaylight.yangtools.yang.model.api.Module
 import org.opendaylight.yangtools.yang.model.api.SchemaContext
 
 import com.google.common.collect.ImmutableSet
+import static org.opendaylight.yangtools.yang.binding.BindingMapping.*
+import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider
+import com.google.common.base.Preconditions
 
 class YangModuleInfoTemplate {
-    val CLASS = "$YangModuleInfoImpl"
 
     val Module module
     val SchemaContext ctx
     val Map<String, String> importMap = new LinkedHashMap()
 
-    new (Module module, SchemaContext ctx) {
-        if (module == null) {
-            throw new IllegalArgumentException("Module reference cannot be NULL!")
-        }
+    @Property
+    val String packageName;
+
+    @Property
+    val String modelBindingProviderName;
+
+    new(Module module, SchemaContext ctx) {
+        Preconditions.checkArgument(module != null, "Module must not be null.");
         this.module = module
         this.ctx = ctx
+        _packageName = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
+        _modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»''';
     }
 
     def String generate() {
-        val String classBody = body().toString
-        '''
-        package «BindingGeneratorUtil.moduleNamespaceToPackageName(module)» ;
+        val body = '''
+            public final class «MODULE_INFO_CLASS_NAME» implements «YangModuleInfo.importedName» {
+
+                    private static final «YangModuleInfo.importedName» INSTANCE = new «MODULE_INFO_CLASS_NAME»();
 
-        «imports»
+                    private final Set<YangModuleInfo> importedModules;
 
-        «classBody»
+                    public static «YangModuleInfo.importedName» getInstance() {
+                    return INSTANCE;
+                    }
+
+                    «module.classBody»
+                }
+        '''
+        return '''
+            
+                package «packageName» ;
+                «imports»
+                «body»
         '''.toString
     }
 
-    def body() '''
-        public final class «CLASS» implements «YangModuleInfo.importedName» {
-
-            private static final «YangModuleInfo.importedName» INSTANCE = new «CLASS»();
+    def String generateModelProvider() {
+        '''
+            package «packageName»;
 
-            private final Set<YangModuleInfo> importedModules;
+            public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements «YangModelBindingProvider.name» {
 
-            public static «YangModuleInfo.importedName» getInstance() {
-                return INSTANCE;
+                public «YangModuleInfo.name» getModuleInfo() {
+                    return «MODULE_INFO_CLASS_NAME».getInstance();
+                }
             }
+        '''
 
-            «module.classBody»
-        }
-    '''
+    }
 
     private def CharSequence classBody(Module m) '''
-        private «CLASS»() {
+        private «MODULE_INFO_CLASS_NAME»() {
             «IF m.imports.size != 0»
                 «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
                 «FOR imp : m.imports»
@@ -87,24 +106,23 @@ class YangModuleInfoTemplate {
                                 «sorted.put(module.revision, module)»
                             «ENDIF»
                         «ENDFOR»
-                        set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(sorted.lastEntry().value)».«CLASS».getInstance());
+                        set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(sorted.lastEntry().value)».«MODULE_INFO_CLASS_NAME».getInstance());
                     «ELSE»
-                        set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(ctx.findModuleByName(name, rev))».«CLASS».getInstance());
+                        set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(ctx.findModuleByName(name, rev))».«MODULE_INFO_CLASS_NAME».getInstance());
                     «ENDIF»
                 «ENDFOR»
                 importedModules = «ImmutableSet.importedName».copyOf(set);
             «ELSE»
                 importedModules = «Collections.importedName».emptySet();
             «ENDIF»
-
-            «InputStream.importedName» stream = «CLASS».class.getResourceAsStream("«sourcePath»");
+            «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream("«sourcePath»");
             if (stream == null) {
                 throw new IllegalStateException("Resource «sourcePath» is missing");
             }
             try {
                 stream.close();
             } catch («IOException.importedName» e) {
-                // Resource leak, but there is nothing we can do
+            // Resource leak, but there is nothing we can do
             }
         }
 
@@ -126,7 +144,7 @@ class YangModuleInfoTemplate {
 
         @Override
         public «InputStream.importedName» getModuleSourceStream() throws IOException {
-            «InputStream.importedName» stream = «CLASS».class.getResourceAsStream("«sourcePath»");
+            «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream("«sourcePath»");
             if (stream == null) {
                 throw new «IOException.importedName»("Resource «sourcePath» is missing");
             }
@@ -138,7 +156,7 @@ class YangModuleInfoTemplate {
             return importedModules;
         }
     '''
-    
+
     def getSourcePath() {
         return "/" + module.moduleSourcePath.replace(java.io.File.separatorChar, '/')
     }
@@ -151,7 +169,6 @@ class YangModuleInfoTemplate {
                 «ENDIF»
             «ENDFOR»
         «ENDIF»
-        
     '''
 
     final protected def importedName(Class<?> cls) {
@@ -226,7 +243,7 @@ class YangModuleInfoTemplate {
             return "?";
         }
         val StringBuilder builder = new StringBuilder();
-        
+
         var int i = 0;
         for (pType : pTypes) {
             val Type t = pTypes.get(i)
index 9320e5af202ddc7e2433a8f753e3d65525527841..704596ec74dee97074ff8e935fbfa32af2eb8c96 100644 (file)
@@ -27,6 +27,8 @@ import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImp
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.java.api.generator.GeneratorJavaFile;
 import org.opendaylight.yangtools.sal.java.api.generator.YangModuleInfoTemplate;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
@@ -35,7 +37,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
 
 public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware {
     private static final String FS = File.separator;
@@ -45,6 +50,7 @@ public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware
 
     private static final Logger logger = LoggerFactory.getLogger(CodeGeneratorImpl.class);
     private MavenProject mavenProject;
+    private File resourceBaseDir;
 
     @Override
     public Collection<File> generateSources(final SchemaContext context, final File outputDir,
@@ -69,11 +75,32 @@ public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware
         }
 
         List<File> result = generator.generateToFile(outputBaseDir, persistentSourcesDir);
+
+        result.addAll(generateModuleInfos(outputBaseDir, yangModules, context));
+        return result;
+    }
+
+    private Collection<? extends File> generateModuleInfos(File outputBaseDir, Set<Module> yangModules,
+            SchemaContext context) {
+        Builder<File> result = ImmutableSet.builder();
+        Builder<String> bindingProviders = ImmutableSet.builder();
         for (Module module : yangModules) {
-            // TODO: add YangModuleInfo class
-            result.add(generateYangModuleInfo(outputBaseDir, module, context));
+            result.addAll(generateYangModuleInfo(outputBaseDir, module, context, bindingProviders));
         }
-        return result;
+
+        result.add(writeMetaInfServices(resourceBaseDir, YangModelBindingProvider.class, bindingProviders.build()));
+        return result.build();
+    }
+
+    private File writeMetaInfServices(File outputBaseDir, Class<YangModelBindingProvider> serviceClass,
+            ImmutableSet<String> services) {
+        File metainfServicesFolder = new File(outputBaseDir, "META-INF" + File.separator + "services");
+        metainfServicesFolder.mkdirs();
+        File serviceFile = new File(metainfServicesFolder, serviceClass.getName());
+
+        String src = Joiner.on('\n').join(services);
+
+        return writeFile(serviceFile, src);
     }
 
     public static final String DEFAULT_OUTPUT_BASE_DIR_PATH = "target" + File.separator + "generated-sources"
@@ -93,7 +120,8 @@ public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware
     }
 
     @Override
-    public void setLog(Log log) {}
+    public void setLog(Log log) {
+    }
 
     @Override
     public void setAdditionalConfig(Map<String, String> additionalConfiguration) {
@@ -102,7 +130,7 @@ public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware
 
     @Override
     public void setResourceBaseDir(File resourceBaseDir) {
-        // no resource processing necessary
+        this.resourceBaseDir = resourceBaseDir;
     }
 
     @Override
@@ -116,29 +144,48 @@ public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware
         this.buildContext = Preconditions.checkNotNull(buildContext);
     }
 
-    private File generateYangModuleInfo(File outputBaseDir, Module module, SchemaContext ctx) {
+    private Set<File> generateYangModuleInfo(File outputBaseDir, Module module, SchemaContext ctx,
+            Builder<String> providerSourceSet) {
+        Builder<File> generatedFiles = ImmutableSet.<File> builder();
+
         final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, ctx);
-        String generatedCode = template.generate();
-        if (generatedCode.isEmpty()) {
+        String moduleInfoSource = template.generate();
+        if (moduleInfoSource.isEmpty()) {
             throw new IllegalStateException("Generated code should not be empty!");
         }
+        String providerSource = template.generateModelProvider();
+
+        final File packageDir = GeneratorJavaFile.packageToDirectory(outputBaseDir,
+                BindingGeneratorUtil.moduleNamespaceToPackageName(module));
+
+        generatedFiles.add(writeJavaSource(packageDir, BindingMapping.MODULE_INFO_CLASS_NAME, moduleInfoSource));
+        generatedFiles
+                .add(writeJavaSource(packageDir, BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME, providerSource));
+        providerSourceSet.add(template.getModelBindingProviderName());
+
+        return generatedFiles.build();
 
-        final File packageDir = GeneratorJavaFile.packageToDirectory(outputBaseDir, BindingGeneratorUtil.moduleNamespaceToPackageName(module));
+    }
 
-        final File file = new File(packageDir, "$YangModuleInfoImpl.java");
+    private File writeJavaSource(File packageDir, String className, String source) {
+        final File file = new File(packageDir, className + ".java");
+        writeFile(file, source);
+        return file;
+    }
+
+    private File writeFile(File file, String source) {
         try (final OutputStream stream = buildContext.newFileOutputStream(file)) {
             try (final Writer fw = new OutputStreamWriter(stream)) {
                 try (final BufferedWriter bw = new BufferedWriter(fw)) {
-                    bw.write(generatedCode);
+                    bw.write(source);
                 }
             } catch (Exception e) {
-                // TODO handle exception
+                logger.error("Could not write file: {}",file,e);
             }
         } catch (Exception e) {
-            // TODO handle exception
+            logger.error("Could not create file: {}",file,e);
         }
         return file;
-
     }
 
 }
diff --git a/integration-test/pom.xml b/integration-test/pom.xml
new file mode 100644 (file)
index 0000000..6080092
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- Copyright (c) 2013 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yangtools</artifactId>
+        <version>0.6.2-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>integration-tests</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>yang-runtime-tests</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-common</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-model-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-model-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-binding</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-parser-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-parser-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin-spi</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <!-- Plugin-related dependencies -->
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-core</artifactId>
+                <version>3.0.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-plugin-api</artifactId>
+                <version>3.0.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>binding-generator-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
diff --git a/integration-test/yang-runtime-tests/pom.xml b/integration-test/yang-runtime-tests/pom.xml
new file mode 100644 (file)
index 0000000..0d6cbc4
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- Copyright (c) 2013 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>integration-tests</artifactId>
+        <version>0.6.2-SNAPSHOT</version>
+    </parent>
+    <artifactId>yang-runtime-tests</artifactId>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-binding</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>binding-generator-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-topology</artifactId>
+            <version>2013.10.21.2-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+               <groupId>org.mockito</groupId>
+               <artifactId>mockito-all</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/tests/ModelDiscoveryTest.java b/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/tests/ModelDiscoveryTest.java
new file mode 100644 (file)
index 0000000..d9944bc
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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.it.yang.runtime.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+
+public class ModelDiscoveryTest {
+
+    public static final YangModuleInfo TOPOLOGY_OLD_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev130712.$YangModuleInfoImpl
+            .getInstance();
+    public static final YangModuleInfo TOPOLOGY_NEW_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.$YangModuleInfoImpl
+            .getInstance();
+
+    public static final String METAINF_PATH = "META-INF/services/" + YangModelBindingProvider.class.getName();
+
+    @Test
+    public void discoveryUsing_BindingReflections_TCCL() {
+
+        ImmutableSet<YangModuleInfo> moduleInfoSet = BindingReflections.loadModuleInfos();
+        assertNotNull(moduleInfoSet);
+        assertFalse(moduleInfoSet.isEmpty());
+        assertTrue(moduleInfoSet.contains(TOPOLOGY_NEW_MODULE));
+    }
+
+    @Test
+    public void discoveryUsing_BindingReflections_classloader_partialServiceMetadata() throws Exception {
+
+        ClassLoader topologyModelClassLoader = new ClassLoader(Thread.currentThread().getContextClassLoader()) {
+
+            @Override
+            public Enumeration<URL> getResources(String name) throws IOException {
+                if (METAINF_PATH.equals(name)) {
+                    Vector<URL> topologyUrlVector = new Vector<>();
+                    topologyUrlVector.add(TOPOLOGY_NEW_MODULE.getClass().getResource("/" + METAINF_PATH));
+                    return topologyUrlVector.elements();
+                }
+                return super.getResources(name);
+            }
+        };
+
+        ImmutableSet<YangModuleInfo> moduleInfoSet = BindingReflections.loadModuleInfos(topologyModelClassLoader);
+        assertNotNull(moduleInfoSet);
+        assertFalse(moduleInfoSet.isEmpty());
+        assertTrue(moduleInfoSet.contains(TOPOLOGY_NEW_MODULE));
+    }
+
+    @Test
+    public void moduleInfoBackedContextTCCL() throws Exception {
+
+        ModuleInfoBackedContext context = ModuleInfoBackedContext.create(GeneratedClassLoadingStrategy.getAlwaysFailClassLoadingStrategy());
+
+        ImmutableSet<YangModuleInfo> moduleInfoSet = BindingReflections.loadModuleInfos();
+
+        context.addModuleInfos(moduleInfoSet);
+        assertNotNull(moduleInfoSet);
+        assertFalse(moduleInfoSet.isEmpty());
+        assertTrue(moduleInfoSet.contains(TOPOLOGY_NEW_MODULE));
+
+        Class<?> linkClass = context.loadClass(Link.class.getName());
+        assertNotNull(linkClass);
+        assertEquals(Link.class, linkClass);
+        Optional<SchemaContext> schemaContext = context.tryToCreateSchemaContext();
+        assertNotNull(schemaContext);
+        assertNotNull(schemaContext.get());
+    }
+
+}
diff --git a/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/tests/MultipleRevisionsSupportTest.java b/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/tests/MultipleRevisionsSupportTest.java
new file mode 100644 (file)
index 0000000..654dc11
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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.it.yang.runtime.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
+
+public class MultipleRevisionsSupportTest {
+
+    public static final YangModuleInfo TOPOLOGY_OLD_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev130712.$YangModuleInfoImpl
+            .getInstance();
+    public static final YangModuleInfo TOPOLOGY_NEW_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.$YangModuleInfoImpl
+            .getInstance();
+
+    public static final Set<YangModuleInfo> DEPENDENCIES = ImmutableSet.<YangModuleInfo> builder() //
+            .addAll(TOPOLOGY_OLD_MODULE.getImportedModules()) //
+            .addAll(TOPOLOGY_NEW_MODULE.getImportedModules()).build();
+
+    @Test
+    public void dependenciesOlderNewer() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void dependenciesNewerOlder() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void newerOlderDependencies() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void newerDependenciesOlder() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void OlderNewerDependencies() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void olderDependenciesNewer() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    private SchemaContext contextVerified(List<InputStream> streams) {
+        YangParserImpl parser = new YangParserImpl();
+        Set<Module> modules = parser.parseYangModelsFromStreams(streams);
+        assertNotNull(modules);
+        assertFalse(modules.isEmpty());
+        SchemaContext context = parser.resolveSchemaContext(modules);
+        assertNotNull(context);
+        return context;
+    }
+
+    private void verifyBindingDifference(SchemaContext schemaContext, YangModuleInfo oldModule, YangModuleInfo newModule) {
+
+        Map<Module, ModuleContext> generatedTypes = generatedTypesVerified(schemaContext, oldModule, newModule);
+
+    }
+
+    private Map<Module, ModuleContext> generatedTypesVerified(SchemaContext schemaContext, YangModuleInfo oldModule,
+            YangModuleInfo newModule) {
+        BindingGeneratorImpl generator = new BindingGeneratorImpl();
+        generator.generateTypes(schemaContext);
+        return generator.getModuleContexts();
+    }
+
+    private void verifySchemaDifference(SchemaContext context, YangModuleInfo topologyOldModule,
+            YangModuleInfo topologyNewModule) {
+        Module oldModel = context.findModuleByNamespaceAndRevision(//
+                URI.create(TOPOLOGY_OLD_MODULE.getNamespace()), QName.parseRevision(TOPOLOGY_OLD_MODULE.getRevision()));
+
+        Module newModel = context.findModuleByNamespaceAndRevision(//
+                URI.create(TOPOLOGY_NEW_MODULE.getNamespace()), QName.parseRevision(TOPOLOGY_NEW_MODULE.getRevision()));
+
+        SchemaNode oldNode = findSchemaNode(oldModel, "network-topology", "topology", "link");
+        SchemaNode newNode = findSchemaNode(newModel, "network-topology", "topology", "link");
+
+        assertNotNull(oldNode);
+        assertNotNull(newNode);
+
+        assertDeepRevision(TOPOLOGY_OLD_MODULE.getRevision(), oldNode);
+        assertDeepRevision(TOPOLOGY_NEW_MODULE.getRevision(), newNode);
+    }
+
+    private static void assertDeepRevision(String revision, SchemaNode node) {
+        assertEquals("Wrong revision: " + node.getPath(), revision, node.getQName().getFormattedRevision());
+        if (node instanceof DataNodeContainer) {
+            for (DataSchemaNode child : ((DataNodeContainer) node).getChildNodes()) {
+                assertDeepRevision(revision, child);
+            }
+        } else if (node instanceof ChoiceNode) {
+            for (DataSchemaNode child : ((ChoiceNode) node).getCases()) {
+                assertDeepRevision(revision, child);
+            }
+        }
+    }
+
+    private static final SchemaNode findSchemaNode(DataNodeContainer container, String... pathArgs) {
+        DataNodeContainer previous = container;
+
+        SchemaNode result = (container instanceof SchemaNode) ? (SchemaNode) container : null;
+        for (String arg : pathArgs) {
+            if (previous == null) {
+                return null;
+            }
+            Set<DataSchemaNode> childs = previous.getChildNodes();
+            for (DataSchemaNode child : childs) {
+                if (child.getQName().getLocalName().equals(arg)) {
+                    if (child instanceof DataNodeContainer) {
+                        previous = (DataNodeContainer) child;
+                    } else {
+                        previous = null;
+                    }
+                    result = child;
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    private static final Iterable<? extends InputStream> toInputStreams(Set<YangModuleInfo> moduleInfos)
+            throws Exception {
+        Builder<InputStream> streams = ImmutableList.<InputStream> builder();
+        for (YangModuleInfo yangModuleInfo : moduleInfos) {
+            streams.add(yangModuleInfo.getModuleSourceStream());
+        }
+        return streams.build();
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index b79a027190ac33a9daac5bbf3f31a64504c9692c..2b9136de821d929111dfcd85f947136db6c92164 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -58,6 +58,7 @@
         <module>code-generator</module>
         <module>model</module>
         <module>restconf</module>
+        <module>integration-test</module>
         <module>mockito-configuration</module>
         <!-- module>third-party</module -->
     </modules>
index 5b1d45a679f762e2f0eaad464da2f15c7feac838..2f1dfb275efedaa5cbc42020f4b505fb896b491e 100644 (file)
@@ -35,7 +35,8 @@ public final class BindingMapping {
 
     private static final Splitter SPACE_SPLITTER = Splitter.on(" ").omitEmptyStrings().trimResults();
 
-    public static final String MODULE_INFO_CLASS_NAME = "$ModuleInfoImpl";
+    public static final String MODULE_INFO_CLASS_NAME = "$YangModuleInfoImpl";
+    public static final String MODEL_BINDING_PROVIDER_CLASS_NAME = "$YangModelBindingProvider";
 
     public static final String getMethodName(QName name) {
         checkArgument(name != null, "Name should not be null.");
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/YangModelBindingProvider.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/YangModelBindingProvider.java
new file mode 100644 (file)
index 0000000..fc05db8
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.binding;
+
+import java.util.ServiceLoader;
+
+/**
+ *
+ * Provider of YangModuleInfo for specified package / model.
+ *
+ * Implementation of this interface should be discoverable
+ * via {@link ServiceLoader}
+ *
+ *
+ *
+ */
+public interface YangModelBindingProvider {
+
+    /**
+     * YangModuleInfo associated to package
+     *
+     * @return
+     */
+    YangModuleInfo getModuleInfo();
+
+}
index 11c4842501486c0044685185e1ef5256f00a4944..48ebcdea4390a25b084fbea4a249dc4e251634c3 100644 (file)
@@ -11,6 +11,7 @@ import java.beans.MethodDescriptor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.util.ServiceLoader;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -28,6 +29,7 @@ import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
 import org.opendaylight.yangtools.yang.binding.annotations.ModuleQName;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -40,6 +42,8 @@ import com.google.common.base.Preconditions;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
 
 public class BindingReflections {
 
@@ -214,5 +218,25 @@ public class BindingReflections {
         checkArgument(potentialNotification != null);
         return Notification.class.isAssignableFrom(potentialNotification);
     }
+    
+    public static ImmutableSet<YangModuleInfo> loadModuleInfos() {
+        return loadModuleInfos(Thread.currentThread().getContextClassLoader());
+    }
+    
+    public static ImmutableSet<YangModuleInfo> loadModuleInfos(ClassLoader loader) {
+        Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.<YangModuleInfo>builder();
+        ServiceLoader<YangModelBindingProvider> serviceLoader = ServiceLoader.load(YangModelBindingProvider.class, loader);
+        for(YangModelBindingProvider bindingProvider : serviceLoader) {
+            collectYangModuleInfo(bindingProvider.getModuleInfo(),moduleInfoSet);
+        }
+        return  moduleInfoSet.build();
+    }
+
+    private static void collectYangModuleInfo(YangModuleInfo moduleInfo, Builder<YangModuleInfo> moduleInfoSet) {
+        moduleInfoSet.add(moduleInfo);
+        for(YangModuleInfo dependency : moduleInfo.getImportedModules()) {
+            collectYangModuleInfo(dependency, moduleInfoSet);
+        }
+    }
 
 }