1.说明

ODL提供了Yang Tools工具从YANG文件生成Java类,
本文介绍使用Maven插件的方式生成,
基于yang-maven-plugin这个插件。

2.创建Maven工程

Eclipse -> File -> New -> Other... -> Maven -> Maven Project
创建一个简单Maven工程,
pom.xml如下:

<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>
<groupId>com.ai.prd.yang</groupId>
<artifactId>generate-yang-tools</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>使用ODL的Yang Tools工具,从yang文件生成Java类</description>
</project>

3.增加父pom

在pom.xml增加mdsal-parent作为父pom:

<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>mdsal-parent</artifactId>
<version>1.10.4</version>
<relativePath>../../parent</relativePath>
</parent>

查看父pom,发现依赖了yang-maven-plugin插件:

<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
<version>3.0.16</version>
</dependency>

以及在build配置了这个插件从YANG生成Java类:

<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
<version>3.0.16</version>
<executions>
<execution>
<id>binding</id>
<goals>
<goal>generate-sources</goal>
</goals>
<configuration>
<codeGenerators>
<generator>
<codeGeneratorClass>org.opendaylight.mdsal.binding.maven.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
<outputBaseDir>D:\Code\Work\ODL-Netconf\yang-demos\generate-yang-tools\target-ide/generated-sources/mdsal-binding</outputBaseDir>
<resourceBaseDir>D:\Code\Work\ODL-Netconf\yang-demos\generate-yang-tools\target-ide/generated-sources/spi</resourceBaseDir>
</generator>
</codeGenerators>
<inspectDependencies>true</inspectDependencies>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.opendaylight.mdsal</groupId>
<artifactId>maven-sal-api-gen-plugin</artifactId>
<version>2.0.17</version>
<scope>compile</scope>
</dependency>
</dependencies>
</plugin>

4.创建YANG文件

由于插件默认的YANG文件位置是src\main\yang,
所有在src\main\yang目录下新建文件acme-system.yang:

module acme-system {
namespace "http://acme.example.com/system";
prefix "acme"; organization "ACME Inc.";
contact "joe@acme.example.com";
description
"The module for entities implementing the ACME system."; revision 2007-06-09 {
description "Initial revision.";
} container system {
leaf host-name {
type string;
description "Hostname for this system";
} leaf-list domain-search {
type string;
description "List of domain names to search";
} container login {
leaf message {
type string;
description
"Message given at start of login session";
} list user {
key "name";
leaf name {
type string;
}
leaf full-name {
type string;
}
leaf class {
type string;
}
}
}
}
}

对该YANG文件的解读请参考:对YANG的解读(一)

5.运行Maven插件

运行Maven命令,
执行插件功能,
从YANG文件生成Java类:

mvn clean generate-sources

Maven编译成功日志:

