第6部分:通知
这部分将利用MD-SAL的主动通知服务,当重大事件发生时使OpenDaylightToaster发送通知。通知可以被注册监听实现或者外部netconf客户使用。如果有一个面包放入的话,一个烤面包机可以只做烤面包。目前,我们的OpenDaylightToaster可以无限供应面包,这在现实世界中这是不现实的。我们将修改OpenDaylightToaster使其可以有限供应烤面包。我们将保持简单和维护一个总体限制,包括所有类型的面包,而不是限制每个面包的类型。
当开始制作面包时,如果没有面包,一个toasterOutOfBread通知将会被发送。
我们还会添加一个RPC调用——restock-toaster,可以用来设置面包存货的数量。除此之外将会发送一个toasterRestocked通知。
当我们收到通知时KitchenService将会为notifications and act accordingly注册。
1.1 定义notifications 和 RPC
在toaster.yang文件中,我们将定义2个notifications和RPC。
1. module toaster {
1. ...
2. rpc restock-toaster {
3. description
4. "Restocks the toaster with the amount of bread specified.";
5.
6.
7. input {
8. leaf amountOfBreadToStock {
9. type uint32;
10. description
11. "Indicates the amount of bread to re-stock";
12. }
13. }
14. }
15.
16.
17. notification toasterOutOfBread {
18. description
19. "Indicates that the toaster has run of out bread.";
20. } // notification toasterOutOfStock
21.
22.
23. notification toasterRestocked {
24. description
25. "Indicates that the toaster has run of out bread.";
26. leaf amountOfBread {
27. type uint32;
28. description
29. "Indicates the amount of bread that was re-stocked";
30. }
31. } // notification toasterRestocked
32.
33. } // module toaster
之后运行以下代码,会生成几个新的类。
1. mvn clean install
l ToasterOutOfBread - 一个接口,为toasterOutOfBread notification定义一个DTO。
l ToasterOutOfBreadBuilder -一个具体类,为创建ToasterOutOfBread实例。
l ToasterRestocked -一个接口,为toasterRestocked notification定义一个DTO。
l ToasterRestockedBuilder -一个具体类,为创建ToasterRestocked实例。
l ToasterListener - 一个接口,该接口用来接受定义在Module中的Notifications,该类继承自包org.opendaylight.yangtools.yang.binding中的NotificationListener接口,并在接口中声明了对Notification进行处理的方法。
1.2 在OpenDaylightToaster实现notifications 和RPC
接下来我们在OpenDaylightToaster添加一段代码用来实现restockToaster RPC并发送通知。
1. public class OpendaylightToaster implements ToasterService, ToasterProviderRuntimeMXBean, AutoCloseable, DataChangeListener {
34. ...
35. private NotificationProviderService notificationProvider;
36. ...
37. private final AtomicLong amountOfBreadInStock = new AtomicLong( 100 );
38. ...
39. public void setNotificationProvider(NotificationProviderService salService) {
40. this.notificationProvider = salService;
41. }
42. ...
43.
44.
45. private void checkStatusAndMakeToast( final MakeToastInput input,
46. final SettableFuture<RpcResult<Void>> futureResult ) {
47.
48. ...
49. final ListenableFuture<RpcResult<TransactionStatus>> commitFuture =
50. Futures.transform( readFuture, new AsyncFunction<Optional<DataObject>,
51. RpcResult<TransactionStatus>>() {
52.
53. @Override
54. public ListenableFuture<RpcResult<TransactionStatus>> apply(
55. Optional<DataObject> toasterData ) throws Exception {
56.
57. ...
58. if( toasterStatus == ToasterStatus.Up ) {
59.
60. if( outOfBread() ) {
61. LOG.debug( "Toaster is out of bread" );
62.
63. return Futures.immediateFuture( Rpcs.<TransactionStatus>getRpcResult(
64. false, null, makeToasterOutOfBreadError() ) );
65. }
66.
67. ...
68. }
69. ...
70. }
71. } );
72. ...
73. }
74. ...
75. /**
76. * RestConf RPC call implemented from the ToasterService interface.
77. * Restocks the bread for the toaster and sends a ToasterRestocked notification.
78. */
79. @Override
80. public Future<RpcResult<java.lang.Void>> restockToaster(RestockToasterInput input) {
81. LOG.info( "restockToaster: " + input );
82.
83. amountOfBreadInStock.set( input.getAmountOfBreadToStock() );
84.
85. if( amountOfBreadInStock.get() > 0 ) {
86. ToasterRestocked reStockedNotification =
87. new ToasterRestockedBuilder().setAmountOfBread( input.getAmountOfBreadToStock() ).build();
88. notificationProvider.publish( reStockedNotification );
89. }
90.
91. return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError> emptySet()));
92. }
93. ...
94. private boolean outOfBread()
95. {
96. return amountOfBreadInStock.get() == 0;
97. }
98.
99.
100. private class MakeToastTask implements Callable<Void> {
101. ...
102. @Override
103. public Void call() throws InterruptedException {
104. ...
105. amountOfBreadInStock.getAndDecrement();
106. if( outOfBread() ) {
107. LOG.info( "Toaster is out of bread!" );
108.
109. notificationProvider.publish( new ToasterOutOfBreadBuilder().build() );
110. }
111. ...
112. }
113. }
1.3 把notifications连接到OpenDaylightToaster
OpenDaylightToaster需要访问MD-SAL的 NotificationProviderService以发送通知。我们需要指定the NotificationProviderService,作为一个在toaster-provider-impl模块中的依赖通过向config:configuration添加一个条目:
1. augment "/config:modules/config:module/config:configuration" {
114. case toaster-consumer-impl {
115. when "/config:modules/config:module/config:type = 'toaster-consumer-impl'";
116. ...
117.
118.
119. container notification-service {
120. uses config:service-ref {
121. refine type {
122. mandatory true;
123. config:required-identity mdsal:binding-notification-service;
124. }
125. }
126. }
127. }
128. }
运行mvn clean install的产生源代码。
生成的AbstractToasterProviderModule类现在应该有一个getNotificationServiceDependency()方法。在ToasterProviderModule.createInstance()方法中,我们可以访问该方法,去把NotificationProviderService注入到OpenDaylightToaster中。
1. opendaylightToaster.setNotificationProvider(getNotificationServiceDependency());
最后我们需要为notification-service添加依赖到初始化配置XML文件中的toaster-provider-impl模块,正如我们之前对'rpc-registry'所做的:
1. <snapshot>
129. <configuration>
130.
131. <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
132. <module>
133. ...
134. <notification-service>
135. <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
136. binding:binding-notification-service
137. </type>
138. <name>binding-notification-broker</name>
139. </notification-service>
140. </module>
141. </modules>
142. ...
143.
144. </configuration>
145. ...
146. </snapshot>
1.4 在KitchenServiceImpl中实现notifications
接下来我们修改KitchenServiceImpl去实现ToasterListener接口和notification方法。
1. public class KitchenServiceImpl implements KitchenService, KitchenServiceRuntimeMXBean, ToasterListener {
147. ...
148. private volatile boolean toasterOutOfBread;
149.
150.
151. private Future<RpcResult<Void>> makeToast( Class<? extends ToastType> toastType,
152. int toastDoneness ) {
153.
154. if( toasterOutOfBread )
155. {
156. log.info( "We're out of toast but we can make eggs" );
157. return Futures.immediateFuture( Rpcs.<Void> getRpcResult( true,
158. Arrays.asList( RpcErrors.getRpcError( "", "partial-operation", null,
159. ErrorSeverity.WARNING,
160. "Toaster is out of bread but we can make you eggs",
161. ErrorType.APPLICATION, null ) ) ) );
162. }
163.
164. // Access the ToasterService to make the toast.
165.
166. MakeToastInput toastInput = new MakeToastInputBuilder()
167. .setToasterDoneness( (long) toastDoneness )
168. .setToasterToastType( toastType )
169. .build();
170.
171. return toaster.makeToast( toastInput );
172. }
173. ...
174. /**
175. * Implemented from the ToasterListener interface.
176. */
177. @Override
178. public void onToasterOutOfBread( ToasterOutOfBread notification ) {
179. log.info( "ToasterOutOfBread notification" );
180. toasterOutOfBread = true;
181. }
182.
183.
184. /**
185. * Implemented from the ToasterListener interface.
186. */
187. @Override
188. public void onToasterRestocked( ToasterRestocked notification ) {
189. log.info( "ToasterRestocked notification - amountOfBread: " + notification.getAmountOfBread() );
190. toasterOutOfBread = false;
191. }
192. }
onToasterOutOfBread 和onToasterRestocked notification方法简单地设置和清除toasterOutOfBread。当开始做早餐时,如果toasterOutOfBread,我们不能做面包但试图去做鸡蛋,所以有人至少可以吃早餐。
1.5 为notifications连接KitchenServiceImpl
KitchenServiceImpl需要通过MD-SAL's NotificationProviderService注册,用来接收通知。我们需要去指定notification-service,作为一个在kitchen-service-impl 模块中的依赖通过向config:configuration添加一个条目:
1. augment "/config:modules/config:module/config:configuration" {
193. case kitchen-service-impl {
194. when "/config:modules/config:module/config:type = 'kitchen-service-impl'";
195. ...
196.
197.
198. container notification-service {
199. uses config:service-ref {
200. refine type {
201. mandatory true;
202. config:required-identity mdsal:binding-notification-service;
203. }
204. }
205. }
206. }
207. }
运行mvn clean install的产生源代码。
生成的AbstractKitchenServiceModule类现在应该有一个getNotificationServiceDependency()方法。
我们可以访问在KitchenServiceModule.createInstance()方法中的该方法,来通过NotificationProviderService注册KitchenServiceImpl 。
1. public java.lang.AutoCloseable createInstance() {
208. ...
209. final Registration<NotificationListener> toasterListenerReg =
210. getNotificationServiceDependency().registerNotificationListener( kitchenService );
211.
212.
213. final KitchenServiceRuntimeRegistration runtimeReg =
214. getRootRuntimeBeanRegistratorWrapper().register( kitchenService );
215.
216.
217. final class AutoCloseableKitchenService implements KitchenService, AutoCloseable {
218. @Override
219. public void close() throws Exception {
220. toasterListenerReg.close();
221. runtimeReg.close();
222. log.info("Toaster consumer (instance {}) torn down.", this);
223. }
224. ...
225. }
226. ...
227. }
最后,我们需要为'notification-service'添加依赖到初始化配置XML文件中的kitchen-service-impl模块。
1. <snapshot>
228. <configuration>
229.
230. <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
231. ...
232. <module>
233. ...
234. <notification-service>
235. <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
236. binding:binding-notification-service
237. </type>
238. <name>binding-notification-broker</name>
239. </notification-service>
240. </module>
241. </modules>
242. ...
243.
244. </configuration>
245. ...
246. </snapshot>
1.6 测试notifications
我们将首先在没有面包的情况下运行烤面包机。默认现有面包数是100。而不是花时间去制作100个面包,我们首先会调用restock-toaster RPC通过restconf去把这个数值降低到3。
1. HTTP Method => POST
247. URL => http://localhost:8080/restconf/operations/toaster:restock-toaster
248. Header => Content-Type: application/yang.data+json
249. Body =>
250. {
251. "input" :
252. {
253. "toaster:amountOfBreadToStock" : "3"
254. }
255. }
接下来,我们将做3份早餐订单耗尽现有的面包。
l 打开JConsole
l 导航到MBeans选项卡
l 展开org.opendaylight.controller->RuntimeBean->kitchen-service-impl->kitchen-service-imp->Operations结点
l 点击makeScrambledWithWheat按钮三次
在完成最后一份早餐后,烤面包机将没有面包并发送toasterOutOfBread通知kitchen服务。你可以在controllerlog/console中看到这个消息:
1. ...KitchenServiceImpl - ToasterOutOfBread notification
再次点击makeScrambledWithWheat——结果应该是真实的,你应该会看到这个消息在日志中:
1. ...KitchenServiceImpl - We're out of toast but we can make eggs
再次调用restock-toaster——您应该看到这个消息在日志中:
1. ...KitchenServiceImpl - ToasterRestocked notification - amountOfBread: 3