Binding2 - Add yang and tests
[mdsal.git] / binding2 / mdsal-binding2-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / javav2 / dom / adapter / impl / operation / ActionServiceAdapter.java
1 /*
2  * Copyright (c) 2017 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.javav2.dom.adapter.impl.operation;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.util.concurrent.FluentFuture;
14 import com.google.common.util.concurrent.FutureCallback;
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 java.lang.reflect.Proxy;
21 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
22 import org.opendaylight.mdsal.binding.javav2.dom.codec.serialized.LazySerializedContainerNode;
23 import org.opendaylight.mdsal.binding.javav2.spec.base.Action;
24 import org.opendaylight.mdsal.binding.javav2.spec.base.Input;
25 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
26 import org.opendaylight.mdsal.binding.javav2.spec.base.Output;
27 import org.opendaylight.mdsal.binding.javav2.spec.base.RpcCallback;
28 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.mdsal.dom.api.DOMActionResult;
31 import org.opendaylight.mdsal.dom.api.DOMActionService;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
33 import org.opendaylight.yangtools.yang.common.RpcResult;
34 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
35 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
36
37 @Beta
38 class ActionServiceAdapter implements InvocationHandler {
39
40     private final Class<? extends Action<? extends TreeNode, ?, ?, ?>> type;
41     private final BindingToNormalizedNodeCodec codec;
42     private final DOMActionService delegate;
43     private final Action<? extends TreeNode, ?, ?, ?> proxy;
44     private final SchemaPath path;
45
46     ActionServiceAdapter(final Class<? extends Action<? extends TreeNode, ?, ?, ?>> type,
47             final BindingToNormalizedNodeCodec codec, final DOMActionService domService) {
48         this.type = requireNonNull(type);
49         this.codec = requireNonNull(codec);
50         this.delegate = requireNonNull(domService);
51         this.path = getCodec().getActionPath(type);
52         proxy = (Action<? extends TreeNode, ?, ?, ?>) Proxy.newProxyInstance(type.getClassLoader(),
53             new Class[] { type }, this);
54     }
55
56     public BindingToNormalizedNodeCodec getCodec() {
57         return codec;
58     }
59
60     public DOMActionService getDelegate() {
61         return delegate;
62     }
63
64     Action<? extends TreeNode, ?, ?, ?> getProxy() {
65         return proxy;
66     }
67
68     public Class<? extends Action<? extends TreeNode, ?, ?, ?>> getType() {
69         return type;
70     }
71
72     public SchemaPath getPath() {
73         return path;
74     }
75
76     @Override
77     @SuppressWarnings("checkstyle:hiddenField")
78     public Object invoke(final Object proxy, final Method method, final Object[] args) {
79
80         switch (method.getName()) {
81             case "equals":
82                 if (args.length == 1) {
83                     return proxy == args[0];
84                 }
85                 break;
86             case "hashCode":
87                 if (args.length == 0) {
88                     return System.identityHashCode(proxy);
89                 }
90                 break;
91             case "toString":
92                 if (args.length == 0) {
93                     return type.getName() + "$Adapter{delegate=" + getDelegate() + "}";
94                 }
95                 break;
96             case "invoke":
97                 if (args.length == 3) {
98                     final Input<?> input = (Input<?>) requireNonNull(args[0]);
99                     final InstanceIdentifier<?> path = (InstanceIdentifier<?>) requireNonNull(args[1]);
100                     final RpcCallback<Output> callback = (RpcCallback<Output>) requireNonNull(args[2]);
101
102                     final FluentFuture<? extends DOMActionResult> future = getDelegate().invokeAction(getPath(),
103                         new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, getCodec().toNormalized(path)),
104                         (ContainerNode) LazySerializedContainerNode.create(getPath(), (TreeNode) input,
105                             getCodec().getCodecRegistry()));
106                     //FIXME:this part is ugly, how to bridge FluentFuture and RpcCallback better?
107                     // Invocation returned a future we know about -- return that future instead
108                     if (future instanceof LazyDOMActionResultFuture) {
109                         ListenableFuture<RpcResult<?>> bindingFuture =
110                             ((LazyDOMActionResultFuture) future).getBindingFuture();
111                         Futures.addCallback(bindingFuture, new FutureCallback<RpcResult<?>>() {
112
113                             @Override
114                             public void onSuccess(final RpcResult<?> result) {
115                                 if (result.isSuccessful()) {
116                                     callback.onSuccess((Output) result.getResult());
117                                 } else {
118                                     //FIXME: It's not suitable to do this way here. It's better for
119                                     // 'onFailure' to accept Collection<RpcError> as input.
120                                     result.getErrors().forEach(e -> callback.onFailure(e.getCause()));
121                                 }
122                             }
123
124                             @Override
125                             public void onFailure(final Throwable throwable) {
126                                 callback.onFailure(throwable);
127                             }
128                         }, MoreExecutors.directExecutor());
129                     } else {
130                         Futures.addCallback(future, new FutureCallback<DOMActionResult>() {
131
132                             @Override
133                             public void onSuccess(final DOMActionResult result) {
134                                 if (result.getErrors().isEmpty()) {
135                                     callback.onSuccess((Output) getCodec().fromNormalizedNodeOperationData(getPath(),
136                                         result.getOutput().get()));
137                                 } else {
138                                     result.getErrors().forEach(e -> callback.onFailure(e.getCause()));
139                                 }
140                             }
141
142                             @Override
143                             public void onFailure(final Throwable throwable) {
144                                 callback.onFailure(throwable);
145                             }
146                         }, MoreExecutors.directExecutor());
147                     }
148                 }
149                 return 0;
150             default:
151                 break;
152         }
153
154         throw new UnsupportedOperationException("Method " + method.toString() + "is unsupported.");
155     }
156 }