opendaylight(Li)下toaster APP的簡單實現

OS:
ubuntu 14.04 64bit

一、安裝jdk
準備資材:
jdk-8u72-linux-x64.tar.gz
(之前使用了jdk7,compile時報“java/util/function/consumer”找不到)

1.解壓jdk
進入/usr/local目錄後,解壓文件
#cd /usr/local
#tar xzvf /home/todd/jdk-8u72-linux-x64.tar.gz

2.添加環境變量
# vi /etc/profile
文件的最後增加以下內容:
export JAVA_HOME=/usr/local/jdk1.8.0_72
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

3.立刻使環境變量生效
# source /etc/profile

二、安裝maven
準備資材:
apache-maven-3.3.3-bin.tar.gz

1.解壓maven
進入/usr/local目錄後,解壓文件
#cd /usr/local
#tar xzvf /home/todd/apache-maven-3.3.3-bin.tar.gz.gz

2.添加環境變量
# vi /etc/profile
文件的最後增加以下內容:
export M2_HOME=/usr/local/apache-maven-3.3.3
export PATH=$PATH:$M2_HOME/bin

3.立刻使環境變量生效
# source /etc/profile

4.使用opendaylight的settings.xml配置文件(ODL沒有使用maven的倉庫,而是維護自己的倉庫)
#cp -n ~/.m2/settings.xml{,.orig} ; \wget -q -O - https://raw.githubusercontent.com/opendaylight/odlparent/stable/lithium/settings.xml > ~/.m2/settings.xml

三、創建opendaylight項目
1.執行以下命令創建項目結構
#mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller -DarchetypeArtifactId=opendaylight-startup-archetype \
-DarchetypeRepository=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/ \
-DarchetypeCatalog=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml

......

Define value for property 'groupId': : org.opendaylight.toaster
Define value for property 'artifactId': : toaster
[INFO] Using property: version = 1.0.0-SNAPSHOT
Define value for property 'package':  org.opendaylight.toaster: :
Mar 28, 2016 11:01:50 PM org.apache.velocity.runtime.log.JdkLogChute log
INFO: FileResourceLoader : adding path '.'
Define value for property 'classPrefix':  Toaster: :
Define value for property 'copyright': : CopyLeft(c)
[INFO] Using property: copyrightYear = 2015
Confirm properties configuration:
groupId: org.opendaylight.toaster
artifactId: toaster
version: 1.0.0-SNAPSHOT
package: org.opendaylight.toaster
classPrefix: ${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)}
copyright: CopyLeft(c)
copyrightYear: 2015
 Y: : y
......

出現以下內容,表示執行成功:
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: opendaylight-startup-archetype:1.0.0-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.opendaylight.toaster
[INFO] Parameter: artifactId, Value: toaster
[INFO] Parameter: version, Value: 0.1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.opendaylight.toaster
[INFO] Parameter: packageInPathFormat, Value: org/opendaylight/toaster
[INFO] Parameter: classPrefix, Value: Toaster
[INFO] Parameter: package, Value: org.opendaylight.toaster
[INFO] Parameter: version, Value: 0.1.0-SNAPSHOT
[INFO] Parameter: copyright, Value: CopyLeft(c)
[INFO] Parameter: groupId, Value: org.opendaylight.toaster
[INFO] Parameter: artifactId, Value: toaster
[WARNING] Don't override file /home/todd/opendaylight/toaster/pom.xml
[INFO] project created from Archetype in dir: /home/todd/opendaylight/toaster
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 14:10 min
[INFO] Finished at: 2016-02-02T22:57:51+08:00
[INFO] Final Memory: 19M/175M
[INFO] ------------------------------------------------------------------------

在當前目錄下創建瞭如下目錄結構:
toaster/
->api/
->artifacts/  
->features/
->impl/
->karaf/
->pom.xml
api:使用yang定義java API,REST API等
artifacts:負責將api,impl等模塊打包陳artifact。
feature:描述當前項目feature的依賴關係。
impl:項目功能實現的feature,包含了代碼和配置文件。