[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< com.ai.prd.yang:generate-yang-tools >-----------------
[INFO] Building generate-yang-tools 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ generate-yang-tools ---
[INFO]
[INFO] --- maven-enforcer-plugin:3.0.0-M2:enforce (enforce-maven) @ generate-yang-tools ---
[INFO]
[INFO] --- maven-enforcer-plugin:3.0.0-M2:enforce (enforce-banned-dependencies) @ generate-yang-tools ---
[INFO]
[INFO] --- git-commit-id-plugin:3.0.1:revision (get-git-infos) @ generate-yang-tools ---
[INFO]
[INFO] --- jacoco-maven-plugin:0.8.5:prepare-agent (pre-unit-test) @ generate-yang-tools ---
[INFO] argLine set to -javaagent:C:\\developtools\\maven-repository\\org\\jacoco\\org.jacoco.agent\\0.8.5\\org.jacoco.agent-0.8.5-runtime.jar=destfile=D:\\Code\\Work\\ODL-Netconf\\yang-demos\\generate-yang-tools\\target\\code-coverage\\jacoco.exec,excludes=**/gen/**:**/generated-sources/**:**/generated-test-sources/**:**/yang-gen/**:**/yang-gen-config/**:**/yang-gen-sal/**:**/yang-gen-code/**:**/pax/**
[INFO]
[INFO] --- yang-maven-plugin:3.0.16:generate-sources (binding) @ generate-yang-tools ---
[INFO] yang-to-sources: Code generator instantiated from org.opendaylight.mdsal.binding.maven.api.gen.plugin.CodeGeneratorImpl
[INFO] yang-to-sources: Inspecting D:\Code\Work\ODL-Netconf\yang-demos\generate-yang-tools\src\main\yang
[INFO] yang-to-sources: Found 0 dependencies in 40.39 ms
[INFO] yang-to-sources: Project model files found: 1
[INFO] yang-to-sources: 1 YANG models processed in 110.2 ms
[INFO] yang-to-sources: Sources will be generated to D:\Code\Work\ODL-Netconf\yang-demos\generate-yang-tools\target\generated-sources\mdsal-binding
[INFO] Found 5 Binding types in 58.11 ms
[INFO] Generating 8 Binding source files into 3 directories
[INFO] yang-to-sources: Sources generated by org.opendaylight.mdsal.binding.maven.api.gen.plugin.CodeGeneratorImpl: 11 in 136.5 ms
[INFO]
[INFO] --- build-helper-maven-plugin:3.0.0:add-source (add-yang-sources) @ generate-yang-tools ---
[INFO] Source directory: D:\Code\Work\ODL-Netconf\yang-demos\generate-yang-tools\target\generated-sources\mdsal-binding added.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.983 s
[INFO] Finished at: 2021-01-27T15:47:28+08:00
[INFO] ------------------------------------------------------------------------

6.查看生成的Java类

Maven命令执行成功后,
生成的Java类在工程的target\generated-sources\mdsal-binding目录下,
然后是Java类的包路径:org\opendaylight\yang\gen\v1\http\acme\example\com\system\rev070609,
目录rev070609的详细文件如下:

.:
system/ '$YangModelBindingProvider.java' '$YangModuleInfoImpl.java'
AcmeSystemData.java System.java SystemBuilder.java ./system:
login/ Login.java LoginBuilder.java ./system/login:
User.java UserBuilder.java UserKey.java

原始的YANG文件也会输出到target\generated-sources\yang\META-INF\yang下面,
并且重命名为acme-system@2007-06-09.yang。

由于生成的Java类都比较大,
这里仅贴出Login.java和LoginBuilder.java两个类。
Login.java:

package org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.system;
import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.$YangModuleInfoImpl;
import org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.System;
import org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.system.login.User;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.ChildOf;
import org.opendaylight.yangtools.yang.binding.CodeHelpers;
import org.opendaylight.yangtools.yang.common.QName; /**
*
* <p>
* This class represents the following YANG schema fragment defined in module <b>acme-system</b>
* <pre>
* container login {
* leaf message {
* type string;
* }
* list user {
* key name;
* leaf name {
* type string;
* }
* leaf full-name {
* type string;
* }
* leaf class {
* type string;
* }
* }
* }
* </pre>The schema path to identify an instance is
* <i>acme-system/system/login</i>
*
* <p>To create instances of this class use {@link LoginBuilder}.
* @see LoginBuilder
*
*/
public interface Login
extends
ChildOf<System>,
Augmentable<Login>
{
public static final @NonNull QName QNAME = $YangModuleInfoImpl.qnameOf("login"); @Override
default Class<org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.system.Login> implementedInterface() {
return org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.system.Login.class;
} /**
* Message given at start of login session
*
*
*
* @return <code>java.lang.String</code> <code>message</code>, or <code>null</code> if not present
*/
@Nullable String getMessage(); /**
* @return <code>java.util.List</code> <code>user</code>, or <code>null</code> if not present
*/
@Nullable List<User> getUser(); /**
* @return <code>java.util.List</code> <code>user</code>, or an empty list if it is not present
*/
default @NonNull List<User> nonnullUser() {
return CodeHelpers.nonnull(getUser());
}
}

LoginBuilder.java:

package org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.system;
import com.google.common.base.MoreObjects;
import java.lang.Class;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.opendaylight.yang.gen.v1.http.acme.example.com.system.rev070609.system.login.User;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.binding.AbstractAugmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
import org.opendaylight.yangtools.yang.binding.CodeHelpers;
import org.opendaylight.yangtools.yang.binding.DataObject; /**
* Class that builds {@link LoginBuilder} instances. Overall design of the class is that of a
* <a href="https://en.wikipedia.org/wiki/Fluent_interface">fluent interface</a>, where method chaining is used.
*
* <p>
* In general, this class is supposed to be used like this template:
* <pre>
* <code>
* LoginBuilder createTarget(int fooXyzzy, int barBaz) {
* return new LoginBuilderBuilder()
* .setFoo(new FooBuilder().setXyzzy(fooXyzzy).build())
* .setBar(new BarBuilder().setBaz(barBaz).build())
* .build();
* }
* </code>
* </pre>
*
* <p>
* This pattern is supported by the immutable nature of LoginBuilder, as instances can be freely passed around without
* worrying about synchronization issues.
*
* <p>
* As a side note: method chaining results in:
* <ul>
* <li>very efficient Java bytecode, as the method invocation result, in this case the Builder reference, is
* on the stack, so further method invocations just need to fill method arguments for the next method
* invocation, which is terminated by {@link #build()}, which is then returned from the method</li>
* <li>better understanding by humans, as the scope of mutable state (the builder) is kept to a minimum and is
* very localized</li>
* <li>better optimization oportunities, as the object scope is minimized in terms of invocation (rather than
* method) stack, making <a href="https://en.wikipedia.org/wiki/Escape_analysis">escape analysis</a> a lot
* easier. Given enough compiler (JIT/AOT) prowess, the cost of th builder object can be completely
* eliminated</li>
* </ul>
*
* @see LoginBuilder
* @see Builder
*
*/
public class LoginBuilder implements Builder<Login> { private String _message;
private List<User> _user; Map<Class<? extends Augmentation<Login>>, Augmentation<Login>> augmentation = Collections.emptyMap(); public LoginBuilder() {
} public LoginBuilder(Login base) {
if (base instanceof AugmentationHolder) {
@SuppressWarnings("unchecked")
Map<Class<? extends Augmentation<Login>>, Augmentation<Login>> aug =((AugmentationHolder<Login>) base).augmentations();
if (!aug.isEmpty()) {
this.augmentation = new HashMap<>(aug);
}
}
this._message = base.getMessage();
this._user = base.getUser();
} public String getMessage() {
return _message;
} public List<User> getUser() {
return _user;
} @SuppressWarnings({ "unchecked", "checkstyle:methodTypeParameterName"})
public <E$$ extends Augmentation<Login>> E$$ augmentation(Class<E$$> augmentationType) {
return (E$$) augmentation.get(CodeHelpers.nonNullValue(augmentationType, "augmentationType"));
} public LoginBuilder setMessage(final String value) {
this._message = value;
return this;
}
public LoginBuilder setUser(final List<User> values) {
this._user = values;
return this;
} public LoginBuilder addAugmentation(Class<? extends Augmentation<Login>> augmentationType, Augmentation<Login> augmentationValue) {
if (augmentationValue == null) {
return removeAugmentation(augmentationType);
} if (!(this.augmentation instanceof HashMap)) {
this.augmentation = new HashMap<>();
} this.augmentation.put(augmentationType, augmentationValue);
return this;
} public LoginBuilder removeAugmentation(Class<? extends Augmentation<Login>> augmentationType) {
if (this.augmentation instanceof HashMap) {
this.augmentation.remove(augmentationType);
}
return this;
} @Override
public Login build() {
return new LoginImpl(this);
} private static final class LoginImpl
extends AbstractAugmentable<Login>
implements Login { private final String _message;
private final List<User> _user; LoginImpl(LoginBuilder base) {
super(base.augmentation);
this._message = base.getMessage();
this._user = base.getUser();
} @Override
public String getMessage() {
return _message;
} @Override
public List<User> getUser() {
return _user;
} private int hash = 0;
private volatile boolean hashValid = false; @Override
public int hashCode() {
if (hashValid) {
return hash;
} final int prime = 31;
int result = 1;
result = prime * result + Objects.hashCode(_message);
result = prime * result + Objects.hashCode(_user);
result = prime * result + Objects.hashCode(augmentations()); hash = result;
hashValid = true;
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DataObject)) {
return false;
}
if (!Login.class.equals(((DataObject)obj).implementedInterface())) {
return false;
}
Login other = (Login)obj;
if (!Objects.equals(_message, other.getMessage())) {
return false;
}
if (!Objects.equals(_user, other.getUser())) {
return false;
}
if (getClass() == obj.getClass()) {
// Simple case: we are comparing against self
LoginImpl otherImpl = (LoginImpl) obj;
if (!Objects.equals(augmentations(), otherImpl.augmentations())) {
return false;
}
} else {
// Hard case: compare our augments with presence there...
for (Map.Entry<Class<? extends Augmentation<Login>>, Augmentation<Login>> e : augmentations().entrySet()) {
if (!e.getValue().equals(other.augmentation(e.getKey()))) {
return false;
}
}
// .. and give the other one the chance to do the same
if (!obj.equals(this)) {
return false;
}
}
return true;
} @Override
public String toString() {
final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper("Login");
CodeHelpers.appendValue(helper, "_message", _message);
CodeHelpers.appendValue(helper, "_user", _user);
CodeHelpers.appendValue(helper, "augmentation", augmentations().values());
return helper.toString();
}
}
}

