
com.yahoo.messagebus.network.rpc.RPCTarget Maven / Gradle / Ivy
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.messagebus.network.rpc;
import com.yahoo.component.Version;
import com.yahoo.jrt.*;
import com.yahoo.log.LogLevel;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
/**
* Implements a target object that encapsulates the JRT connection
* target. Instances of this class are returned by {@link RPCService}, and
* cached by {@link RPCTargetPool}.
*
* @author Simon Thoresen Hult
*/
public class RPCTarget implements RequestWaiter {
private static final Logger log = Logger.getLogger(RPCTarget.class.getName());
private final AtomicInteger ref = new AtomicInteger(1);
private final String name;
private final Target target;
private boolean targetInvoked = false;
private Version version = null;
private List versionHandlers = new LinkedList<>();
/**
* Constructs a new instance of this class.
*
* @param spec The connection spec of this target.
* @param orb The jrt supervisor to use when connecting to target.
*/
public RPCTarget(Spec spec, Supervisor orb) {
this.name = spec.toString();
this.target = orb.connect(spec);
}
/**
* Returns the encapsulated JRT target.
*
* @return The target.
*/
public Target getJRTTarget() {
return target;
}
/**
* This method is used for explicit reference counting targets to allow
* reusing open connections. An instance of this class is constructed with a
* single reference.
*
* @see #subRef()
*/
public void addRef() {
ref.incrementAndGet();
}
/**
* This method is used for explicit reference counting targets to allow
* reusing open connections. When the reference count reaches 0, the
* connection is closed.
*
* @see #addRef()
*/
public void subRef() {
if (ref.decrementAndGet() == 0) {
target.close();
}
}
/**
* Returns the current reference count of this target. If this ever
* returns 0 it means the underlying connection is closed and invalid.
*
* @return The number of references in use.
*/
public int getRefCount() {
return ref.get();
}
/**
* Requests the version of this target be passed to the given {@link
* VersionHandler}. If the version is available, the handler is called
* synchronously; if not, the handler is called by the network thread once
* the target responds to the version query.
*
* @param timeout The timeout for the request in seconds.
* @param handler The handler to be called once the version is available.
*/
public void resolveVersion(double timeout, VersionHandler handler) {
boolean hasVersion = false;
boolean shouldInvoke = false;
boolean shouldLog = log.isLoggable(LogLevel.DEBUG);
synchronized (this) {
if (version != null) {
if (shouldLog) {
log.log(LogLevel.DEBUG, "Version already available for target '" + name + "' (version " + version + ").");
}
hasVersion = true;
} else {
if (shouldLog) {
log.log(LogLevel.DEBUG, "Registering version handler '" + handler + "' for target '" + name + "'.");
}
versionHandlers.add(handler);
if (!targetInvoked) {
targetInvoked = true;
shouldInvoke = true;
}
}
}
if (hasVersion) {
handler.handleVersion(version);
} else if (shouldInvoke) {
if (shouldLog) {
log.log(LogLevel.DEBUG, "Invoking mbus.getVersion() on target '" + name + "'");
}
Request req = new Request("mbus.getVersion");
target.invokeAsync(req, timeout, this);
}
}
@Override
public void handleRequestDone(Request req) {
List handlers;
boolean shouldLog = log.isLoggable(LogLevel.DEBUG);
synchronized (this) {
targetInvoked = false;
if (req.checkReturnTypes("s")) {
String str = req.returnValues().get(0).asString();
try {
version = new Version(str);
if (shouldLog) {
log.log(LogLevel.DEBUG, "Target '" + name + "' has version " + version + ".");
}
} catch (IllegalArgumentException e) {
log.log(LogLevel.WARNING, "Failed to parse '" + str + "' as version for target '" + name + "'.", e);
}
} else {
log.log(LogLevel.INFO, "Method mbus.getVersion() failed for target '" + name + "'; " +
req.errorMessage());
}
handlers = versionHandlers;
versionHandlers = new LinkedList<>();
}
for (VersionHandler handler : handlers) {
handler.handleVersion(version);
}
}
/**
* Declares a version handler used when resolving the version of a
* target. An instance of this is passed to {@link
* RPCTarget#resolveVersion(double,
* com.yahoo.messagebus.network.rpc.RPCTarget.VersionHandler)}, and invoked
* either synchronously or asynchronously, depending on whether or not the
* version is already available.
*/
public interface VersionHandler {
/**
* This method is invoked once the version of the corresponding
* {@link RPCTarget} becomes available. If a problem occured while
* retrieving the version, this method is invoked with a null
* argument.
*
* @param ver The version of corresponding target, or null.
*/
public void handleVersion(Version ver);
}
}