/* * Copyright (c) 2014 Cisco Systems, Inc. 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.controller.sal.binding.api; import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.RpcService; /** * Provides a registry for Remote Procedure Call (RPC) service implementations. The RPCs are * defined in YANG models. *

* There are 2 types of RPCs: *

* *

Global RPC

*

* An RPC is global if there is intended to be only 1 registered implementation. A global RPC is not * explicitly declared as such, essentially any RPC that is not defined to be routed is considered global. *

* Global RPCs are registered using the * {@link #addRpcImplementation(Class, RpcService)} method. * *

Routed RPC

*

* MD-SAL supports routing of RPC between multiple implementations where the appropriate * implementation is selected at run time based on the content of the RPC message as described in * YANG model. *

* RPC routing is based on: *

* *

Context type

*

* A context type is modeled in YANG using a combination of a YANG identity * and Opendaylight specific extensions from yang-ext module. These extensions are: *

* * *

Routed RPC example

*

*

1. Defining a Context Type
*

* The following snippet declares a simple YANG identity named example-context: * *

 * module example {
 *     ...
 *     identity example-context {
 *          description "Identity used to define an example-context type";
 *     }
 *     ...
 * }
 * 
*

* We then use the declared identity to define a context type by using it in combination * with the context-instance YANG extension. We'll associate the context type * with a list element in the data tree. This defines the set of nodes whose instance * identifiers are valid for the example-context context type. *

* The following YANG snippet imports the yang-ext module and defines the list * element named item inside a container named foo: * *

 * module foo {
 *     ...
 *     import yang-ext {prefix ext;}
 *     ...
 *     container foo {
 *          list item {
 *              key "id";
 *              leaf id {type string;}
 *              ext:context-instance "example-context";
 *          }
 *     }
 *     ...
 * }
 * 
*

* The statement ext:context-instance "example-context"; inside the list element * declares that any instance identifier referencing item in the data * tree is valid for example-context. For example, the following instance * identifier: *

 *     InstanceIdentifier.create(Foo.class).child(Item.class,new ItemKey("Foo"))
 * 
* is valid for example-context. However the following: *
 *     InstanceIdentifier.create(Example.class)
 * 
* is not valid. *

* So using an identity in combination with context-instance we * have effectively defined a context type that can be referenced in a YANG RPC input. * *

2. Defining an RPC to use the Context Type
*

* To define an RPC to be routed based on the context type we need to add an input leaf element * that references the context type which will hold an instance identifier value to be * used to route the RPC. *

* The following snippet defines an RPC named show-item with 2 leaf elements * as input: item of type instance-identifier and description: * *

 * module foo {
 *      ...
 *      import yang-ext {prefix ext;}
 *      ...
 *      rpc show-item {
 *          input {
 *              leaf item {
 *                  type instance-identifier;
 *                  ext:context-reference example-context;
 *              }
 *              leaf description {
 *                  type "string";
 *              }
 *          }
 *      }
 * }
 * 
*

* We mark the item leaf with a context-reference statement that * references the example-context context type. RPC calls will then be routed * based on the instance identifier value contained in item. Only instance * identifiers that point to a foo/item node are valid as input. *

* The generated RPC Service interface for the module is: * *

 * interface FooService implements RpcService {
 *      Future<RpcResult<Void>> showItem(ShowItemInput input);
 * }
 * 
*

* For constructing the RPC input, there are generated classes ShowItemInput and ShowItemInputBuilder. * *

3. Registering a routed RPC implementation
*

* To register a routed implementation for the show-item RPC, we must use the * {@link #addRoutedRpcImplementation(Class, RpcService)} method. This * will return a {@link RoutedRpcRegistration} instance which can then be used to register / * unregister routed paths associated with the registered implementation. *

* The following snippet registers myImpl as the RPC implementation for an * item with key "foo": *

 * // Create the instance identifier path for item "foo"
 * InstanceIdentifier path = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("foo"));
 *
 * // Register myImpl as the implementation for the FooService RPC interface
 * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
 *
 * // Now register for the context type and specific path ID. The context type is specified by the
 * // YANG-generated class for the example-context identity.
 * reg.registerPath(ExampleContext.class, path);
 * 
*

* It is also possible to register the same implementation for multiple paths: * *

 * InstanceIdentifier one = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("One"));
 * InstanceIdentifier two = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("Two"));
 *
 * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
 * reg.registerPath(ExampleContext.class, one);
 * reg.registerPath(ExampleContext.class, two);
 * 
* *

* When another client invokes the showItem(ShowItemInput) method on the proxy instance * retrieved via {@link RpcConsumerRegistry#getRpcService(Class)}, the proxy will inspect the * arguments in ShowItemInput, extract the InstanceIdentifier value of the item leaf and select * the implementation whose registered path matches the InstanceIdentifier value of the item leaf. * *

Notes for RPC Implementations

* *

RpcResult

*

* The generated interfaces require implementors to return * {@link java.util.concurrent.Future Future}<{@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}<{RpcName}Output>> instances. * * Implementations should do processing of RPC calls asynchronously and update the * returned {@link java.util.concurrent.Future Future} instance when processing is complete. * However using {@link com.google.common.util.concurrent.Futures#immediateFuture(Object) Futures.immediateFuture} * is valid only if the result is immediately available and asynchronous processing is unnecessary and * would only introduce additional complexity. * *

* The {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult} is a generic * wrapper for the RPC output payload, if any, and also allows for attaching error or * warning information (possibly along with the payload) should the RPC processing partially * or completely fail. This is intended to provide additional human readable information * for users of the API and to transfer warning / error information across the system * so it may be visible via other external APIs such as Restconf. *

* It is recommended to use the {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult} * for conveying appropriate error information * on failure rather than purposely throwing unchecked exceptions if at all possible. * While unchecked exceptions will fail the returned {@link java.util.concurrent.Future Future}, * using the intended RpcResult to convey the error information is more user-friendly. */ public interface RpcProviderRegistry extends // RpcConsumerRegistry, // RouteChangePublisher> { /** * Registers a global implementation of the provided RPC service interface. * All methods of the interface are required to be implemented. * * @param serviceInterface the YANG-generated interface of the RPC Service for which to register. * @param implementation "the implementation of the RPC service interface. * @return an RpcRegistration instance that should be used to unregister the RPC implementation * when no longer needed by calling {@link RpcRegistration#close()}. * * @throws IllegalStateException * if the supplied RPC interface is a routed RPC type. */ RpcRegistration addRpcImplementation(Class serviceInterface, T implementation) throws IllegalStateException; /** * Registers an implementation of the given routed RPC service interface. *

* See the {@link RpcProviderRegistry class} documentation for information and example on * how to use routed RPCs. * * @param serviceInterface the YANG-generated interface of the RPC Service for which to register. * @param implementation the implementation instance to register. * @return a RoutedRpcRegistration instance which can be used to register paths for the RPC * implementation via invoking {@link RoutedRpcRegistration#registerPath(....). * {@link RoutedRpcRegistration#close()} should be called to unregister the implementation * and all previously registered paths when no longer needed. * * @throws IllegalStateException * if the supplied RPC interface is not a routed RPC type. */ RoutedRpcRegistration addRoutedRpcImplementation(Class serviceInterface, T implementation) throws IllegalStateException; }