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. c学习 - 第八章:函数

    8.7 数组作函数的参数 1.数组元素作函数的参数--值传递,单向传递 2.数组名做函数的参数--地址传送 (1)实参:数组名做实参,传递的是数组首元素的地址 (2)形参:使用同类型的数组名或指针变量 ...

  2. Templates and Default Arguments

    Default parameters for templates in C++: Like function default arguments, templates can also have de ...

  3. 【编程思想】【设计模式】【结构模式Structural】桥梁模式/桥接模式bridge

    Python版 https://github.com/faif/python-patterns/blob/master/structural/bridge.py #!/usr/bin/env pyth ...

  4. 用 WinUI 3 开发了一个摸鱼应用

    1. 开发了一个摸鱼 App 我做了一个简单的 App:摸鱼. 如上图所示,这个 App 就只有一个按钮,点击后假装开始 Windows Update,然后用户就可以光明正大地摸鱼了. 不要小看摸鱼, ...

  5. 『学了就忘』Linux系统管理 — 82、Linux中进程的查看(ps命令)

    目录 1.ps命令介绍 2.ps aux命令示例 3.ps -le命令示例 4.pstree命令 1.ps命令介绍 ps命令是用来静态显示系统中进程的命令. 不过这个命令有些特殊,它部分命令的选项前不 ...

  6. 【紧急】Log4j又发新版2.17.0,只有彻底搞懂漏洞原因,才能以不变应万变,小白也能看懂

    1 事件背景 经过一周时间的Log4j2 RCE事件的发酵,事情也变也越来越复杂和有趣,就连 Log4j 官方紧急发布了 2.15.0 版本之后没有过多久,又发声明说 2.15.0 版本也没有完全解决 ...

  7. java 多线程 发布订阅模式:发布者java.util.concurrent.SubmissionPublisher;订阅者java.util.concurrent.Flow.Subscriber

    1,什么是发布订阅模式? 在软件架构中,发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者).而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话 ...

  8. IPtables 之“四表五链”

    目录 架构图 IP tables 简介 包过滤防火墙 Iptables如何过滤 "四表" "五链" Iptables流程 架构图 公司架构模式(酒店迎宾比喻) ...

  9. 交通运输类文档下载——JT/T 808-2019、JT/T 809-2019文档分享

    JT/T 808-2019.JT/T 809-2019文档分享 网盘:https://pan.baidu.com/s/1vfgenani8WR3in2lua3qWQ提取码:fktd 官网下载808协议 ...

  10. typora 基本使用和漂亮的主题样式

    以下是使用博客园的markdown的效果: typora 基本使用和漂亮的主题样式 一.typora 基本使用 ps:文字排版,使用markdown nice 可以一键复制到公众号.知乎:https: ...