2.編譯運行
# mvn clean install -DskipTests
#cd karaf/target/assembly/bin
#./karaf
opendaylight-user@root>feature:list | grep toaster
odl-toaster                     | 1.3.1-SNAPSHOT   |           | odl-mdsal-1.3.1-SNAPSHOT            | OpenDaylight :: Toaster                           
odl-toaster-api                 | 1.0.0-SNAPSHOT   | x         | odl-toaster-1.0.0-SNAPSHOT          | OpenDaylight :: toaster :: api                    
odl-toaster                     | 1.0.0-SNAPSHOT   | x         | odl-toaster-1.0.0-SNAPSHOT          | OpenDaylight :: toaster                           
odl-toaster-rest                | 1.0.0-SNAPSHOT   | x         | odl-toaster-1.0.0-SNAPSHOT          | OpenDaylight :: toaster :: REST                   
odl-toaster-ui                  | 1.0.0-SNAPSHOT   | x         | odl-toaster-1.0.0-SNAPSHOT          | OpenDaylight :: toaster :: UI
toaster已經安裝

四、定義Toaster YANG 數據模型
1.編輯toaster/api/src/main/yang/toaster.yang文件,定義toaster的基本屬性YANG:
module toaster {
    yang-version 1;
    namespace "urn:opendaylight:params:xml:ns:yang:toaster";
    prefix "toaster";

    //Defines the organization which defined / owns this .yang file.
    organization "Netconf Central";

    //defines the primary contact of this yang file.
    contact
      "Andy Bierman <[email protected]>";

    //provides a description of this .yang file.
    description
      "YANG version of the TOASTER-MIB.";

    revision "2015-01-05" {
        description "Initial revision of toaster model";
    }

    //declares a base identity, in this case a base type for different types of toast.
    identity toast-type {
      description
        "Base for all bread types supported by the toaster. New bread types not listed here nay be added in the future.";
    }

    //the below identity section is used to define globally unique identities
    //Note - removed a number of different types of bread to shorten the text length.
    identity white-bread {
      base toast-type;       //logically extending the declared toast-type above.
      description "White bread.";  //free text description of this type.
    }

    identity wheat-bread {
      base toast-type;
      description "Wheat bread.";
    }

    identity wonder-bread {
      base toast-type;
      description "Wonder bread.";
    }

    //defines a new "Type" string type which limits the length
    typedef DisplayString {
      type string {
        length "0 .. 255";
      }
      description
        "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION.";
      reference
        "RFC 2579, section 2.";

    }

    // This definition is the top-level configuration "item" that defines a toaster. The "presence" flag connotes there
    // can only be one instance of a toaster which, if present, indicates the service is available.
    container toaster {
      presence
        "Indicates the toaster service is available";
      description
        "Top-level container for all toaster database objects.";

      //Note in these three attributes that config = false. This indicates that they are operational attributes.
      leaf toasterManufacturer {
        type DisplayString;
        config false;
        mandatory true;
        description
          "The name of the toaster's manufacturer. For instance, Microsoft Toaster.";
      }

      leaf toasterModelNumber {
        type DisplayString;
        config false;
        mandatory true;
        description
          "The name of the toaster's model. For instance, Radiant Automatic.";
      }

      leaf toasterStatus {
        type enumeration {
          enum "up" {
            value 1;
            description
              "The toaster knob position is up. No toast is being made now.";
          }
          enum "down" {
            value 2;
            description
              "The toaster knob position is down. Toast is being made now.";
          }
        }
        config false;
        mandatory true;
        description
          "This variable indicates the current state of  the toaster.";
      }

      leaf darknessFactor {
        type uint32;
        config true;
        default 1000;
        description "The darkness factor";
      }
    }

    rpc make-toast{
      input{
        leaf toasterDoneness {
          type uint32 {
            range "1 .. 10";
          }
          default '5';  
        }
        leaf toasterToastType {
          type identityref {
            base toast-type;
          }
          default 'wheat-bread';
        }
      }
    }

    rpc cancel-toast {

    }

