2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.operation;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.JdkFutureAdapters;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Proxy;
22 import java.util.Collection;
23 import java.util.Optional;
24 import org.opendaylight.mdsal.binding.javav2.dom.adapter.extractor.ContextReferenceExtractor;
25 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
26 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
27 import org.opendaylight.mdsal.binding.javav2.dom.codec.serialized.LazySerializedContainerNode;
28 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
29 import org.opendaylight.mdsal.binding.javav2.spec.base.Input;
30 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
31 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
32 import org.opendaylight.mdsal.binding.javav2.spec.base.Output;
33 import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
34 import org.opendaylight.mdsal.binding.javav2.spec.base.RpcCallback;
35 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
36 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
37 import org.opendaylight.mdsal.dom.api.DOMRpcService;
38 import org.opendaylight.mdsal.dom.spi.RpcRoutingStrategy;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.common.RpcError;
41 import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
49 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
50 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
51 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
54 class RpcServiceAdapter implements InvocationHandler {
56 private final RpcInvocationStrategy strategy;
57 private final Class<? extends Rpc<?, ?>> type;
58 private final BindingToNormalizedNodeCodec codec;
59 private final DOMRpcService delegate;
60 private final Rpc<?, ?> proxy;
62 RpcServiceAdapter(final Class<? extends Rpc<?, ?>> type, final BindingToNormalizedNodeCodec codec,
63 final DOMRpcService domService) {
64 this.type = requireNonNull(type);
65 this.codec = requireNonNull(codec);
66 this.delegate = requireNonNull(domService);
67 strategy = createStrategy(type);
68 proxy = (Rpc<?, ?>) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type }, this);
71 private RpcInvocationStrategy createStrategy(final Class<? extends Rpc<?, ?>> rpcInterface) {
72 final RpcDefinition rpc = codec.getRpcDefinition(rpcInterface);
73 final RpcRoutingStrategy domStrategy = RpcRoutingStrategy.from(rpc);
74 if (domStrategy.isContextBasedRouted()) {
76 return new RoutedStrategy(rpc.getPath(),
77 rpcInterface.getMethod("invoke", Input.class, RpcCallback.class), domStrategy.getLeaf());
78 } catch (final NoSuchMethodException e) {
79 throw new IllegalStateException("Can not find 'invoke' method", e);
82 return new NonRoutedStrategy(rpc.getPath());
85 Rpc<?, ?> getProxy() {
90 @SuppressWarnings("checkstyle:hiddenField")
91 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
92 switch (method.getName()) {
94 return type.getName() + "$Adapter{delegate=" + delegate.toString() + "}";
96 return System.identityHashCode(proxy);
98 return proxy == args[0];
100 if (args.length == 2) {
101 final Input<?> input = (Input<?>) requireNonNull(args[0]);
102 final RpcCallback<Output> callback = (RpcCallback<Output>) requireNonNull(args[1]);
103 ListenableFuture<RpcResult<?>> future = strategy.invoke((TreeNode) input);
104 Futures.addCallback(future, new FutureCallback<RpcResult<?>>() {
107 public void onSuccess(final RpcResult<?> result) {
108 if (result.getErrors().isEmpty()) {
109 callback.onSuccess((Output) result.getResult());
111 result.getErrors().forEach(e -> callback.onFailure(e.getCause()));
116 public void onFailure(final Throwable throwable) {
117 callback.onFailure(throwable);
119 }, MoreExecutors.directExecutor());
126 throw new UnsupportedOperationException("Method " + method.toString() + "is unsupported.");
129 private abstract class RpcInvocationStrategy {
131 private final SchemaPath rpcName;
133 protected RpcInvocationStrategy(final SchemaPath path) {
137 final ListenableFuture<RpcResult<?>> invoke(final TreeNode input) {
138 return invoke0(rpcName, serialize(input));
141 abstract NormalizedNode<?, ?> serialize(TreeNode input);
143 final ListenableFuture<RpcResult<?>> invokeEmpty() {
144 return invoke0(rpcName, null);
147 final SchemaPath getRpcName() {
151 private ListenableFuture<RpcResult<?>> invoke0(final SchemaPath schemaPath, final NormalizedNode<?, ?> input) {
152 final ListenableFuture<DOMRpcResult> listenInPoolThread =
153 JdkFutureAdapters.listenInPoolThread(delegate.invokeRpc(schemaPath, input));
154 if (listenInPoolThread instanceof LazyDOMRpcResultFuture) {
155 return ((LazyDOMRpcResultFuture) listenInPoolThread).getBindingFuture();
158 return transformFuture(schemaPath, listenInPoolThread, codec.getCodecFactory());
161 private ListenableFuture<RpcResult<?>> transformFuture(final SchemaPath rpc,
162 final ListenableFuture<DOMRpcResult> domFuture, final BindingNormalizedNodeCodecRegistry resultCodec) {
163 return Futures.transform(domFuture, input -> {
164 final NormalizedNode<?, ?> domData = input.getResult();
165 final TreeNode bindingResult;
166 if (domData != null) {
167 final SchemaPath rpcOutput = rpc.createChild(QName.create(rpc.getLastComponent(), "output"));
168 bindingResult = resultCodec.fromNormalizedNodeOperationData(rpcOutput, (ContainerNode) domData);
170 bindingResult = null;
173 // DOMRpcResult does not have a notion of success, hence we have to reverse-engineer it by looking
174 // at reported errors and checking whether they are just warnings.
175 final Collection<? extends RpcError> errors = input.getErrors();
176 return RpcResult.class.cast(RpcResultBuilder.status(errors.stream()
177 .noneMatch(error -> error.getSeverity() == ErrorSeverity.ERROR))
178 .withResult(bindingResult).withRpcErrors(errors).build());
179 }, MoreExecutors.directExecutor());
183 private final class NonRoutedStrategy extends RpcInvocationStrategy {
185 protected NonRoutedStrategy(final SchemaPath path) {
190 NormalizedNode<?, ?> serialize(final TreeNode input) {
191 return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());
196 private final class RoutedStrategy extends RpcInvocationStrategy {
198 private final ContextReferenceExtractor refExtractor;
199 private final NodeIdentifier contextName;
201 protected RoutedStrategy(final SchemaPath path, final Method rpcMethod, final QName leafName) {
203 final Optional<Class<? extends Instantiable<?>>> maybeInputType =
204 BindingReflections.resolveOperationInputClass(rpcMethod);
205 Preconditions.checkState(maybeInputType.isPresent(), "RPC method %s has no input", rpcMethod.getName());
206 final Class<? extends Instantiable<?>> inputType = maybeInputType.get();
207 refExtractor = ContextReferenceExtractor.from(inputType);
208 this.contextName = new NodeIdentifier(leafName);
211 @SuppressWarnings("rawtypes")
213 NormalizedNode<?, ?> serialize(final TreeNode input) {
214 final InstanceIdentifier<?> bindingII = refExtractor.extract(input);
215 if (bindingII != null) {
216 final YangInstanceIdentifier yangII = codec.toYangInstanceIdentifierCached(bindingII);
217 final LeafNode contextRef = ImmutableNodes.leafNode(contextName, yangII);
218 return LazySerializedContainerNode.withContextRef(getRpcName(), input, contextRef,
219 codec.getCodecRegistry());
221 return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());