All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.controller.remote.rpc.OpsInvoker Maven / Gradle / Ivy

There is a newer version: 10.0.4
Show newest version
/*
 * Copyright (c) 2014 Cisco Systems, Inc. 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.controller.remote.rpc;

import static java.util.Objects.requireNonNull;

import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.Status.Failure;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Collection;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor;
import org.opendaylight.controller.remote.rpc.messages.ActionResponse;
import org.opendaylight.controller.remote.rpc.messages.ExecuteAction;
import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc;
import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
import org.opendaylight.mdsal.dom.api.DOMActionService;
import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.mdsal.dom.api.DOMRpcResult;
import org.opendaylight.mdsal.dom.api.DOMRpcService;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
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.stmt.SchemaNodeIdentifier.Absolute;

/**
 * Actor receiving invocation requests from remote nodes, routing them to
 * {@link DOMRpcService#invokeRpc(SchemaPath, NormalizedNode)} and
 * {@link DOMActionService#invokeAction(SchemaPath, DOMDataTreeIdentifier, ContainerNode)}.
 *
 * 

* Note that while the two interfaces are very similar, invocation strategies are slightly different due to historic * behavior of RPCs: *

    *
  • RPCs allow both null input and output, and this is passed to the infrastructure. Furthermore any invocation * which results in errors being reported drops the output content, even if it is present -- which is wrong, as * 'errors' in this case can also be just warnings.
  • *
  • Actions do not allow null input, but allow null output. If the output is present, it is passed along with any * errors reported.
  • *
*/ final class OpsInvoker extends AbstractUntypedActor { private final DOMRpcService rpcService; private final DOMActionService actionService; private OpsInvoker(final DOMRpcService rpcService, final DOMActionService actionService) { this.rpcService = requireNonNull(rpcService); this.actionService = requireNonNull(actionService); } public static Props props(final DOMRpcService rpcService, final DOMActionService actionService) { return Props.create(OpsInvoker.class, requireNonNull(rpcService, "DOMRpcService can not be null"), requireNonNull(actionService, "DOMActionService can not be null")); } @Override protected void handleReceive(final Object message) { if (message instanceof ExecuteRpc executeRpc) { LOG.debug("Handling ExecuteOps Message"); execute(executeRpc); } else if (message instanceof ExecuteAction executeAction) { execute(executeAction); } else { unknownMessage(message); } } @SuppressWarnings("checkstyle:IllegalCatch") private void execute(final ExecuteRpc msg) { LOG.debug("Executing RPC {}", msg.getType()); final ActorRef sender = getSender(); final ListenableFuture future; try { future = rpcService.invokeRpc(msg.getType(), msg.getInput()); } catch (final RuntimeException e) { LOG.debug("Failed to invoke RPC {}", msg.getType(), e); sender.tell(new Failure(e), self()); return; } Futures.addCallback(future, new AbstractCallback(getSender(), msg.getType()) { @Override Object nullResponse(final QName type) { LOG.warn("Execution of {} resulted in null result", type); return new RpcResponse(null); } @Override Object response(final QName type, final DOMRpcResult result) { final Collection errors = result.errors(); return errors.isEmpty() ? new RpcResponse(result.value()) // This is legacy (wrong) behavior, which ignores the fact that errors may be just warnings, // discarding any output : new Failure(new RpcErrorsException(String.format("Execution of rpc %s failed", type), errors)); } }, MoreExecutors.directExecutor()); } @SuppressWarnings("checkstyle:IllegalCatch") private void execute(final ExecuteAction msg) { LOG.debug("Executing Action {}", msg.getType()); final ActorRef sender = getSender(); final ListenableFuture future; try { future = actionService.invokeAction(msg.getType(), msg.getPath(), msg.getInput()); } catch (final RuntimeException e) { LOG.debug("Failed to invoke action {}", msg.getType(), e); sender.tell(new Failure(e), self()); return; } Futures.addCallback(future, new AbstractCallback(getSender(), msg.getType()) { @Override Object nullResponse(final Absolute type) { throw new IllegalStateException("Null invocation result of action " + type); } @Override Object response(final Absolute type, final DOMRpcResult result) { final var errors = result.errors(); return errors.isEmpty() ? new ActionResponse(result.value(), errors) // This is legacy (wrong) behavior, which ignores the fact that errors may be just warnings, // discarding any output : new Failure(new RpcErrorsException(String.format("Execution of action %s failed", type), errors)); } }, MoreExecutors.directExecutor()); } private abstract class AbstractCallback implements FutureCallback { private final ActorRef replyTo; private final T type; AbstractCallback(final ActorRef replyTo, final T type) { this.replyTo = requireNonNull(replyTo); this.type = requireNonNull(type); } @Override public final void onSuccess(final R result) { final Object response; if (result == null) { // This shouldn't happen but the FutureCallback annotates the result param with Nullable so handle null // here to avoid FindBugs warning. response = nullResponse(type); } else { response = response(type, result); } LOG.debug("Sending response for execution of {} : {}", type, response); replyTo.tell(response, self()); } @Override public final void onFailure(final Throwable failure) { LOG.debug("Failed to execute operation {}", type, failure); LOG.error("Failed to execute operation {} due to {}. More details are available on DEBUG level.", type, Throwables.getRootCause(failure).getMessage()); replyTo.tell(new Failure(failure), self()); } abstract @NonNull Object nullResponse(@NonNull T type); abstract @NonNull Object response(@NonNull T type, @NonNull R result); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy