/*
* dCache - http://www.dcache.org/
*
* Copyright (C) 2016 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.poolmanager;
import com.google.common.collect.Ordering;
import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Iterator;
import java.util.List;
import diskCacheV111.util.PnfsId;
import diskCacheV111.vehicles.Message;
import diskCacheV111.vehicles.PoolIoFileMessage;
import diskCacheV111.vehicles.PoolManagerMessage;
import diskCacheV111.vehicles.PoolMgrGetPoolMsg;
import dmg.cells.nucleus.CellAddressCore;
import dmg.cells.nucleus.CellEndpoint;
import dmg.cells.nucleus.CellMessage;
import dmg.cells.nucleus.CellPath;
import org.dcache.cells.FutureCellMessageAnswerable;
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.util.Objects.requireNonNull;
/**
*/
public class RendezvousPoolManagerHandler implements SerializablePoolManagerHandler
{
private static final long serialVersionUID = 8055227719635104693L;
private final List<CellAddressCore> backends;
private final CellAddressCore serviceAddress;
public RendezvousPoolManagerHandler(CellAddressCore serviceAddress, List<CellAddressCore> backends)
{
checkArgument(!backends.isEmpty());
checkArgument(Ordering.natural().isOrdered(backends));
this.serviceAddress = serviceAddress;
this.backends = backends;
}
public CellAddressCore backendFor(PnfsId pnfsId)
{
Iterator<CellAddressCore> iterator = backends.iterator();
CellAddressCore address = iterator.next();
int min = hash(pnfsId, address);
while (iterator.hasNext()) {
CellAddressCore nextAddress = iterator.next();
int nextHash = hash(pnfsId, nextAddress);
if (nextHash < min) {
min = nextHash;
address = nextAddress;
}
}
return address;
}
public CellAddressCore backendFor(PoolManagerMessage msg)
{
if (msg instanceof PoolMgrGetPoolMsg) {
return backendFor(((PoolMgrGetPoolMsg) msg).getFileAttributes().getPnfsId());
}
return serviceAddress;
}
public CellAddressCore backendFor(PoolIoFileMessage msg)
{
return backendFor(msg.getFileAttributes().getPnfsId());
}
private static int hash(PnfsId pnfsId, CellAddressCore address)
{
return Hashing.murmur3_32().newHasher()
.putObject(pnfsId, PnfsId.funnel())
.putString(address.toString(), US_ASCII)
.hash().asInt();
}
@Override
public <T extends PoolManagerMessage> ListenableFuture<T> sendAsync(CellEndpoint endpoint, T msg, long timeout)
{
return submit(endpoint, new CellPath(backendFor(msg)), msg, timeout);
}
@Override
public void send(CellEndpoint endpoint, CellMessage envelope, PoolManagerMessage msg)
{
checkArgument(envelope.getSourcePath().hops() > 0, "Envelope is missing source address.");
envelope.getDestinationPath().insert(backendFor(msg));
envelope.setMessageObject(msg);
endpoint.sendMessage(envelope, CellEndpoint.SendFlag.PASS_THROUGH);
}
@Override
public <T extends PoolIoFileMessage> ListenableFuture<T> startAsync(
CellEndpoint endpoint, CellAddressCore pool, T msg, long timeout)
{
return submit(endpoint, new CellPath(pool), msg, timeout);
}
@Override
public void start(CellEndpoint endpoint, CellMessage envelope, PoolIoFileMessage msg)
{
checkArgument(envelope.getSourcePath().hops() > 0, "Envelope is missing source address.");
envelope.setMessageObject(msg);
endpoint.sendMessage(envelope, CellEndpoint.SendFlag.PASS_THROUGH);
}
@Override
public SerializablePoolManagerHandler.Version getVersion()
{
return new Version(serviceAddress, backends);
}
@SuppressWarnings("unchecked")
protected <T extends Message> ListenableFuture<T> submit(CellEndpoint endpoint, CellPath path, T msg, long timeout)
{
FutureCellMessageAnswerable<T> callback = new FutureCellMessageAnswerable<>((Class<T>) msg.getClass());
endpoint.sendMessage(new CellMessage(path, msg), callback, MoreExecutors.directExecutor(), timeout);
return callback;
}
@Override
public String toString()
{
return "rendezvous{service=" + serviceAddress + ", backends=" + backends + "}";
}
protected static class Version implements SerializablePoolManagerHandler.Version
{
private static final long serialVersionUID = 7093718261159908650L;
private final CellAddressCore serviceAddress;
private final List<CellAddressCore> backends;
public Version(CellAddressCore serviceAddress, List<CellAddressCore> backends)
{
this.serviceAddress = requireNonNull(serviceAddress);
this.backends = requireNonNull(backends);
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Version version = (Version) o;
return serviceAddress.equals(version.serviceAddress) && backends.equals(version.backends);
}
@Override
public int hashCode()
{
return 31 * serviceAddress.hashCode() + backends.hashCode();
}
}
}