第5部分:爲ToasterService添加一個消費者

5部分:ToasterService添加一個消費者——讓我們做早餐!

我們已經看到了如何使用RestConf訪問ToasterService RPC方法。在本節中,我們將展示如何從內部控制器以編程方式訪問ToasterService

我們將創建一個名爲KitchenService的新服務,它提供了一個方法來做早餐(這是位於sample-toaster-consumer項目中)。此服務將訪問ToasterService提供早餐的烤麪包。

KitchenService定義了一個更高級別的服務爲做一個完整的早餐。這個很好地展示了“service chaining”,消費者的一個或多個服務也是另一個服務提供者。這個例子只會調用到‘toast’服務,但可以看到,它可以擴展到eggs”服務也可以是添加一個“coffee”服務等。

1.1 定義KitchenService接口

爲了簡便起見,我們將手工編寫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<RpcResult<Void>> makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness );

9.    

10. }

我們的早餐只包括簡單的雞蛋和烤麪包——一個完整的早餐也包括燻肉或香腸和咖啡。早餐雞蛋、肉、咖啡等也可以單獨的分配不同的數據模型與相應的服務,ToasterService——我們把它留給讀者作爲練習。

1.2 定義KitchenService實現

接下來,我們創建一個類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<RpcResult<Void>> makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness ) {

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<RpcResult<Void>> makeToastFuture = JdkFutureAdapters.listenInPoolThread(

28.                 makeToast( toastType, toastDoneness ), executor );

29.   

30.         ListenableFuture<RpcResult<Void>> makeEggsFuture = makeEggs( eggsType );

31.   

32.         // Combine the 2 ListenableFutures into 1 containing a list of RpcResults.

33.   

34.         ListenableFuture<List<RpcResult<Void>>> 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<List<RpcResult<Void>>,RpcResult<Void>>() {

41.                 @Override

42.                 public ListenableFuture<RpcResult<Void>> apply( List<RpcResult<Void>> results )

43.                                                                                  throws Exception {

44.                     boolean atLeastOneSucceeded = false;

45.                     Builder<RpcError> errorList = ImmutableList.builder();

46.                     for( RpcResult<Void> result: results ) {

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.<Void> getRpcResult( atLeastOneSucceeded, errorList.build() ) );

58.                 }

59.         } );

60.     }

61.   

62.     private ListenableFuture<RpcResult<Void>> makeEggs( EggsType eggsType ) {

63.   

64.         return executor.submit( new Callable<RpcResult<Void>>() {

65.   

66.             @Override

67.             public RpcResult<Void> call() throws Exception {

68.   

69.                 // We don't actually do anything here - just return a successful result.

70.                 return Rpcs.<Void> getRpcResult( true, Collections.<RpcError>emptyList() );

71.             }

72.         } );

73.     }

74.   

75.     private Future<RpcResult<Void>> makeToast( Class<? extends ToastType> toastType,

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. }

1.3 連接KitchenService實現

類似於烤麪包機供應者服務,在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<RpcResult<Void>> makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness ) {

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. <snapshot>

154.    <configuration>

155.        

156.            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">

157.               ...

158.               <module>

159.                  <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">

160.                     kitchen:kitchen-service-impl

161.                  </type>

162.                  <name>kitchen-service-impl</name>

163.                  

164.  

165.                  <rpc-registry>

166.                     <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>

167.                     <name>binding-rpc-broker</name>

168.                  </rpc-registry>

169.                </module>

170.            </modules>

171.            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">

172.                <service>

173.                    <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">

174.                        kitchen:kitchen-service

175.                    </type>

176.                    <instance>

177.                        <name>kitchen-service</name>

178.                        <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>

179.                    </instance>

180.                </service>

181.            </services>

182.        

183.    </configuration>

184.    

185.    <required-capabilities>

186.        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl?module=kitchen-service-impl&revision=2014-01-31</capability>

187.        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31</capability>

188.    </required-capabilities>

189. </snapshot>

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

1.4 添加JMX RPC做早餐

在這一點上,如果我們部署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<Void> result = makeBreakfast( EggsType.SCRAMBLED, WheatBread.class, 2 ).get();

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.    ...

1.5 通過JMX做早餐

我們可以通過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的值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章