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());
}
return TCCL_STRATEGY;
}
+ public static final GeneratedClassLoadingStrategy getAlwaysFailClassLoadingStrategy() {
+ return ALWAYS_FAIL_STRATEGY;
+ }
+
private static final class TCCLClassLoadingStrategy extends GeneratedClassLoadingStrategy {
@Override
--- /dev/null
+/*
+ * 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) {
+
+ }
+}
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»
«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
}
}
@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");
}
return importedModules;
}
'''
-
+
def getSourcePath() {
return "/" + module.moduleSourcePath.replace(java.io.File.separatorChar, '/')
}
«ENDIF»
«ENDFOR»
«ENDIF»
-
'''
final protected def importedName(Class<?> cls) {
return "?";
}
val StringBuilder builder = new StringBuilder();
-
+
var int i = 0;
for (pType : pTypes) {
val Type t = pTypes.get(i)
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;
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;
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,
}
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"
}
@Override
- public void setLog(Log log) {}
+ public void setLog(Log log) {
+ }
@Override
public void setAdditionalConfig(Map<String, String> additionalConfiguration) {
@Override
public void setResourceBaseDir(File resourceBaseDir) {
- // no resource processing necessary
+ this.resourceBaseDir = resourceBaseDir;
}
@Override
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;
-
}
}
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
<module>code-generator</module>
<module>model</module>
<module>restconf</module>
+ <module>integration-test</module>
<module>mockito-configuration</module>
<!-- module>third-party</module -->
</modules>
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.");
--- /dev/null
+/*
+ * 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();
+
+}
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;
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;
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 {
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);
+ }
+ }
}