import org.opendaylight.mdsal.binding.javav2.spec.base.Action;
import org.opendaylight.mdsal.binding.javav2.spec.base.ListAction;
import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
/**
* Provides access to registered Remote Procedure Call (RPC) and Action service implementations.
* @param <T> interface type
* @return returns proxy for the requested RPC
*/
- <T extends Rpc> T getRpcService(Class<T> serviceInterface);
+ <T extends Rpc<?, ?>> T getRpcService(Class<T> serviceInterface);
/**
* Returns an implementation of a requested Action service.
* @param <T> interface type
* @return returns proxy for the requested Action
*/
- <T extends Action> T getActionService(Class<T> serviceInterface);
+ <T extends Action<? extends TreeNode, ?, ?>> T getActionService(Class<T> serviceInterface);
/**
* Returns an implementation of a requested ListAction service.
* @param <T> interface type
* @return returns proxy for the requested ListAction
*/
- <T extends ListAction> T getListActionService(Class<T> serviceInterface);
+ <T extends ListAction<? extends TreeNode, ?, ?>> T getListActionService(Class<T> serviceInterface);
}
* @param <T> service implementation type
* @return returns class representing a RPC registration
*/
- <S extends Rpc, T extends S> ObjectRegistration<T> registerRpcImplementation(Class<S> type,
+ <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> registerRpcImplementation(Class<S> type,
T implementation);
/**
* @param <T> service implementation type
* @return returns class representing a RPC registration
*/
- <S extends Rpc, T extends S> ObjectRegistration<T> registerRpcImplementation(Class<S> type,
+ <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> registerRpcImplementation(Class<S> type,
T implementation, Set<InstanceIdentifier<?>> paths);
/**
* @param <T> service implementation type
* @return returns class representing a Action registration
*/
- <S extends Action, T extends S, P extends TreeNode> ObjectRegistration<T> registerActionImplementation(
+ <S extends Action<? extends TreeNode, ?, ?>, T extends S, P extends TreeNode> ObjectRegistration<T>
+ registerActionImplementation(
Class<S> type, InstanceIdentifier<P> parent, T implementation);
/**
* @param <T> service implementation type
* @return returns class representing a ListAction registration
*/
- <S extends ListAction, T extends S, P extends TreeNode, K> ObjectRegistration<T> registerListActionImplementation(
+ <S extends ListAction<? extends TreeNode, ?, ?>, T extends S, P extends TreeNode, K> ObjectRegistration<T>
+ registerListActionImplementation(
Class<S> type, KeyedInstanceIdentifier<P, K> parent, T implementation);
}
/**
* Extract context-reference (Instance Identifier) from Binding TreeNode.
*
- * @param obj
+ * @param input
* - TreeNode from which context reference should be extracted.
*
* @return Instance Identifier representing context reference or null, if tree node does not contain
* context reference.
*/
@Nullable
- public abstract InstanceIdentifier<? extends TreeNode> extract(TreeNode obj);
+ public abstract InstanceIdentifier<? extends TreeNode> extract(TreeNode input);
/**
* Method for return specific extractor of input object.
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.adapter.impl.operation;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.operation.invoker.OperationServiceInvoker;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.serialized.LazySerializedContainerNode;
+import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.dom.api.DOMRpcException;
+import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMRpcImplementation;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Operation implementation adapter.
+ */
+@Beta
+public class BindingDOMOperationImplementationAdapter implements DOMRpcImplementation {
+
+ private static final Cache<Class<? extends Operation>, OperationServiceInvoker> SERVICE_INVOKERS =
+ CacheBuilder.newBuilder().weakKeys().build();
+ // Default implementations are 0, we need to perform some translation, hence we have a slightly higher
+ // cost
+ private static final int COST = 1;
+
+ private final BindingNormalizedNodeCodecRegistry codec;
+ private final OperationServiceInvoker invoker;
+ private final Operation delegate;
+ private final QName inputQname;
+
+ <T extends Operation> BindingDOMOperationImplementationAdapter(final BindingNormalizedNodeCodecRegistry codec,
+ final Class<T> type, final Map<SchemaPath, Method> localNameToMethod, final T delegate) {
+ try {
+ this.invoker = SERVICE_INVOKERS.get(type, () -> {
+ final Map<QName, Method> map = new HashMap<>();
+ for (final Entry<SchemaPath, Method> e : localNameToMethod.entrySet()) {
+ map.put(e.getKey().getLastComponent(), e.getValue());
+ }
+
+ return OperationServiceInvoker.from(map);
+ });
+ } catch (final ExecutionException e) {
+ throw new IllegalArgumentException("Failed to create invokers for type " + type, e);
+ }
+
+ this.codec = Preconditions.checkNotNull(codec);
+ this.delegate = Preconditions.checkNotNull(delegate);
+ inputQname = QName.create(BindingReflections.getQNameModule(type), "input").intern();
+ }
+
+ @Nonnull
+ @Override
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull final DOMRpcIdentifier rpc,
+ @Nullable final NormalizedNode<?, ?> input) {
+
+ final SchemaPath schemaPath = rpc.getType();
+ final TreeNode bindingInput = input != null ? deserialize(rpc.getType(), input) : null;
+ final ListenableFuture<RpcResult<?>> bindingResult = invoke(schemaPath, bindingInput);
+ return transformResult(bindingResult);
+ }
+
+ @Override
+ public long invocationCost() {
+ return COST;
+ }
+
+ private TreeNode deserialize(final SchemaPath rpcPath, final NormalizedNode<?, ?> input) {
+ if (input instanceof LazySerializedContainerNode) {
+ return ((LazySerializedContainerNode) input).bindingData();
+ }
+ final SchemaPath inputSchemaPath = rpcPath.createChild(inputQname);
+ return codec.fromNormalizedNodeOperationData(inputSchemaPath, (ContainerNode) input);
+ }
+
+ private ListenableFuture<RpcResult<?>> invoke(final SchemaPath schemaPath, final TreeNode input) {
+ return JdkFutureAdapters.listenInPoolThread(invoker.invoke(delegate, schemaPath.getLastComponent(), input));
+ }
+
+ private CheckedFuture<DOMRpcResult, DOMRpcException>
+ transformResult(final ListenableFuture<RpcResult<?>> bindingResult) {
+ return LazyDOMOperationResultFuture.create(codec, bindingResult);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.adapter.impl.operation;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.mdsal.binding.javav2.api.RpcActionProviderService;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.registration.BindingDOMOperationAdapterRegistration;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Action;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.KeyedInstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.ListAction;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+//FIXME missing support of Action operation (dependence on support of Yang 1.1 in DOM part of MD-SAL)
+/**
+ * Operation service provider adapter.
+ */
+@Beta
+public class BindingDOMOperationProviderServiceAdapter implements RpcActionProviderService {
+
+ private static final Set<YangInstanceIdentifier> GLOBAL = ImmutableSet.of(YangInstanceIdentifier.builder().build());
+ private final BindingToNormalizedNodeCodec codec;
+ private final DOMRpcProviderService domRpcRegistry;
+
+ public BindingDOMOperationProviderServiceAdapter(final DOMRpcProviderService domRpcRegistry,
+ final BindingToNormalizedNodeCodec codec) {
+ this.codec = codec;
+ this.domRpcRegistry = domRpcRegistry;
+ }
+
+ @Override
+ public <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
+ final T implementation) {
+ return register(type, implementation, GLOBAL);
+ }
+
+ @Override
+ public <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
+ final T implementation, final Set<InstanceIdentifier<?>> paths) {
+ return register(type, implementation, toYangInstanceIdentifiers(paths));
+ }
+
+ private <S extends Rpc<?, ?>, T extends S> ObjectRegistration<T> register(final Class<S> type,
+ final T implementation, final Collection<YangInstanceIdentifier> rpcContextPaths) {
+ final Map<SchemaPath, Method> rpcs = codec.getRPCMethodToSchemaPath(type).inverse();
+
+ final BindingDOMOperationImplementationAdapter adapter =
+ new BindingDOMOperationImplementationAdapter(codec.getCodecRegistry(), type, rpcs, implementation);
+ final Set<DOMRpcIdentifier> domRpcs = createDomRpcIdentifiers(rpcs.keySet(), rpcContextPaths);
+ final DOMRpcImplementationRegistration<?> domReg = domRpcRegistry.registerRpcImplementation(adapter, domRpcs);
+ return new BindingDOMOperationAdapterRegistration<>(implementation, domReg);
+ }
+
+ private static Set<DOMRpcIdentifier> createDomRpcIdentifiers(final Set<SchemaPath> rpcs,
+ final Collection<YangInstanceIdentifier> paths) {
+ final Set<DOMRpcIdentifier> ret = new HashSet<>();
+ for (final YangInstanceIdentifier path : paths) {
+ for (final SchemaPath rpc : rpcs) {
+ ret.add(DOMRpcIdentifier.create(rpc, path));
+ }
+ }
+ return ret;
+ }
+
+ private Collection<YangInstanceIdentifier> toYangInstanceIdentifiers(final Set<InstanceIdentifier<?>> identifiers) {
+ final Collection<YangInstanceIdentifier> ret = new ArrayList<>(identifiers.size());
+ for (final InstanceIdentifier<?> binding : identifiers) {
+ ret.add(codec.toYangInstanceIdentifierCached(binding));
+ }
+ return ret;
+ }
+
+ @Override
+ public <S extends Action<? extends TreeNode, ?, ?>, T extends S, P extends TreeNode> ObjectRegistration<T>
+ registerActionImplementation(final Class<S> type, final InstanceIdentifier<P> parent,
+ final T implementation) {
+ // TODO implement after improve DOM part of MD-SAL for support of Yang 1.1
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <S extends ListAction<? extends TreeNode, ?, ?>, T extends S, P extends TreeNode, K> ObjectRegistration<T>
+ registerListActionImplementation(final Class<S> type, final KeyedInstanceIdentifier<P, K> parent,
+ final T implementation) {
+ // TODO implement after improve DOM part of MD-SAL for support of Yang 1.1
+ throw new UnsupportedOperationException();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.adapter.impl.operation;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.binding.javav2.api.RpcActionConsumerRegistry;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Action;
+import org.opendaylight.mdsal.binding.javav2.spec.base.ListAction;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.api.DOMService;
+
+//FIXME implement after improve DOM part of MD-SAL for support of Yang 1.1
+/**
+ * Adapter for operation service.
+ */
+@Beta
+public class BindingDOMOperationServiceAdapter implements RpcActionConsumerRegistry {
+
+ public static final Factory<RpcActionConsumerRegistry> BUILDER_FACTORY = Builder::new;
+
+ private final DOMRpcService domService;
+ private final BindingToNormalizedNodeCodec codec;
+ private final LoadingCache<Class<? extends Operation>, RpcServiceAdapter> proxies = CacheBuilder.newBuilder()
+ .weakKeys().build(new CacheLoader<Class<? extends Operation>, RpcServiceAdapter>() {
+
+ @SuppressWarnings("unchecked")
+ private RpcServiceAdapter createProxy(final Class<? extends Operation> key) {
+ Preconditions.checkArgument(BindingReflections.isBindingClass(key));
+ Preconditions.checkArgument(key.isInterface(),
+ "Supplied Operation service type must be interface.");
+ if (Rpc.class.isAssignableFrom(key)) {
+ return new RpcServiceAdapter((Class<? extends Rpc<?, ?>>) key, codec, domService);
+ }
+ // TODO implement after improve DOM part of MD-SAL for support of Yang 1.1
+ throw new UnsupportedOperationException();
+ }
+
+ @Nonnull
+ @Override
+ public RpcServiceAdapter load(@Nonnull final Class<? extends Operation> key) throws Exception {
+ return createProxy(key);
+ }
+
+ });
+
+ public BindingDOMOperationServiceAdapter(final DOMRpcService domService, final BindingToNormalizedNodeCodec codec) {
+ this.domService = Preconditions.checkNotNull(domService);
+ this.codec = Preconditions.checkNotNull(codec);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends Rpc<?, ?>> T getRpcService(final Class<T> rpc) {
+ Preconditions.checkArgument(rpc != null, "Rpc needs to be specified.");
+ return (T) proxies.getUnchecked(rpc).getProxy();
+ }
+
+ private static final class Builder extends BindingDOMAdapterBuilder<RpcActionConsumerRegistry> {
+
+ @Override
+ protected RpcActionConsumerRegistry createInstance(final BindingToNormalizedNodeCodec codec,
+ final ClassToInstanceMap<DOMService> delegates) {
+ final DOMRpcService domRpc = delegates.getInstance(DOMRpcService.class);
+ return new BindingDOMOperationServiceAdapter(domRpc, codec);
+ }
+
+ @Override
+ public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+ return ImmutableSet.of(DOMRpcService.class);
+ }
+ }
+
+ @Override
+ public <T extends Action<? extends TreeNode, ?, ?>> T getActionService(final Class<T> serviceInterface) {
+ // TODO implement after improve DOM part of MD-SAL for support of Yang 1.1
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T extends ListAction<? extends TreeNode, ?, ?>> T getListActionService(final Class<T> serviceInterface) {
+ // TODO implement after improve DOM part of MD-SAL for support of Yang 1.1
+ throw new UnsupportedOperationException();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.adapter.impl.operation;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.dom.api.DOMRpcException;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * DOM operation result from Binding.
+ */
+@Beta
+final class LazyDOMOperationResultFuture implements CheckedFuture<DOMRpcResult, DOMRpcException> {
+
+ private final ListenableFuture<RpcResult<?>> bindingFuture;
+ private final BindingNormalizedNodeCodecRegistry codec;
+ private volatile DOMRpcResult result;
+
+ private LazyDOMOperationResultFuture(final ListenableFuture<RpcResult<?>> delegate,
+ final BindingNormalizedNodeCodecRegistry codec) {
+ this.bindingFuture = Preconditions.checkNotNull(delegate, "delegate");
+ this.codec = Preconditions.checkNotNull(codec, "codec");
+ }
+
+ static CheckedFuture<DOMRpcResult, DOMRpcException> create(final BindingNormalizedNodeCodecRegistry codec,
+ final ListenableFuture<RpcResult<?>> bindingResult) {
+ return new LazyDOMOperationResultFuture(bindingResult, codec);
+ }
+
+ ListenableFuture<RpcResult<?>> getBindingFuture() {
+ return bindingFuture;
+ }
+
+ @Override
+ public boolean cancel(final boolean mayInterruptIfRunning) {
+ return bindingFuture.cancel(mayInterruptIfRunning);
+ }
+
+ @Override
+ public void addListener(@Nonnull final Runnable listener, @Nonnull final Executor executor) {
+ bindingFuture.addListener(listener, executor);
+ }
+
+ @Override
+ public DOMRpcResult get() throws InterruptedException, ExecutionException {
+ if (result != null) {
+ return result;
+ }
+ return transformIfNecessary(bindingFuture.get());
+ }
+
+ @Override
+ public DOMRpcResult get(@Nonnull final long timeout, final TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ if (result != null) {
+ return result;
+ }
+ return transformIfNecessary(bindingFuture.get(timeout, unit));
+ }
+
+ @Override
+ public DOMRpcResult checkedGet() throws DOMRpcException {
+ try {
+ return get();
+ } catch (InterruptedException | ExecutionException e) {
+ // FIXME: Add exception mapping
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public DOMRpcResult checkedGet(@Nonnull final long timeout, final TimeUnit unit)
+ throws TimeoutException, DOMRpcException {
+ try {
+ return get(timeout, unit);
+ } catch (InterruptedException | ExecutionException e) {
+ // FIXME: Add exception mapping
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return bindingFuture.isCancelled();
+ }
+
+ @Override
+ public boolean isDone() {
+ return bindingFuture.isDone();
+ }
+
+ private synchronized DOMRpcResult transformIfNecessary(final RpcResult<?> input) {
+ if (result == null) {
+ result = transform(input);
+ }
+ return result;
+ }
+
+ private DOMRpcResult transform(final RpcResult<?> input) {
+ if (input.isSuccessful()) {
+ final Object inputData = input.getResult();
+ if (inputData instanceof DataContainer) {
+ return new DefaultDOMRpcResult(codec.toNormalizedNodeOperationData((TreeNode) inputData));
+ } else {
+ return new DefaultDOMRpcResult((NormalizedNode<?, ?>) null);
+ }
+ }
+ return new DefaultDOMRpcResult(input.getErrors());
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.adapter.impl.operation;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Map.Entry;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.extractor.ContextReferenceExtractor;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.serialized.LazySerializedContainerNode;
+import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.dom.api.DOMRpcException;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.spi.RpcRoutingStrategy;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@Beta
+class RpcServiceAdapter implements InvocationHandler {
+
+ private final ImmutableMap<Method, RpcInvocationStrategy> rpcNames;
+ private final Class<? extends Rpc<?, ?>> type;
+ private final BindingToNormalizedNodeCodec codec;
+ private final DOMRpcService delegate;
+ private final Rpc<?, ?> proxy;
+
+ RpcServiceAdapter(final Class<? extends Rpc<?, ?>> type, final BindingToNormalizedNodeCodec codec,
+ final DOMRpcService domService) {
+ this.type = Preconditions.checkNotNull(type);
+ this.codec = Preconditions.checkNotNull(codec);
+ this.delegate = Preconditions.checkNotNull(domService);
+ final ImmutableMap.Builder<Method, RpcInvocationStrategy> rpcBuilder = ImmutableMap.builder();
+ for (final Entry<Method, OperationDefinition> rpc : codec.getRPCMethodToSchema(type).entrySet()) {
+ rpcBuilder.put(rpc.getKey(), createStrategy(rpc.getKey(), (RpcDefinition) rpc.getValue()));
+ }
+ rpcNames = rpcBuilder.build();
+ proxy = (Rpc<?, ?>) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type }, this);
+ }
+
+ private RpcInvocationStrategy createStrategy(final Method method, final RpcDefinition schema) {
+ final RpcRoutingStrategy strategy = RpcRoutingStrategy.from(schema);
+ if (strategy.isContextBasedRouted()) {
+ return new RoutedStrategy(schema.getPath(), method, strategy.getLeaf());
+ }
+ return new NonRoutedStrategy(schema.getPath());
+ }
+
+ Rpc<?, ?> getProxy() {
+ return proxy;
+ }
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+
+ final RpcInvocationStrategy rpc = rpcNames.get(method);
+ if (rpc != null) {
+ if (method.getParameterTypes().length == 0) {
+ return rpc.invokeEmpty();
+ }
+ if (args.length != 1) {
+ throw new IllegalArgumentException("Input must be provided.");
+ }
+ return rpc.invoke((TreeNode) args[0]);
+ }
+
+ if (isObjectMethod(method)) {
+ return callObjectMethod(proxy, method, args);
+ }
+ throw new UnsupportedOperationException("Method " + method.toString() + "is unsupported.");
+ }
+
+ private static boolean isObjectMethod(final Method method) {
+ switch (method.getName()) {
+ case "toString":
+ return method.getReturnType().equals(String.class) && method.getParameterTypes().length == 0;
+ case "hashCode":
+ return method.getReturnType().equals(int.class) && method.getParameterTypes().length == 0;
+ case "equals":
+ return method.getReturnType().equals(boolean.class) && method.getParameterTypes().length == 1
+ && method.getParameterTypes()[0] == Object.class;
+ default:
+ return false;
+ }
+ }
+
+ private Object callObjectMethod(final Object self, final Method method, final Object[] args) {
+ switch (method.getName()) {
+ case "toString":
+ return type.getName() + "$Adapter{delegate=" + delegate.toString() + "}";
+ case "hashCode":
+ return System.identityHashCode(self);
+ case "equals":
+ return self == args[0];
+ default:
+ return null;
+ }
+ }
+
+ private abstract class RpcInvocationStrategy {
+
+ private final SchemaPath rpcName;
+
+ protected RpcInvocationStrategy(final SchemaPath path) {
+ rpcName = path;
+ }
+
+ final ListenableFuture<RpcResult<?>> invoke(final TreeNode input) {
+ return invoke0(rpcName, serialize(input));
+ }
+
+ abstract NormalizedNode<?, ?> serialize(TreeNode input);
+
+ final ListenableFuture<RpcResult<?>> invokeEmpty() {
+ return invoke0(rpcName, null);
+ }
+
+ final SchemaPath getRpcName() {
+ return rpcName;
+ }
+
+ private ListenableFuture<RpcResult<?>> invoke0(final SchemaPath schemaPath, final NormalizedNode<?, ?> input) {
+ final CheckedFuture<DOMRpcResult, DOMRpcException> result = delegate.invokeRpc(schemaPath, input);
+
+ if (result instanceof LazyDOMOperationResultFuture) {
+ return ((LazyDOMOperationResultFuture) result).getBindingFuture();
+ }
+
+ return transformFuture(schemaPath, result, codec.getCodecFactory());
+ }
+
+ private ListenableFuture<RpcResult<?>> transformFuture(final SchemaPath rpc,
+ final ListenableFuture<DOMRpcResult> domFuture, final BindingNormalizedNodeCodecRegistry codec) {
+ return Futures.transform(domFuture, (Function<DOMRpcResult, RpcResult<?>>) input -> {
+ final NormalizedNode<?, ?> domData = input.getResult();
+ final TreeNode bindingResult;
+ if (domData != null) {
+ final SchemaPath rpcOutput = rpc.createChild(QName.create(rpc.getLastComponent(), "output"));
+ bindingResult = codec.fromNormalizedNodeOperationData(rpcOutput, (ContainerNode) domData);
+ } else {
+ bindingResult = null;
+ }
+ return RpcResult.class.cast(RpcResultBuilder.success(bindingResult).build());
+ });
+ }
+
+ }
+
+ private final class NonRoutedStrategy extends RpcInvocationStrategy {
+
+ protected NonRoutedStrategy(final SchemaPath path) {
+ super(path);
+ }
+
+ @Override
+ NormalizedNode<?, ?> serialize(final TreeNode input) {
+ return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());
+ }
+
+ }
+
+ private final class RoutedStrategy extends RpcInvocationStrategy {
+
+ private final ContextReferenceExtractor refExtractor;
+ private final NodeIdentifier contextName;
+
+ protected RoutedStrategy(final SchemaPath path, final Method rpcMethod, final QName leafName) {
+ super(path);
+ final Optional<Class<? extends Instantiable<?>>> maybeInputType =
+ BindingReflections.resolveOperationInputClass(rpcMethod);
+ Preconditions.checkState(maybeInputType.isPresent(), "RPC method %s has no input", rpcMethod.getName());
+ final Class<? extends Instantiable<?>> inputType = maybeInputType.get();
+ refExtractor = ContextReferenceExtractor.from(inputType);
+ this.contextName = new NodeIdentifier(leafName);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ NormalizedNode<?, ?> serialize(final TreeNode input) {
+ final InstanceIdentifier<?> bindingII = refExtractor.extract(input);
+ if (bindingII != null) {
+ final YangInstanceIdentifier yangII = codec.toYangInstanceIdentifierCached(bindingII);
+ final LeafNode contextRef = ImmutableNodes.leafNode(contextName, yangII);
+ return LazySerializedContainerNode.withContextRef(getRpcName(), input, contextRef,
+ codec.getCodecRegistry());
+ }
+ return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());
+ }
+
+ }
+}
\ No newline at end of file
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
-import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
@Override
public final <I extends Operation> Future<RpcResult<?>> invoke(@Nonnull final I impl,
- @Nonnull final QName operationName, @Nullable final Instantiable<?> input) {
+ @Nonnull final QName operationName, @Nullable final TreeNode input) {
Preconditions.checkNotNull(impl, "Implementation must be supplied");
import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
import org.opendaylight.yangtools.yang.common.RpcResult;
@Beta
private static final Lookup LOOKUP = MethodHandles.publicLookup();
- protected abstract <T extends Operation> Future<RpcResult<?>> invokeOn(T impl, Instantiable<?> input);
+ protected abstract <T extends Operation> Future<RpcResult<?>> invokeOn(T impl, TreeNode input);
protected static OperationMethodInvoker from(final Method method) {
final Optional<Class<? extends Instantiable<?>>> input = BindingReflections.resolveOperationInputClass(method);
import org.opendaylight.mdsal.binding.javav2.spec.base.ListAction;
import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
import org.opendaylight.yangtools.yang.common.RpcResult;
@Beta
}
@Override
- public <T extends Operation> Future<RpcResult<?>> invokeOn(final T impl, final Instantiable<?> input) {
+ public <T extends Operation> Future<RpcResult<?>> invokeOn(final T impl, final TreeNode input) {
return invoking(handle, impl, input);
}
}
import java.lang.invoke.MethodType;
import java.util.concurrent.Future;
import org.opendaylight.mdsal.binding.javav2.spec.base.Action;
-import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
import org.opendaylight.mdsal.binding.javav2.spec.base.ListAction;
import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
import org.opendaylight.yangtools.yang.common.RpcResult;
@Beta
}
@Override
- public <T extends Operation> Future<RpcResult<?>> invokeOn(final T impl, final Instantiable<?> input) {
+ public <T extends Operation> Future<RpcResult<?>> invokeOn(final T impl, final TreeNode input) {
return invoking(handle, impl);
}
}
import java.util.concurrent.Future;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
import org.opendaylight.mdsal.binding.javav2.spec.base.Operation;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.common.RpcResult;
*
* <p>
* Operation Service invoker provides common invocation interface for any subtype of operation. via
- * {@link #invoke(Operation, QName, Instantiable)} method.
+ * {@link #invoke(Operation, QName, TreeNode)} method.
*/
@Beta
public abstract class OperationServiceInvoker {
* @return Future which will complete once operation processing is finished.
*/
public abstract <T extends Operation> Future<RpcResult<?>> invoke(@Nonnull T impl, @Nonnull QName operationName,
- @Nullable Instantiable<?> input);
+ @Nullable TreeNode input);
}
import org.opendaylight.mdsal.binding.javav2.api.DataBroker;
import org.opendaylight.mdsal.binding.javav2.api.NotificationPublishService;
import org.opendaylight.mdsal.binding.javav2.api.NotificationService;
+import org.opendaylight.mdsal.binding.javav2.api.RpcActionConsumerRegistry;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.data.BindingDOMDataBrokerAdapter;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.notification.BindingDOMNotificationPublishServiceAdapter;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.notification.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.operation.BindingDOMOperationServiceAdapter;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.AdapterBuilder;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder;
import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder.Factory;
@Beta
public abstract class BindingDOMAdapterLoader extends AdapterLoader<BindingService, DOMService> {
- // TODO add all factory of services
@SuppressWarnings("checkstyle:GenericWhitespace")
private static final Map<Class<?>, BindingDOMAdapterBuilder.Factory<?>> FACTORIES =
ImmutableMap.<Class<?>, BindingDOMAdapterBuilder.Factory<?>> builder()
.put(DataBroker.class, BindingDOMDataBrokerAdapter.BUILDER_FACTORY)
.put(NotificationPublishService.class, BindingDOMNotificationPublishServiceAdapter.BUILDER_FACTORY)
.put(NotificationService.class, BindingDOMNotificationServiceAdapter.BUILDER_FACTORY)
+ .put(RpcActionConsumerRegistry.class, BindingDOMOperationServiceAdapter.BUILDER_FACTORY)
.build();
private final BindingToNormalizedNodeCodec codec;
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.dom.adapter.impl.operation.invoker;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Action;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Rpc;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+
+public class ClassBaseOperationServiceInvokerTest {
+
+ @Test
+ public void rpcTest() {
+ final OperationServiceInvoker serviceInvoker = ClassBasedOperationServiceInvoker.instanceFor(DummyRpc.class);
+ assertNotNull(serviceInvoker);
+ }
+
+ @Test
+ public void actionTest() {
+ final OperationServiceInvoker serviceInvoker = ClassBasedOperationServiceInvoker.instanceFor(DummyAction.class);
+ assertNotNull(serviceInvoker);
+ }
+
+ @SuppressWarnings("rawtypes")
+ private interface DummyRpc extends Rpc, TreeNode {
+ }
+
+ @SuppressWarnings("rawtypes")
+ private interface DummyAction extends Action, TreeNode {
+ }
+}
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
-import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
import org.opendaylight.mdsal.binding.javav2.spec.base.Notification;
import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
* @return NormalizedNode representation of operation data
*/
@Nonnull
- ContainerNode toNormalizedNodeOperationData(@Nonnull Instantiable<?> data);
+ ContainerNode toNormalizedNodeOperationData(@Nonnull TreeNode data);
}
@Nonnull
@SuppressWarnings("unchecked")
@Override
- public ContainerNode toNormalizedNodeOperationData(@Nonnull final Instantiable<?> data) {
+ public ContainerNode toNormalizedNodeOperationData(@Nonnull final TreeNode data) {
final NormalizedNodeResult result = new NormalizedNodeResult();
// We create DOM stream writer which produces normalized nodes
final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
@SuppressWarnings("rawtypes")
- final Class<? extends TreeNode> type = (Class) data.implementedInterface();
+ final Class<? extends TreeNode> type = data.getClass();
final Class<? extends Instantiable<?>> instData = (Class<? extends Instantiable<?>>) data.getClass();
final BindingStreamEventWriter writer = newOperationWriter(instData, domWriter);
try {
- getSerializer(type).serialize((TreeNode) data, writer);
+ getSerializer(type).serialize(data, writer);
} catch (final IOException e) {
LOG.error("Unexpected failure while serializing data {}", data, e);
throw new IllegalStateException("Failed to create normalized node", e);
import org.opendaylight.mdsal.binding.javav2.runtime.context.BindingRuntimeContext;
import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
-import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
import org.opendaylight.mdsal.binding.javav2.spec.base.Notification;
import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
@Nonnull
@Override
- public ContainerNode toNormalizedNodeOperationData(@Nonnull final Instantiable<?> data) {
+ public ContainerNode toNormalizedNodeOperationData(@Nonnull final TreeNode data) {
return codecRegistry.toNormalizedNodeOperationData(data);
}
import java.util.Map;
import javax.annotation.Nonnull;
import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
-import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
public class LazySerializedContainerNode implements ContainerNode {
private final NodeIdentifier identifier;
- private final Instantiable<?> bindingData;
+ private final TreeNode bindingData;
private BindingNormalizedNodeCodecRegistry registry;
private ContainerNode domData;
- private LazySerializedContainerNode(final QName identifier, final Instantiable<?> binding,
+ private LazySerializedContainerNode(final QName identifier, final TreeNode binding,
final BindingNormalizedNodeCodecRegistry registry) {
this.identifier = NodeIdentifier.create(identifier);
this.bindingData = binding;
* - specifc codec for operation
* @return instance of lazy serialized container node
*/
- public static NormalizedNode<?, ?> create(final SchemaPath operationName, final Instantiable<?> data,
+ public static NormalizedNode<?, ?> create(final SchemaPath operationName, final TreeNode data,
final BindingNormalizedNodeCodecRegistry codec) {
return new LazySerializedContainerNode(operationName.getLastComponent(), data, codec);
}
* - specific codec
* @return insntance of lazy serialized container node with pre-cached serialized leaf
*/
- public static NormalizedNode<?, ?> withContextRef(final SchemaPath operationName, final Instantiable<?> data,
+ public static NormalizedNode<?, ?> withContextRef(final SchemaPath operationName, final TreeNode data,
final LeafNode<?> contextRef, final BindingNormalizedNodeCodecRegistry codec) {
return new WithContextRef(operationName.getLastComponent(), data, contextRef, codec);
}
*
* @return binding data.
*/
- public final Instantiable<?> bindingData() {
+ public final TreeNode bindingData() {
return bindingData;
}
private final LeafNode<?> contextRef;
- private WithContextRef(final QName identifier, final Instantiable<?> binding, final LeafNode<?> contextRef,
+ private WithContextRef(final QName identifier, final TreeNode binding, final LeafNode<?> contextRef,
final BindingNormalizedNodeCodecRegistry registry) {
super(identifier, binding, registry);
this.contextRef = contextRef;