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 ";

    //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> cancelToast() {
         LOG.info("cancelToast");
         return Futures.immediateFuture(RpcResultBuilder. success().build());
     }

     @Override
     public Future> makeToast(MakeToastInput input) {
         LOG.info("makeToast:{}",input);
         return Futures.immediateFuture(RpcResultBuilder. 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 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 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{
     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> currentMakeToastTask = new AtomicReference<>();
     
     private final ExecutorService executor = Executors.newFixedThreadPool(1);

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

     @Override
     public Future> makeToast(final MakeToastInput input) {
         LOG.info("makeToast: {}",input);
         
         final InstanceIdentifier TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
         final ReadWriteTransaction tx = broker.newReadWriteTransaction();
         ListenableFuture> readFuture =
                tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );
         
         //将Optional类型的ListenableFuture转换成Void的ListenableFuture
          final ListenableFuture commitFuture =
                 Futures.transform( readFuture, new AsyncFunction,Void>() {

                     @Override
                     public ListenableFuture apply(
                             final Optional 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() {
            @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. 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 rpcReg = null;
    
    private ListenerRegistration 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> currentMakeToastTask = new AtomicReference<>();
     
     final InstanceIdentifier 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> cancelToast() {
         LOG.info("cancelToast");
         return Futures.immediateFuture(RpcResultBuilder. success().build());
     }

     @Override
     public Future> makeToast(final MakeToastInput input) {
         LOG.info("makeToast: {}",input);
         
         final InstanceIdentifier TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
         final ReadWriteTransaction tx = broker.newReadWriteTransaction();
         ListenableFuture> readFuture =
                tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );
         
         //将Optional类型的ListenableFuture转换成Void的ListenableFuture
          final ListenableFuture commitFuture =
                 Futures.transform( readFuture, new AsyncFunction,Void>() {

                     @Override
                     public ListenableFuture apply(
                             final Optional 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() {
            @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. success().build());
     }

        @Override
        public void onDataChanged( final AsyncDataChangeEvent, 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={}}

你可能感兴趣的:(opendaylight)