7.问题解决

在Eclipse缺少某些插件时,
会报Maven的一些goals无法执行的错误,
下面配置去掉Eclipse中的pom报错,
并且不影响Maven命令执行结果:

<build>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
com.github.spotbugs
</groupId>
<artifactId>
spotbugs-maven-plugin
</artifactId>
<versionRange>
[3.1.12.2,)
</versionRange>
<goals>
<goal>check</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-checkstyle-plugin
</artifactId>
<versionRange>
[3.1.1,)
</versionRange>
<goals>
<goal>check</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

8.使用ODL提供的样例工程

ODL提供了一个和上面类似的工程,
下载地址:https://github.com/opendaylight/controller/releases?after=v2.0.5
选择下载版本release/sodium-sr4,
这个是钠版本的稳定发布版本,
且支持JDK 1.8和Maven 3.6.9,
这是由于在父pom中指定了requireJavaVersion为1.8.0,
以及requireMavenVersion大于等于3.5.0。

<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M2</version>
<executions>
<execution>
<id>enforce-maven</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>1.8.0</version>
</requireJavaVersion>
<requireMavenVersion>
<version>[3.5.0,)</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
<execution>
<id>enforce-banned-dependencies</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<message>Please always use mockito-core instead of mockito-all (see https://bugs.opendaylight.org/show_bug.cgi?id=7662), and spotbugs:annotations instead of findbugs:annotations</message>
<excludes>
<exclude>org.mockito:mockito-all</exclude>
<exclude>com.google.code.findbugs:annotations</exclude>
</excludes>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>

9.使用样例工程

下载的controller-release-sodium-sr4.zip解压到本地,
找到toaster工程:
D:\temp\controller-release-sodium-sr4\opendaylight\md-sal\samples\toaster
然后在工程下面执行编译命令即可:

mvn clean generate-sources

也可以把工程复制出来单独编译,
也可以把工程导入Eclipse编译,
生成的结果和上面类似。

10.参考文章

使用yangtools将yang文件转化成javaYang Tools Github

YangTools从YANG生成Java类(Maven)的更多相关文章

  1. Jsonschema2pojo从JSON生成Java类(Maven)

    1.说明 jsonschema2pojo工具可以从JSON Schema(或示例JSON文件)生成Java类型, 并且可以配置生成Jackson 1.x,Jackson 2.x, Moshi 1.x或 ...

  2. Jsonschema2pojo从JSON生成Java类(命令行)

    1.说明 jsonschema2pojo工具可以从JSON Schema(或示例JSON文件)生成Java类型, 在文章Jsonschema2pojo从JSON生成Java类(Maven) 已经介绍过 ...

  3. mybits根据表自动生成 java类和mapper 文件

    mybits根据表自动生成 java类和mapper 文件 我这个脑子啊,每次创建新的工程都会忘记是怎么集成mybits怎么生成mapper文件的,so today , I can't write t ...

  4. mybatis怎样自动生成java类,配置文件?

    其实没有什么东西是可以自动生成的,只不过是别人已经写好了,你调用罢了. 所以想要mybatis自动生成java类,配置文件等,就必须要一些配置和一些jar包.当然这些配置也很简单. 为了有个初步的认识 ...

  5. JSP-讲解(生成java类、静态导入与动态导入)

    一.JSP技术简介 JSP是Java Server Page的缩写,它是Servlet的扩展,它的作用是简化网站的创建和维护. JSP是HTML代码与Java代码的混合体. JSP文件通常以JSP或J ...

  6. 根据XML文件 生成 java类

    最近一直在做关于webservice 的项目,这种项目最麻烦的就是根据对方的要求产生XML,文档里面虽然有XML结构,但是要转化为java里面的实体实在费劲, 有个自动化的工具就好了,半自动化也好,省 ...

  7. rpc框架: thrift/avro/protobuf 之maven插件生成java类

    thrift.avro.probobuf 这几个rpc框架的基本思想都差不多,先定义IDL文件,然后由各自的编译器(或maven插件)生成目标语言的源代码,但是,根据idl生成源代码这件事,如果每次都 ...

  8. Hibernate、Mybatis 通过数据库表反向生成java类和配置

    一.通过MyEclipse生成Hibernate类文件和hbm.xml文件,或者annotation文件    (转载孙宇老师的文章) 二.Mybatis生成实体类和配置文件: myeclipse下生 ...

  9. 根据数据库表结构生成java类

    import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWrit ...

随机推荐

  1. pop回指定控制器

    //OCNSArray *array = [NSMutableArray new]; array = self.navigationController.viewControllers; //1.返回 ...

  2. vue 第三方图标库

    "font-awesome": "^4.7.0", "dependencies": { "axios": "^ ...

  3. 关于ssh-keygen 生成的key以“BEGIN OPENSSH PRIVATE KEY”开头

    现在使用命令 ssh-keygen -t rsa  生成ssh,默认是以新的格式生成,id_rsa的第一行变成了"BEGIN OPENSSH PRIVATE KEY" 而不在是&q ...

  4. HDC2021技术分论坛:如何高效完成HarmonyOS分布式应用测试?

    作者:liuxun,HarmonyOS测试架构师 HarmonyOS是新一代的智能终端操作系统,给开发者提供了设备发现.设备连接.跨设备调用等丰富的分布式API.随着越来越多的开发者投入到Harmon ...

  5. shell脚本 安全删除MySQL大表

    一.简介 源码地址 日期:2018/4/12 介绍:工具用于安全删除MySQL表,对于一些特定场景可能有用 应用场景:大批删除不走索引但是有主键的场景[可以是单列索引,也可是多列索引] 实现思路:根据 ...

  6. 数据恢复binlog2sql

    目录 一.原理及其使用 用途 闪回原理简析 binlog 有三种可选的格式: 来实例演习下来实例演习下 二.准备工作 一.原理及其使用 生产上误删数据.误改数据的现象也是时常发生的现象,作为运维这时候 ...

  7. Mysql资料 慢查询

    目录 一.简介 二.查询 三.开启 永久配置 临时配置 四.测试 一.简介 MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句. 具体环 ...

  8. rpm-build方式制作rpm包

    目录 一.简介 二.具体操作 一.简介 可以将编译完成的服务打成rpm包放到私有仓库了,用于自定义的各种软件进行安装部署配置. 二.具体操作 1.安装软件,这个命令将构建rpm包 yum -y ins ...

  9. CF935B Fafa and the Gates 题解

    Content 一个动点 \(F\) 一开始在平面直角坐标系上原点的位置,随后它会移动 \(n\) 次,每次只能向上走或者向右走 \(1\) 个单位,求经过直线 \(y=x\) 的次数. 数据范围:\ ...

  10. BZOJ 题目乱做

    记录一点在 BZOJ 上做的题. 众所周知原 BZOJ 炸掉了,于是跑去了 HydroOJ 的 BZOJ 域上面做. 目录 P1001 [Beijing2006]狼抓兔子 P1002 [FJOI200 ...