Opendaylight课堂之深度剖析toaster(二)
2017-12-11 21:57
232 查看
接上一篇内容
一、实现服务--mytoaster-provider(核心内容)
1、在目录demo2中增加mytoaster-provider目录以及子目录、文件:
mytoaster-provider
├── pom.xml
└── src
└── main
├── java
└── resources
2、Pom文件主要修改内容(参考sample中):
3、增加源码文件,org/opendaylight/controller/demo2/mytoaster/provider/MyOpenDaylightToaster.java
在开始写代码前介绍一下在java1.7新引入AutoCloseable接口。在java1.7之前释放资源,比如关闭文件句柄,一般写道try-catch中的finally中,但如果在finally中出现异常,那么这个资源就无法回收了。因此在java1.7中增加了AutoCloseable接口,以解决这种问题,用于在最后回收资源,感觉像C++中的析构函数。(但是AutoCloseable出现异常呢?等待高手指点)
我们知道对于OSGi框架最小调度单元是bundle,因此我们先现实mytoaster-provider这个bundle,使其能够在odl中跑起来。为了实现这个功能,需要实现以下三步骤:
1)AutoCloseable接口。
2) 注册bundle。在碳版本中,bundle的注册是通过blueprint方式,具体blueprint使用可以参考如下两个地址:IBM开发者社区 和Opendaylight官方社区。
3)Feature定义。
具体代码如下:
注册bundle,在resources目录中创建org/opendaylight/blueprint/mytoaster-provider.xml,具体文件内容如下:
经过上面两步骤,就可以完成了bundle基本功能了,比之前的配置子系统要简单很多!!进入mytoaster-provider,进行编译,编译过程中应该没有任何问题,接下来需要设置feature。
设置feature需要做一下修改:
1) Feature定义是在目录controller/features,我们的mytoaster是定义在mdsal中,因此进入controller/features/mdsal增加我们的feature。修改mdsal目录中pom文件,增加<module>odl-mytoaster</module>
2) 由于碳版本中karaf升级到4.0版本(碳版本是过渡版本),feature定义方式存在两种方式。在karaf4下面需要将各个feature独立定义在以俄国目录中,例如是odl-toaster目录。碳之前版本是在mdsal/feature/src/main/features.xml文件中定义。由于对karaf4.0不是很了解,这里只介绍旧方式,即在features.xml文件中定义。增加features定义:
在mdsal/features-mdsal/pom.xml文件中增加依赖:
在mdsal/features-mdsal/src/main/features/features.xml文件中增加feature定义:
以上内容就是feature的定义,在mdsal目录中进行编译应该可以正常编译出来。虽然能编译出来但是还不能运行,因为从github下载controller代码,并没有依赖上restconf,因此需要作如下修改:
3) 修改controller/karaf/opendaylight-karaf目录中的pom文件,增加如下内容:
经过以上内容修改,把修改的模块重新编译一次,应该就能运行起来了。至此修改编译的目录以及顺序如下:
demo2/mytoaster,demo2/mytoaster-provider,features/mdsal,karaf/opendaylight-karaf。
编译完成之后,启动karaf,如下:
我们是postman获取一下toaster基本信息,如何返回和下图一致,则说明正确:
至此服务的注册已经完成,接下来就是要是服务。我们都知道odl采用rpc的方式提供服务,因此我们要把yang模型中定义的所有rpc都要实现。在MyOpendaylightToaster实现MytoasterService接口,并且添加rpc的实现如下(只显示修改内容):
到目前为止,rpc的主要服务均以实现,但是还存在一个问题,我们的rpc已经实现了,但是如何让mdsal知道这个rpc存在呢?答案是:通过blueprint进行注册。
只需要在mytoaster-provider.xml文件中增加: <odl:rpc-implementation ref="mytoaster"/>。
接下来我们来验证结果:
Url:http://localhost:8080/restconf/operations/mytoaster:make-toast
可以看到Break is out!!Please take it
是在15秒之后打印出来的,但是返回结果立即返回并且是就餐者请稍等片刻。这样我们就实现了一个简单rpc服务以及调用。
一、实现服务--mytoaster-provider(核心内容)
1、在目录demo2中增加mytoaster-provider目录以及子目录、文件:
mytoaster-provider
├── pom.xml
└── src
└── main
├── java
└── resources
2、Pom文件主要修改内容(参考sample中):
<groupId>org.opendaylight.controller.demo2</groupId> <artifactId>mytoaster-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>bundle</packaging> <dependencies> <dependency><!-- mytoaster-provide需要依赖mytoaster定义的服务 --> <groupId>${project.groupId}</groupId> <artifactId>mytoaster</artifactId> <version>${project.version}</version> </dependency> .... </dependencies>
3、增加源码文件,org/opendaylight/controller/demo2/mytoaster/provider/MyOpenDaylightToaster.java
在开始写代码前介绍一下在java1.7新引入AutoCloseable接口。在java1.7之前释放资源,比如关闭文件句柄,一般写道try-catch中的finally中,但如果在finally中出现异常,那么这个资源就无法回收了。因此在java1.7中增加了AutoCloseable接口,以解决这种问题,用于在最后回收资源,感觉像C++中的析构函数。(但是AutoCloseable出现异常呢?等待高手指点)
我们知道对于OSGi框架最小调度单元是bundle,因此我们先现实mytoaster-provider这个bundle,使其能够在odl中跑起来。为了实现这个功能,需要实现以下三步骤:
1)AutoCloseable接口。
2) 注册bundle。在碳版本中,bundle的注册是通过blueprint方式,具体blueprint使用可以参考如下两个地址:IBM开发者社区 和Opendaylight官方社区。
3)Feature定义。
具体代码如下:
public class MyOpendaylightToaster implements MytoasterService,AutoCloseable { private static final InstanceIdentifier<Mytoaster> TOASTER_IID = InstanceIdentifier.builder(Mytoaster.class).build(); private static final MyDisplayString TOASTER_MANUFACTURER = new MyDisplayString("Opendaylight"); private static final MyDisplayString TOASTER_MODEL_NUMBER = new MyDisplayString("Model 1 - Binding Aware"); private DataBroker dataBroker; public void setDataBroker(DataBroker dataBroker) { this.dataBroker = dataBroker; } public MyOpendaylightToaster() { } /** * 初始化Toaster 创建一个toaster保存到datastore中 */ public void init() { setToasterStatusIdle(null); } /** * 关闭服务 回调函数 * @throws Exception */ public void close() throws Exception { if (dataBroker != null) { //将toaster从datastore中删除 WriteTransaction tx = dataBroker.newWriteOnlyTransaction(); tx.delete(LogicalDatastoreType.OPERATIONAL, TOASTER_IID); Futures.addCallback(tx.submit(), new FutureCallback<Void>() { @Override public void onSuccess(@Nullable Void aVoid) { System.out.println("Delete MyToaster From Datastore Success."); } @Override public void onFailure(Throwable throwable) { System.out.println("Delete MyToaster From Datastore Failed."); } }); } } /** * 创建一个Mytoaster服务 * @param defaultStatus -- 默认状态 idle * @return 返回一个Mytoaster实例 */ private Mytoaster createToaster(ToasterStatus defaultStatus) { MytoasterBuilder builder = new MytoasterBuilder(); builder.setToasterManufacturer(TOASTER_MANUFACTURER); builder.setToasterModelNumber(TOASTER_MODEL_NUMBER); builder.setToasterStatus(defaultStatus); return builder.build(); } /** * 创建一个Toaster并且设置成IDLE态 * @param resultCallback -- 回调函数 */ private void setToasterStatusIdle(final Function<Boolean, Void> resultCallback) { //调用datastore接口 创建一个可写事务 //将新创建的Toaster保存到dataStore中 WriteTransaction tx = dataBroker.newWriteOnlyTransaction(); tx.put(LogicalDatastoreType.OPERATIONAL, TOASTER_IID, createToaster(ToasterStatus.Idle)); //提交事务 一般放到一个FUTURE中,如果设置了回调函数 则将设置的结果通过回调函数返回 Futures.addCallback(tx.submit(), new FutureCallback<Void>() { @Override public void onSuccess(@Nullable Void result) { notifyCallback(true); } @Override public void onFailure(Throwable throwable) { notifyCallback(false); } private void notifyCallback(Boolean result) { if (resultCallback != null) { resultCallback.apply(result); } } }); }
注册bundle,在resources目录中创建org/opendaylight/blueprint/mytoaster-provider.xml,具体文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!-- 此处的odl是 opendaylight对blueprint的扩展 扩展了一些自己特有的 具体内容可以参考推荐的两个网址 --> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" odl:use-default-for-reference-types="true"> <!-- 引入dataBroker服务 这样才能操作datastroe --> <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/> <!-- 通过bean创建一个MyOpendaylightToaster实例并注册到mdsal中 --> <bean id="mytoaster" class="org.opendaylight.controller.demo2.mytoaster.provider.MyOpendaylightToaster" init-method="init" destroy-method="close"> <property name="dataBroker" ref="dataBroker"/> </bean> </blueprint>
经过上面两步骤,就可以完成了bundle基本功能了,比之前的配置子系统要简单很多!!进入mytoaster-provider,进行编译,编译过程中应该没有任何问题,接下来需要设置feature。
设置feature需要做一下修改:
1) Feature定义是在目录controller/features,我们的mytoaster是定义在mdsal中,因此进入controller/features/mdsal增加我们的feature。修改mdsal目录中pom文件,增加<module>odl-mytoaster</module>
2) 由于碳版本中karaf升级到4.0版本(碳版本是过渡版本),feature定义方式存在两种方式。在karaf4下面需要将各个feature独立定义在以俄国目录中,例如是odl-toaster目录。碳之前版本是在mdsal/feature/src/main/features.xml文件中定义。由于对karaf4.0不是很了解,这里只介绍旧方式,即在features.xml文件中定义。增加features定义:
在mdsal/features-mdsal/pom.xml文件中增加依赖:
<!-- mytoaster --> <dependency> <groupId>org.opendaylight.controller.demo2</groupId> <artifactId>mytoaster</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.opendaylight.controller.demo2</groupId> <artifactId>mytoaster-provider</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
在mdsal/features-mdsal/src/main/features/features.xml文件中增加feature定义:
<feature name='odl-mytoaster' version='${project.version}' description="OpenDaylight :: MyToaster"> <feature version='${yangtools.version}'>odl-yangtools-common</feature> <feature version='${mdsal.version}'>odl-mdsal-binding-runtime</feature> <feature version='${project.version}'>odl-mdsal-broker</feature> <bundle>mvn:org.opendaylight.controller.demo2/mytoaster/{{VERSION}}</bundle> <bundle>mvn:org.opendaylight.controller.demo2/mytoaster-provider/{{VERSION}}</bundle> </feature>
以上内容就是feature的定义,在mdsal目录中进行编译应该可以正常编译出来。虽然能编译出来但是还不能运行,因为从github下载controller代码,并没有依赖上restconf,因此需要作如下修改:
3) 修改controller/karaf/opendaylight-karaf目录中的pom文件,增加如下内容:
<dependency> <groupId>org.opendaylight.netconf</groupId> <artifactId>features-netconf</artifactId> <version>1.2.2-SNAPSHOT</version> <classifier>features</classifier> <type>xml</type> <scope>runtime</scope> </dependency> <dependency> <groupId>org.opendaylight.netconf</groupId> <artifactId>features-netconf-connector</artifactId> <version>1.2.2-SNAPSHOT</version> <classifier>features</classifier> <type>xml</type> <scope>runtime</scope> </dependency> <dependency> <groupId>org.opendaylight.netconf</groupId> <artifactId>features-restconf</artifactId> <version>1.5.2-SNAPSHOT</version> <classifier>features</classifier> <type>xml</type> <scope>runtime</scope> </dependency> <dependency> <groupId>org.opendaylight.netconf</groupId> <artifactId>features-yanglib</artifactId> <version>1.2.2-SNAPSHOT</version> <classifier>features</classifier> <type>xml</type> <scope>runtime</scope> </dependency>
经过以上内容修改,把修改的模块重新编译一次,应该就能运行起来了。至此修改编译的目录以及顺序如下:
demo2/mytoaster,demo2/mytoaster-provider,features/mdsal,karaf/opendaylight-karaf。
编译完成之后,启动karaf,如下:
opendaylight-user@root> opendaylight-user@root> opendaylight-user@root> opendaylight-user@root>feature:install odl-restconf-all opendaylight-user@root>feature:install odl-mytoaster opendaylight-user@root>bundle:list | grep toaster 284 | Active | 80 | 0.0.1.SNAPSHOT | mytoaster 285 | Active | 80 | 0.0.1.SNAPSHOT | mytoaster-provider opendaylight-user@root>
我们是postman获取一下toaster基本信息,如何返回和下图一致,则说明正确:
Url:http://localhost:8181/restconf/operational/mytoaster:mytoaster,用户名和密码都是admin
至此服务的注册已经完成,接下来就是要是服务。我们都知道odl采用rpc的方式提供服务,因此我们要把yang模型中定义的所有rpc都要实现。在MyOpendaylightToaster实现MytoasterService接口,并且添加rpc的实现如下(只显示修改内容):
public class MyOpendaylightToaster implements MytoasterService,AutoCloseable { ... private static final int MAX_TRY_COUNT = 3;//发生异常时最大尝试次数 private final ExecutorService executor; private final AtomicReference<Future<?>> currentToastTask = new AtomicReference<>(); .. public MyOpendaylightToaster() { //只有一个线程 因为一个面包机只能烤完一个才能烤下一个 executor = Executors.newFixedThreadPool(1); } public void close() throws Exception { executor.shutdown();//关闭线程池否则服务无法正常退出 ... } @Override public Future<RpcResult<MakeToastOutput>> makeToast(MakeToastInput input) { final SettableFuture<RpcResult<MakeToastOutput>> futureResult = SettableFuture.create(); //检查toaster状态并且制作面包 checkStatusAndMakeToast(input, MAX_TRY_COUNT, futureResult); return futureResult; } @Override public Future<RpcResult<Void>> cancelToast() { Future<?> current = currentToastTask.getAndSet(null); if (current != null) { current.cancel(true); } // Always return success from the cancel toast call return Futures.immediateFuture(RpcResultBuilder.<Void> success().build()); } private void checkStatusAndMakeToast(final MakeToastInput input, final int tries, final SettableFuture<RpcResult<MakeToastOutput>> futureResult) { //在系统启动的时候 创建了一个toaster实例并保存在datastore中 我们要先判断这个toaster的状态: //Idle状态:继续后续流程 制作面包 //Work状态:直接返回 提示用户正忙,稍后尝试 ReadWriteTransaction tx = dataBroker.newReadWriteTransaction(); //此处的Optional导入的包是com.google.common.base.Optional; ListenableFuture<Optional<Mytoaster>> readFuture = tx.read(LogicalDatastoreType.OPERATIONAL, TOASTER_IID); //获取datastore中Toaster的状态 final ListenableFuture<Void> commitFuture = Futures.transform(readFuture, (AsyncFunction<Optional<Mytoaster>, Void>) mytoasterOptional -> { ToasterStatus status = ToasterStatus.Idle; if (mytoasterOptional.isPresent()) { status = mytoasterOptional.get().getToasterStatus(); } if (status == ToasterStatus.Idle) { tx.put(LogicalDatastoreType.OPERATIONAL, TOASTER_IID, createToaster(ToasterStatus.Work)); return tx.submit(); } else { return Futures.immediateFailedFuture(new TransactionCommitFailedException( "", RpcResultBuilder.newError(RpcError.ErrorType.APPLICATION, "Error", "" + "Read Data Is Error.") )); } }); //异步等待结果 Futures.addCallback(commitFuture, new FutureCallback<Void>() { @Override public void onSuccess(@Nullable Void aVoid) { //表示可以制作面包 现实生活中烤面包是需要一定时间的 而且烤成功后会提醒 所以就餐人员可以 //作其他事情,不必一直等在面包机钱买年 因此我们把烤面包过程交给一个线程 然后立即响应成功 currentToastTask.set(executor.submit(new MakeToastTask(input))); futureResult.set(RpcResultBuilder.success(makeToastOutput("Please Wait A Moment")) .build()); } @Override public void onFailure(Throwable ex) {//表示不能制作面包 if (ex instanceof OptimisticLockFailedException) { //如果是加锁失败则进行尝试 if (tries - 1 > 0) { checkStatusAndMakeToast(input, tries - 1, futureResult); } else { futureResult.set(RpcResultBuilder.<MakeToastOutput>failed().withResult(makeToastOutput("couldn't Make Toaster!")) .build()); } } else if (ex instanceof TransactionCommitFailedException) { futureResult.set(RpcResultBuilder.<MakeToastOutput>failed().withResult(makeToastOutput("couldn't Make Toaster!")) .build()); } else { futureResult.set(RpcResultBuilder.<MakeToastOutput>failed().withResult(makeToastOutput("couldn't Make Toaster!")) .build()); } } }); } //内部类 实现callable接口 供线程池使用 private class MakeToastTask implements Callable<Void> { final MakeToastInput toastRequest; public MakeToastTask(final MakeToastInput toastRequest) { this.toastRequest = toastRequest; } @Override public Void call() throws Exception { try { // 睡眠15秒 表示制作面包 Thread.sleep(15); System.out.println("Break is out!!Please take it"); //通知烤面包成功 -- notification 后面实现 } catch (InterruptedException e) { System.out.println("Interrupted while making the toast"); //通知烤面包失败 -- notification } finally { //制作完面包 要把toaster状态改成idle状态 以便其他人可以继续烤面包 setToasterStatusIdle(result -> { currentToastTask.set(null); return null; }); } return null; } } }
到目前为止,rpc的主要服务均以实现,但是还存在一个问题,我们的rpc已经实现了,但是如何让mdsal知道这个rpc存在呢?答案是:通过blueprint进行注册。
只需要在mytoaster-provider.xml文件中增加: <odl:rpc-implementation ref="mytoaster"/>。
接下来我们来验证结果:
Url:http://localhost:8080/restconf/operations/mytoaster:make-toast
可以看到Break is out!!Please take it
是在15秒之后打印出来的,但是返回结果立即返回并且是就餐者请稍等片刻。这样我们就实现了一个简单rpc服务以及调用。
相关文章推荐
- Opendaylight课堂之深度剖析toaster(三)
- Opendaylight课堂之深度剖析toaster(一)
- Sybase在线课堂报名:深度剖析——Sybase ASE 15.5的实时数据处理(11月17日 周三)
- 11月9日晚7点在线课堂:Flex 4与服务器端通信方式深度剖析
- 11月9日晚7点在线课堂:Flex 4与服务器端通信方式深度剖析
- 深度剖析WinPcap之(二)——网络分析与嗅探的基础知识(5)
- 深度剖析WinPcap之(八)——打开与关闭适配器(15)
- 深度剖析WinPcap之(九)——数据包的发送过程(6)
- 深度剖析WinPcap之(十)——数据包的内核过滤(7)
- 深度剖析Redis持久化
- libevent源码深度剖析十一
- read系统调用深度剖析
- <<C语言深度剖析>>学习笔记之一:C语言中32个关键字
- 深度剖析WinPcap之(六)――驱动程序的初始化与清除(2)
- 深度剖析WinPcap之(八)――打开与关闭适配器(3)
- 深度剖析WinPcap之(八)――打开与关闭适配器(17)
- ACE开发环境搭建与源码深度剖析
- 深度剖析WinPcap之(九)――数据包的发送过程(13-2)
- 《 C++深度剖析》学习日志十八——神秘的临时对象
- 深度剖析陈晓和贝恩之阴谋