    notification toastDone{
      leaf toastStatus{
        type uint32;
      }
    }
}

2.編譯運行
# mvn clean install -DskipTests
#cd karaf/target/assembly/bin
#./karaf
opendaylight-user@root>

3.使用curl命令來進行RESTCONF調用,查看configure中的數據:
另外打開一個終端,進行數據的寫入:
#curl -H 'Content-type:application/json' -X PUT -d '{"toaster":{"darknessFactor":500}}' --verbose -u admin:admin http://localhost:8181/restconf/config/toaster:toaster
查看剛纔寫入的數據:
#curl --verbose -u admin:admin http://localhost:8181/restconf/config/toaster:toaster
......
{"toaster":{"darknessFactor":500}}

關於darknessFactor,雖然在yang中定義了default值爲1000,但是如果部執行數據的寫入,而直接查看數據信息,卻無法獲取
這個默認值,可能是opendaylight的一個bug吧。

五、實現自定義RPC(make-toaster和cancel-toaster)
1.在ToasterProvider.java同目錄下定義ToasterServiceImpl.java,實現make-toaster和cancel-toaster方法,
如果覺得使用文本編輯器寫代碼麻煩,可以執行mvn eclipse:eclipse轉換成eclipse工程,然後導入到eclipse
後敲,還要注意一點,java文件必須要在以“/*”開頭,不然編譯的時候會提示錯誤。
/*
 * Copyright © 2015 CopyLeft(c) and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.toaster.impl;

import java.util.concurrent.Future;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.MakeToastInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterService;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.Futures;

public class ToasterServiceImpl implements ToasterService {

     private static final Logger LOG = LoggerFactory.getLogger(ToasterServiceImpl.class);

     @Override
     public Future<RpcResult<java.lang.Void>> cancelToast() {
         LOG.info("cancelToast");
         return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
     }

     @Override
     public Future<RpcResult<java.lang.Void>> makeToast(MakeToastInput input) {
         LOG.info("makeToast:{}",input);
         return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
     }
 }


2.修改ToasterProvider.java將ToasterServiceImpl註冊到MD-SAL:
/*
 * Copyright © 2015 CopyLeft(c) and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.toaster.impl;

import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ToasterProvider implements BindingAwareProvider, AutoCloseable {

    private static final Logger LOG = LoggerFactory.getLogger(ToasterProvider.class);

    private RpcRegistration<ToasterService> rpcReg = null;

    @Override
    public void onSessionInitiated(ProviderContext session) {
        LOG.info("ToasterProvider Session Initiated");
        rpcReg = session.addRpcImplementation(ToasterService.class, new ToasterServiceImpl());

    }

    @Override
    public void close() throws Exception {
        if(rpcReg != null){
            rpcReg.close();
        }
        LOG.info("ToasterProvider Closed");
    }
}

3.編譯運行
#mvn clean install -DskipTests
#cd karaf/target/assembly/bin
#./karaf
opendaylight-user@root>

4.打開log顯示
opendaylight-user@root>log:tail

5.執行一下RPC請求:
#curl -H 'Content-type:application/json' -X POST -d '{"input":{"toaster:toasterDoneness":"10","toaster:toasterToastType":"wheat-bread"}}' --verbose -u admin:admin http://localhost:8181/restconf/operations/toaster:make-toast
請求應該返回200 OK

同時,opendaylight的界面出現如下log:
2016-04-01 23:57:19,473 | INFO  | qtp227610208-75  | ToasterServiceImpl               | 154 - org.opendaylight.toaster.impl - 1.0.0.SNAPSHOT | makeToast: MakeToastInput{getToasterDoneness=10, getToasterToastType=class org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.WheatBread, augmentations={}}


六、讀取dataStore中的數據,執行make toaster,
1.修改ToasterProvider.java的內容如下:
/*
 * Copyright © 2015 CopyLeft(c) and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.toaster.impl;

import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ToasterProvider implements BindingAwareProvider, AutoCloseable {

    private static final Logger LOG = LoggerFactory.getLogger(ToasterProvider.class);

    private RpcRegistration<ToasterService> rpcReg = null;

    @Override
    public void onSessionInitiated(ProviderContext session) {
        //從session中獲取broker
        DataBroker broker = session.getSALService(DataBroker.class);
        //將broker交給實現者
        ToasterServiceImpl service = new ToasterServiceImpl(broker);
        rpcReg = session.addRpcImplementation(ToasterService.class, service);
        LOG.info("ToasterProvider Session Initiated");
    }

    @Override
    public void clos3.編譯運行
#mvn clean install -DskipTests
#cd karaf/target/assembly/bin
#./karaf
opendaylight-user@root>

2.在修改ToasterProvider.java同一目錄下創建MakeToastTask.java,用於執行make toaster任務,文件內容如下:
/*
 * Copyright © 2015 CopyLeft(c) and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.toaster.impl;

import java.util.concurrent.Callable;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.MakeToastInput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class MakeToastTask implements Callable<Void>{
     private static final Logger LOG = LoggerFactory.getLogger(MakeToastTask.class);
     
     private final MakeToastInput toastRequest;
     
     public MakeToastTask(MakeToastInput toastRequest){
         this.toastRequest = toastRequest;
     }
     
     public Void call(){
         try{
             LOG.info("makeToast start,Doneness:"+toastRequest.getToasterDoneness()+",Type:"+toastRequest.getToasterToastType());
             Thread.sleep(10000);
             LOG.info("makeToast end.....");
         }catch (InterruptedException e){
             LOG.info("interrupted while making the toast");
         }
         return null;
     }
}


3.修改ToasterServiceImpl.java文件的內容如下:
/*
 * Copyright © 2015 CopyLeft(c) and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.toaster.impl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;

import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.MakeToastInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.Toaster;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.Toaster.ToasterStatus;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterService;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

public class ToasterServiceImpl implements ToasterService {

     private static final Logger LOG = LoggerFactory.getLogger(ToasterServiceImpl.class);
     
     private final AtomicReference<Future<?>> currentMakeToastTask = new AtomicReference<>();
     
     private final ExecutorService executor = Executors.newFixedThreadPool(1);

     private DataBroker broker;
     
     public ToasterServiceImpl(DataBroker broker) {
         this.broker = broker;
     }
     
     @Override
     public Future<RpcResult<java.lang.Void>> cancelToast() {
         LOG.info("cancelToast");
         return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
     }

     @Override
     public Future<RpcResult<java.lang.Void>> makeToast(final MakeToastInput input) {
         LOG.info("makeToast: {}",input);
         
         final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
         final ReadWriteTransaction tx = broker.newReadWriteTransaction();
         ListenableFuture<Optional<Toaster>> readFuture =
                tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );
         
         //將Optional<Toaster>類型的ListenableFuture轉換成Void的ListenableFuture
          final ListenableFuture<Void> commitFuture =
                 Futures.transform( readFuture, new AsyncFunction<Optional<Toaster>,Void>() {

                     @Override
                     public ListenableFuture<Void> apply(
                             final Optional<Toaster> toasterData ) throws Exception {
                         //獲取toaster的tasterStatus
                         ToasterStatus toasterStatus = ToasterStatus.Down;
                         if( toasterData.isPresent() ) {
                             toasterStatus = toasterData.get().getToasterStatus();
                         }
                         
                         //判斷當前的狀態是不是Up
                         if( toasterStatus == ToasterStatus.Up ) {
                             //如果是Up狀態,則拋出異常
                             LOG.info("the toaster is already using,please wait a moment!");
                           return Futures.immediateFailedCheckedFuture(
                                  new TransactionCommitFailedException( "", RpcResultBuilder.newWarning( ErrorType.APPLICATION, "in-use",
                                         "Toaster is busy", null, null, null ) ) );
                         } else{
                             //如果是down狀態,則修改成Up狀態
                             tx.put( LogicalDatastoreType.OPERATIONAL, TOASTER_IID,
                                     new ToasterBuilder().setToasterStatus( ToasterStatus.Up ).build());
                             return tx.submit();
                         }
                     }
             } );
         
          //添加callback函數
         Futures.addCallback( commitFuture, new FutureCallback<Void>() {
            @Override
            public void onSuccess( final Void result ) {
                // 如果更新data store成功則執行makeToast
                currentMakeToastTask.set( executor.submit(new MakeToastTask(input)));
            }

            @Override
            public void onFailure( final Throwable ex ) {
                    LOG.debug( "Failed to commit Toaster status", ex );
                }
            }
         );
         
         return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
     }
 }


4.編譯運行
#mvn clean install -DskipTests
#cd karaf/target/assembly/bin
#./karaf
opendaylight-user@root>

5.打開log顯示
opendaylight-user@root>log:tail

6.打開終端,執行make toast請求:
#curl -H 'Content-type:application/json' -X POST -d '{"input":{"toaster:toasterDoneness":"10","toaster:toasterToastType":"wheat-bread"}}' --verbose -u admin:admin http://localhost:8181/restconf/operations/toaster:make-toast

在log中會顯示如下信息:
2016-04-04 16:09:21,899 | INFO  | pool-30-thread-1 | MakeToastTask                    | 154 - org.opendaylight.toaster.impl - 1.0.0.SNAPSHOT | makeToast start,Doneness:10,Type:class org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.WheatBread
2016-04-04 16:09:31,901 | INFO  | pool-30-thread-1 | MakeToastTask                    | 154 - org.opendaylight.toaster.impl - 1.0.0.SNAPSHOT | makeToast end.....

7.再次執行make toast請求:
在log中會顯示如下信息:(因爲md-sal的data store中的toasterStatus一直爲up)
2016-04-04 16:12:09,851 | INFO  | tp1594748046-332 | ToasterServiceImpl               | 154 - org.opendaylight.toaster.impl - 1.0.0.SNAPSHOT | makeToast: MakeToastInput{getToasterDoneness=10, getToasterToastType=class org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.WheatBread, augmentations={}}
2016-04-04 16:12:09,853 | INFO  | tp1594748046-332 | ToasterServiceImpl               | 154 - org.opendaylight.toaster.impl - 1.0.0.SNAPSHOT | the toaster is already using,please wait a moment!

七、data change事件,toasterStatus改變時APP能獲取到
1.在ToasterProvider.java文件進行如下修改:
在onSessionInitiated方法中註冊dataChange。
在close方法中增加對dataChangeListenerRegistration的close。

代碼如下:
/*
 * Copyright © 2015 CopyLeft(c) and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.toaster.impl;

import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ToasterProvider implements BindingAwareProvider, AutoCloseable {

    private static final Logger LOG = LoggerFactory.getLogger(ToasterProvider.class);

    private RpcRegistration<ToasterService> rpcReg = null;
    
    private ListenerRegistration<DataChangeListener> dataChangeListenerRegistration = null;

    @Override
    public void onSessionInitiated(ProviderContext session) {
        //從session中獲取broker
        DataBroker broker = session.getSALService(DataBroker.class);
        //將broker交給實現者
        ToasterServiceImpl service = new ToasterServiceImpl(broker);
        //註冊rpc 和dataChange
        rpcReg = session.addRpcImplementation(ToasterService.class, service);
        dataChangeListenerRegistration = broker
                .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, service.TOASTER_IID,
                        service, DataChangeScope.SUBTREE);
        LOG.info("ToasterProvider Session Initiated");
    }

    @Override
    public void close() throws Exception {
        if(rpcReg != null){
            rpcReg.close();
        }
        if(dataChangeListenerRegistration != null){
            dataChangeListenerRegistration.close();
        }
        LOG.info("ToasterProvider Closed");
    }
}


2.在ToasterServiceImpl.java中實現DataChangeListener接口的onDataChanged方法:
/*
 * Copyright © 2015 CopyLeft(c) and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.toaster.impl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;

import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.MakeToastInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.Toaster;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.Toaster.ToasterStatus;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.toaster.rev150105.ToasterService;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

public class ToasterServiceImpl implements ToasterService,DataChangeListener {

     private static final Logger LOG = LoggerFactory.getLogger(ToasterServiceImpl.class);
     
     private final AtomicReference<Future<?>> currentMakeToastTask = new AtomicReference<>();
     
     final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
     
     private final ExecutorService executor = Executors.newFixedThreadPool(1);
     
     private DataBroker broker;
     
     public ToasterServiceImpl(DataBroker broker) {
         this.broker = broker;
     }
     
     @Override
     public Future<RpcResult<java.lang.Void>> cancelToast() {
         LOG.info("cancelToast");
         return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
     }

     @Override
     public Future<RpcResult<java.lang.Void>> makeToast(final MakeToastInput input) {
         LOG.info("makeToast: {}",input);
         
         final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
         final ReadWriteTransaction tx = broker.newReadWriteTransaction();
         ListenableFuture<Optional<Toaster>> readFuture =
                tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );
         
         //將Optional<Toaster>類型的ListenableFuture轉換成Void的ListenableFuture
          final ListenableFuture<Void> commitFuture =
                 Futures.transform( readFuture, new AsyncFunction<Optional<Toaster>,Void>() {

                     @Override
                     public ListenableFuture<Void> apply(
                             final Optional<Toaster> toasterData ) throws Exception {
                         //獲取toaster的tasterStatus
                         ToasterStatus toasterStatus = ToasterStatus.Down;
                         if( toasterData.isPresent() ) {
                             toasterStatus = toasterData.get().getToasterStatus();
                         }
                         
                         //判斷當前的狀態是不是Up
                         if( toasterStatus == ToasterStatus.Up ) {
                             //如果是Up狀態,則拋出異常
                             LOG.info("the toaster is already using,please wait a moment!");
                           return Futures.immediateFailedCheckedFuture(
                                  new TransactionCommitFailedException( "", RpcResultBuilder.newWarning( ErrorType.APPLICATION, "in-use",
                                         "Toaster is busy", null, null, null ) ) );
                         } else{
                             //如果是down狀態,則修改成Up狀態
                             tx.put( LogicalDatastoreType.OPERATIONAL, TOASTER_IID,
                                     new ToasterBuilder().setToasterStatus( ToasterStatus.Up ).build());
                             return tx.submit();
                         }
                     }
             } );
         
          //添加callback函數
         Futures.addCallback( commitFuture, new FutureCallback<Void>() {
            @Override
            public void onSuccess( final Void result ) {
                // 如果更新data store成功則執行makeToast
                currentMakeToastTask.set( executor.submit(new MakeToastTask(input)));
            }

            @Override
            public void onFailure( final Throwable ex ) {
                    LOG.debug( "Failed to commit Toaster status", ex );
                }
            }
         );
         
         return Futures.immediateFuture(RpcResultBuilder.<Void> success().build());
     }

        @Override
        public void onDataChanged( final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change ) {
               DataObject dataObject = change.getUpdatedSubtree();
            if( dataObject instanceof Toaster )
            {
                Toaster toaster = (Toaster) dataObject;
                LOG.info("onDataChanged - new Toaster config: {}", toaster);
            }
        }
 }

3.編譯運行
#mvn clean install -DskipTests
#cd karaf/target/assembly/bin
#./karaf
opendaylight-user@root>

4.打開log顯示
opendaylight-user@root>log:tail

5.打開終端,執行make toast請求:
#curl -H 'Content-type:application/json' -X PUT -d '{"toaster":{"darknessFactor":510}}' --verbose -u admin:admin http://localhost:8181/restconf/config/toaster:toaster
#curl -H 'Content-type:application/json' -X PUT -d '{"toaster":{"darknessFactor":511}}' --verbose -u admin:admin http://localhost:8181/restconf/config/toaster:toaster

6.log顯示如下信息:
2016-04-04 19:58:48,980 | INFO  | n-dispatcher-183 | ToasterServiceImpl               | 154 - org.opendaylight.toaster.impl - 1.0.0.SNAPSHOT | onDataChanged - new Toaster config: Toaster{getDarknessFactor=510, augmentations={}}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章