我们已经看到了如何使用RestConf访问ToasterService RPC方法。在本节中,我们将展示如何从内部控制器以编程方式访问ToasterService。
我们将创建一个名为KitchenService的新服务,它提供了一个方法来做早餐(这是位于sample-toaster-consumer项目中)。此服务将访问ToasterService提供早餐的烤面包。
KitchenService定义了一个更高级别的服务为做一个完整的早餐。这个很好地展示了“service chaining”,消费者的一个或多个服务也是另一个服务提供者。这个例子只会调用到‘toast’服务,但可以看到,它可以扩展到eggs”服务也可以是添加一个“coffee”服务等。
为了简便起见,我们将手工编写KitchenService数据模型和接口,而不是在yang文件中定义。在真正kitchenService模型中您可能想在yang文件中定义kitchenService,去获得由MDSAL提供的自动生成类和开箱即用的功能的好处。对于这个示例,我们定义一个枚举类型和接口的java文件在src /main/ java下,在包org.opendaylight.controller.sample.kitchen.api中。
1. //EggsType.java
1. public enum EggsType {
2. SCRAMBLED,
3. OVER_EASY,
4. POACHED
5. }
1. //KitchenService.java
6. public interface KitchenService {
7.
8. Future
9.
10. }
我们的早餐只包括简单的鸡蛋和烤面包——一个完整的早餐也包括熏肉或香肠和咖啡。早餐鸡蛋、肉、咖啡等也可以单独的分配不同的数据模型与相应的服务,如ToasterService——我们把它留给读者作为练习。
接下来,我们创建一个类KitchenServiceImp,来实现接口和访问的ToasterService去烤面包:
1. public class KitchenServiceImpl implements KitchenService {
11.
12. private static final Logger log = LoggerFactory.getLogger( KitchenServiceImpl.class );
13.
14. private final ToasterService toaster;
15.
16. public KitchenServiceImpl(ToasterService toaster) {
17. this.toaster = toaster;
18. }
19.
20. @Override
21. public Future
22.
23. // Call makeToast and use JdkFutureAdapters to convert the Future to a ListenableFuture,
24. // The OpendaylightToaster impl already returns a ListenableFuture so the conversion is
25. // actually a no-op.
26.
27. ListenableFuture
28. makeToast( toastType, toastDoneness ), executor );
29.
30. ListenableFuture
31.
32. // Combine the 2 ListenableFutures into 1 containing a list of RpcResults.
33.
34. ListenableFuture>> combinedFutures =
35. Futures.allAsList( ImmutableList.of( makeToastFuture, makeEggsFuture ) );
36.
37. // Then transform the RpcResults into 1.
38.
39. return Futures.transform( combinedFutures,
40. new AsyncFunction>,RpcResult
41. @Override
42. public ListenableFuture
43. throws Exception {
44. boolean atLeastOneSucceeded = false;
45. Builder
46. for( RpcResult
47. if( result.isSuccessful() ) {
48. atLeastOneSucceeded = true;
49. }
50.
51. if( result.getErrors() != null ) {
52. errorList.addAll( result.getErrors() );
53. }
54. }
55.
56. return Futures.immediateFuture(
57. Rpcs.
58. }
59. } );
60. }
61.
62. private ListenableFuture
63.
64. return executor.submit( new Callable
65.
66. @Override
67. public RpcResult
68.
69. // We don't actually do anything here - just return a successful result.
70. return Rpcs.
71. }
72. } );
73. }
74.
75. private Future
76. int toastDoneness ) {
77. // Access the ToasterService to make the toast.
78.
79. MakeToastInput toastInput = new MakeToastInputBuilder()
80. .setToasterDoneness( (long) toastDoneness )
81. .setToasterToastType( toastType )
82. .build();
83.
84. return toaster.makeToast( toastInput );
85. }
86. }
类似于烤面包机供应者服务,在yang和提供初始配置xml中我们将描述一个厨房服务实现。因此MDSAL可以把这些连接起来。
定义厨房服务
在kitchen-service-impl.yang中我们将定义厨房服务实现及其依赖项:
1. module kitchen-service-impl {
87.
88. yang-version 1;
89. namespace "urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl";
90. prefix "kitchen-service-impl";
91.
92. import config { prefix config; revision-date 2013-04-05; }
93. import rpc-context { prefix rpcx; revision-date 2013-06-17; }
94.
95. import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
96.
97. description
98. "This module contains the base YANG definitions for
99. kitchen-service impl implementation.";
100.
101. revision "2014-01-31" {
102. description
103. "Initial revision.";
104. }
105.
106. // This is the definition of kitchen service interface identity.
107. identity kitchen-service {
108. base "config:service-type";
109. config:java-class "org.opendaylight.controller.sample.kitchen.api.KitchenService";
110. }
111.
112. // This is the definition of kitchen service implementation module identity.
113. identity kitchen-service-impl {
114. base config:module-type;
115. config:provided-service kitchen-service;
116. config:java-name-prefix KitchenService;
117. }
118.
119. augment "/config:modules/config:module/config:configuration" {
120. case kitchen-service-impl {
121. when "/config:modules/config:module/config:type = 'kitchen-service-impl'";
122.
123. container rpc-registry {
124. uses config:service-ref {
125. refine type {
126. mandatory true;
127. config:required-identity mdsal:binding-rpc-registry;
128. }
129. }
130. }
131. }
132. }
133. }
这类似于toaster-provider-impl yang,除了我们也定义一个kitchen-service 服务类型的身份,这为厨房服务接口定义了一个全局标识符,并且可以被引用。config:java-class性能指定KitchenService java接口。
这kitchen-service的身份将被用于配置子系统去通知提供的服务实例kitchen-service-impl模块作为OSGi服务KitchenService java接口。
实现kitchenservicemodule
在运行'mvn clean install'之后,几个源文件将生成类似于the toaster provider,我们只需要修改KitchenServiceModule.createInstance()方法来实例化KitchenServiceImpl实例并且连接他:
1. @Override
134. public java.lang.AutoCloseable createInstance() {
135. ToasterService toasterService = getRpcRegistryDependency().getRpcService(ToasterService.class);
136.
137. final KitchenServiceImpl kitchenService = new KitchenServiceImpl(toasterService);
138.
139. final class AutoCloseableKitchenService implements KitchenService, AutoCloseable {
140.
141. @Override
142. public void close() throws Exception {
143. }
144.
145. @Override
146. public Future
147. return kitchenService.makeBreakfast( eggs, toast, toastDoneness );
148. }
149. }
150.
151. AutoCloseable ret = new AutoCloseableKitchenService();
152. return ret;
153. }
因为我们指定的为在kitchen-service-impl.yang中的厨房服务实现模块提供服务,我们必须返回一个AutoCloseable实例并且也实现了KitchenService接口。否则这将导致一个失败的配置子系统。
定义初始配置
最后,在初始配置xml创建之前把厨房服务和模块定义添加到其中:
1.
154.
155.
156.
157. ...
158.
159.
160. kitchen:kitchen-service-impl
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174. kitchen:kitchen-service
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
kitchen-service-impl模块定义类似于toaster-provider-impl模块概述。
我们还为kitchen-service接口定义了一个服务入口,用来告知config subsystem去通知OSGI服务。这个type元素指的是完整的kitchen-service标识并且是指定服务的接口类型。Instance元素指定服务实例信息。Name元素指定一个独一无二的服务名称,provider元素指定的路径形式/modules/module/name来定位kitchen-service-impl模块,通过模块名称提供了服务实例。在运行时,实际的服务实例被实例化,并被插入在config的/services/service/ hierarchy结点下并通知OSGI。
在这一点上,如果我们部署kitchen服务我们不能通过restconf去访问它,因为我们没有为它定义一个yang数据模型。据推测,真正的服务,会有java客户去消耗它。取而代之的是我们可以利用JMX训练kitchen服务来做早餐。
通过JMX MD-SAL也支持RPC调用。我们只是在yang中简单定义了RPC并且通过augmentation把它与config:state绑定起来,就像我们之前为toaster provider中的clearToastsMade RPC做的一样。
我们将会为kitchen-service-impl.yang添加一个make-scrambled-with-wheat RPC定义。这个调用不接受输入和hard-codes。
1. augment "/config:modules/config:module/config:state" {
190. case kitchen-service-impl {
191. when "/config:modules/config:module/config:type = 'kitchen-service-impl'";
192.
193. rpcx:rpc-context-instance "make-scrambled-with-wheat-rpc";
194. }
195. }
196.
197. identity make-scrambled-with-wheat-rpc;
198.
199. rpc make-scrambled-with-wheat {
200. description
201. "Shortcut JMX call to make breakfast with scrambled eggs and wheat toast for testing.";
202.
203. input {
204. uses rpcx:rpc-context-ref {
205. refine context-instance {
206. rpcx:rpc-context-instance make-scrambled-with-wheat-rpc;
207. }
208. }
209. }
210. output {
211. leaf result {
212. type boolean;
213. }
214. }
215. }
重新生成源后,修改KitchenServiceImpl 实现生成的接口KitchenServiceRuntimeMXBean,它定义了makeScrambledWithWheat()方法。
1. @Override
216. public Boolean makeScrambledWithWheat() {
217. try {
218. // This call has to block since we must return a result to the JMX client.
219. RpcResult
220. if( result.isSuccessful() ) {
221. log.info( "makeBreakfast succeeded" );
222. } else {
223. log.warn( "makeBreakfast failed: " + result.getErrors() );
224. }
225.
226. return result.isSuccessful();
227.
228. } catch( InterruptedException | ExecutionException e ) {
229. log.warn( "An error occurred while maing breakfast: " + e );
230. }
231.
232. return Boolean.FALSE;
233. }
接下来,修改KitchenServiceModule.createInstance()来使用JMX注册KitchenService,然后在AutoCloseable wrapper内关闭它。
1. final KitchenServiceRuntimeRegistration runtimeReg =
234. getRootRuntimeBeanRegistratorWrapper().register( kitchenService );
235. ...
236. final class AutoCloseableKitchenService implements AutoCloseable {
237. @Override
238. public void close() throws Exception {
239. ...
240. runtimeReg.close();
241. }
242. }
243. ...
我们可以通过JConsole来访问kitchen-service-impl MBean,正如我们前面对toaster-service-impl MBean所做的一样。
l 导航到MBeans选项卡。
l 扩展org.opendaylight.controller->RuntimeBean->kitchen-service-impl->kitchen-service-imp->Operations结点。
l 点击makeScrambledWithWheat按钮。
l 来验证实际面包,扩展org.opendaylight.controller->RuntimeBean->toaster-provider-impl->toaster-provider-imp ->Attributes并检查ToastsMade的值。