Integrate Action with BindingContract
[mdsal.git] / binding / mdsal-binding-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / dom / adapter / ActionAdapter.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.dom.adapter;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.mdsal.binding.dom.adapter.StaticConfiguration.ENABLE_CODEC_SHORTCUT;
13 import static org.opendaylight.yangtools.yang.common.YangConstants.operationInputQName;
14
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.Method;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.mdsal.binding.api.ActionSpec;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.mdsal.dom.api.DOMActionResult;
24 import org.opendaylight.mdsal.dom.api.DOMActionService;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.opendaylight.yangtools.yang.binding.RpcInput;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
30
31 final class ActionAdapter extends AbstractBindingAdapter<DOMActionService> implements InvocationHandler {
32     private final @NonNull ActionSpec<?, ?> spec;
33     private final @NonNull NodeIdentifier inputName;
34     private final @NonNull Absolute actionPath;
35
36     ActionAdapter(final AdapterContext codec, final DOMActionService delegate, final ActionSpec<?, ?> spec) {
37         super(codec, delegate);
38         this.spec = requireNonNull(spec);
39         actionPath = currentSerializer().getActionPath(spec);
40         inputName = NodeIdentifier.create(operationInputQName(actionPath.lastNodeIdentifier().getModule()));
41     }
42
43     @Override
44     public Object invoke(final Object proxy, final Method method, final Object [] args) throws Throwable {
45         switch (method.getName()) {
46             case "equals":
47                 if (args.length == 1) {
48                     return proxy == args[0];
49                 }
50                 break;
51             case "hashCode":
52                 if (args.length == 0) {
53                     return System.identityHashCode(proxy);
54                 }
55                 break;
56             case "toString":
57                 if (args.length == 0) {
58                     return spec.type().getName() + "$Adapter{delegate=" + getDelegate() + "}";
59                 }
60                 break;
61             case "invoke":
62                 if (args.length == 2) {
63                     final InstanceIdentifier<?> path = (InstanceIdentifier<?>) requireNonNull(args[0]);
64                     checkArgument(!path.isWildcarded(), "Cannot invoke action on wildcard path %s", path);
65
66                     final RpcInput input = (RpcInput) requireNonNull(args[1]);
67                     final CurrentAdapterSerializer serializer = currentSerializer();
68                     final ListenableFuture<? extends DOMActionResult> future = getDelegate().invokeAction(actionPath,
69                         new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
70                             serializer.toYangInstanceIdentifier(path)),
71                         serializer.toLazyNormalizedNodeActionInput(spec.type(), inputName, input));
72
73                     // Invocation returned a future we know about -- return that future instead
74                     if (ENABLE_CODEC_SHORTCUT && future instanceof BindingRpcFutureAware bindingAware) {
75                         return bindingAware.getBindingFuture();
76                     }
77
78                     return Futures.transform(future,
79                         dom -> RpcResultUtil.rpcResultFromDOM(dom.getErrors(), dom.getOutput()
80                             .map(output -> serializer.fromNormalizedNodeActionOutput(spec.type(), output))
81                             .orElse(null)),
82                         MoreExecutors.directExecutor());
83                 }
84                 break;
85             default:
86                 break;
87         }
88
89         if (method.isDefault()) {
90             return InvocationHandler.invokeDefault(proxy, method, args);
91         }
92         throw new NoSuchMethodError("Method " + method.toString() + "is unsupported.");
93     }
94 }