Binding2 - Add yang and tests
[mdsal.git] / binding2 / mdsal-binding2-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / javav2 / dom / adapter / impl / operation / BindingDOMOperationProviderServiceAdapter.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.collect.ImmutableSet;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.SettableFuture;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.HashSet;
19 import java.util.Set;
20 import java.util.concurrent.CompletableFuture;
21 import javax.annotation.Nonnull;
22 import javax.annotation.Nullable;
23 import org.opendaylight.mdsal.binding.javav2.api.DataTreeIdentifier;
24 import org.opendaylight.mdsal.binding.javav2.api.RpcActionProviderService;
25 import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.operation.BindingDOMOperationProviderServiceAdapter.AbstractImplAdapter.ActionAdapter;
26 import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.operation.BindingDOMOperationProviderServiceAdapter.AbstractImplAdapter.RpcAdapter;
27 import org.opendaylight.mdsal.binding.javav2.dom.adapter.registration.BindingDOMOperationAdapterRegistration;
28 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
29 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
30 import org.opendaylight.mdsal.binding.javav2.dom.codec.serialized.LazySerializedContainerNode;
31 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
32 import org.opendaylight.mdsal.binding.javav2.spec.base.Action;
33 import org.opendaylight.mdsal.binding.javav2.spec.base.Input;
34 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
35 import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
36 import org.opendaylight.mdsal.binding.javav2.spec.base.Output;
37 import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
38 import org.opendaylight.mdsal.binding.javav2.spec.base.RpcCallback;
39 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
40 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
41 import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
42 import org.opendaylight.mdsal.dom.api.DOMActionInstance;
43 import org.opendaylight.mdsal.dom.api.DOMActionProviderService;
44 import org.opendaylight.mdsal.dom.api.DOMActionResult;
45 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
46 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
47 import org.opendaylight.mdsal.dom.api.DOMRpcImplementation;
48 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationRegistration;
49 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
50 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
51 import org.opendaylight.yangtools.concepts.ObjectRegistration;
52 import org.opendaylight.yangtools.yang.common.QName;
53 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
54 import org.opendaylight.yangtools.yang.common.RpcResult;
55 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
56 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
57 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
59 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
60
61 /**
62  * Operation service provider adapter.
63  */
64 @Beta
65 public class BindingDOMOperationProviderServiceAdapter implements RpcActionProviderService {
66
67     private static final Set<YangInstanceIdentifier> GLOBAL = ImmutableSet.of(YangInstanceIdentifier.builder().build());
68     private final BindingToNormalizedNodeCodec codec;
69     private final DOMRpcProviderService domRpcRegistry;
70     private final DOMActionProviderService domActionRegistry;
71
72     public BindingDOMOperationProviderServiceAdapter(final DOMRpcProviderService domRpcRegistry,
73             final DOMActionProviderService domActionRegistry, final BindingToNormalizedNodeCodec codec) {
74         this.codec = codec;
75         this.domRpcRegistry = domRpcRegistry;
76         this.domActionRegistry = domActionRegistry;
77     }
78
79     @Override
80     public <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
81             final T implementation) {
82         return register(type, implementation, GLOBAL);
83     }
84
85     @Override
86     public <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
87             final T implementation, final Set<InstanceIdentifier<?>> paths) {
88         return register(type, implementation, toYangInstanceIdentifiers(paths));
89     }
90
91     private <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> register(final Class<S> type,
92             final T implementation, final Collection<YangInstanceIdentifier> rpcContextPaths) {
93         final SchemaPath path = codec.getRpcPath(type);
94         final Set<DOMRpcIdentifier> domRpcs = createDomRpcIdentifiers(path, rpcContextPaths);
95         final DOMRpcImplementationRegistration<?> domReg = domRpcRegistry.registerRpcImplementation(
96             new RpcAdapter(codec.getCodecRegistry(), type, implementation), domRpcs);
97         return new BindingDOMOperationAdapterRegistration<>(implementation, domReg);
98     }
99
100     private static Set<DOMRpcIdentifier> createDomRpcIdentifiers(final SchemaPath rpc,
101             final Collection<YangInstanceIdentifier> paths) {
102         final Set<DOMRpcIdentifier> ret = new HashSet<>();
103         for (final YangInstanceIdentifier path : paths) {
104             ret.add(DOMRpcIdentifier.create(rpc, path));
105         }
106         return ret;
107     }
108
109     private Collection<YangInstanceIdentifier> toYangInstanceIdentifiers(final Set<InstanceIdentifier<?>> identifiers) {
110         final Collection<YangInstanceIdentifier> ret = new ArrayList<>(identifiers.size());
111         for (final InstanceIdentifier<?> binding : identifiers) {
112             ret.add(codec.toYangInstanceIdentifierCached(binding));
113         }
114         return ret;
115     }
116
117     @Override
118     public <S extends Action<? extends TreeNode, ?, ?, ?>, T extends S, P extends TreeNode> ObjectRegistration<T>
119             registerActionImplementation(final Class<S> type, final T implementation,
120                 final LogicalDatastoreType datastore, final Set<DataTreeIdentifier<P>> validNodes) {
121         final SchemaPath path = codec.getActionPath(type);
122         final ObjectRegistration<ActionAdapter> domReg = domActionRegistry.registerActionImplementation(
123             new ActionAdapter(codec.getCodecRegistry(), type, implementation),
124             DOMActionInstance.of(path, codec.toDOMDataTreeIdentifiers(validNodes)));
125         return new BindingDOMOperationAdapterRegistration<>(implementation, domReg);
126     }
127
128     public abstract static class AbstractImplAdapter<D> {
129         protected final BindingNormalizedNodeCodecRegistry codec;
130         protected final D delegate;
131         private final QName inputQname;
132
133         AbstractImplAdapter(final BindingNormalizedNodeCodecRegistry codec, final Class<? extends Operation> clazz,
134                    final D delegate) {
135             this.codec = requireNonNull(codec);
136             this.delegate = requireNonNull(delegate);
137             inputQname = QName.create(BindingReflections.getQNameModule(clazz), "input").intern();
138         }
139
140         TreeNode deserialize(final SchemaPath path, final NormalizedNode<?, ?> input) {
141             if (input instanceof LazySerializedContainerNode) {
142                 return ((LazySerializedContainerNode) input).bindingData();
143             }
144             final SchemaPath inputSchemaPath = path.createChild(inputQname);
145             return codec.fromNormalizedNodeOperationData(inputSchemaPath, (ContainerNode) input);
146         }
147
148         public static final class RpcAdapter extends AbstractImplAdapter<Rpc> implements DOMRpcImplementation {
149
150             RpcAdapter(BindingNormalizedNodeCodecRegistry codec, Class<? extends Operation> clazz,
151                     Rpc<?, ?> delegate) {
152                 super(codec, clazz, delegate);
153             }
154
155             @SuppressWarnings("checkstyle:illegalCatch, unchecked")
156             @Nonnull
157             @Override
158             public FluentFuture<DOMRpcResult> invokeRpc(@Nonnull final DOMRpcIdentifier rpc,
159                                                         @Nullable final NormalizedNode<?, ?> input) {
160                 final TreeNode bindingInput = input != null ? deserialize(rpc.getType(), input) : null;
161                 final SettableFuture<RpcResult<?>> bindingResult = SettableFuture.create();
162                 CompletableFuture.runAsync(() -> delegate.invoke((Input<?>) bindingInput,
163                     new RpcCallback<Output<?>>() {
164                         public void onSuccess(Output<?> output) {
165                             bindingResult.set(RpcResultBuilder.success(output).build());
166                         }
167
168                         public void onFailure(Throwable error) {
169                             bindingResult.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION,
170                                 error.getMessage(), error).build());
171                         }
172                     })
173                 );
174                 return LazyDOMRpcResultFuture.create(codec,bindingResult);
175             }
176         }
177
178         public static final class ActionAdapter extends AbstractImplAdapter<Action>
179                 implements DOMActionImplementation {
180
181             ActionAdapter(BindingNormalizedNodeCodecRegistry codec, Class<? extends Operation> clazz,
182                     Action<?, ?, ?, ?> delegate) {
183                 super(codec, clazz, delegate);
184             }
185
186             @SuppressWarnings("checkstyle:illegalCatch, unchecked")
187             @Nonnull
188             public FluentFuture<? extends DOMActionResult> invokeAction(SchemaPath type, DOMDataTreeIdentifier path,
189                     ContainerNode input) {
190                 final TreeNode bindingInput = input != null ? deserialize(type, input) : null;
191                 final SettableFuture<RpcResult<?>> bindingResult = SettableFuture.create();
192                 CompletableFuture.runAsync(() -> delegate.invoke((Input<?>) bindingInput,
193                     codec.fromYangInstanceIdentifier(path.getRootIdentifier()),
194                     new RpcCallback<Output<?>>() {
195                         public void onSuccess(Output<?> output) {
196                             bindingResult.set(RpcResultBuilder.success(output).build());
197                         }
198
199                         public void onFailure(Throwable error) {
200                             bindingResult.set(RpcResultBuilder.failed().withError(ErrorType.APPLICATION,
201                                 error.getMessage(), error).build());
202                         }
203                     })
204                 );
205                 return LazyDOMActionResultFuture.create(codec, bindingResult);
206             }
207         }
208     }
209 }