/*
* 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 diskCacheV111.services.space;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import diskCacheV111.vehicles.Message;
import diskCacheV111.vehicles.PoolAcceptFileMessage;
import diskCacheV111.vehicles.PoolIoFileMessage;
import diskCacheV111.vehicles.PoolManagerMessage;
import diskCacheV111.vehicles.PoolMgrSelectWritePoolMsg;
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 org.dcache.poolmanager.SerializablePoolManagerHandler;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
/**
* PoolManagerHandler published by space manager.
*
* Decides whether to submit requests to space manager or pass them on to a
* PoolManagerHandler obtained from pool manager.
*/
public class SpaceManagerHandler implements SerializablePoolManagerHandler
{
private static final long serialVersionUID = -8954797295015774582L;
protected final CellAddressCore destination;
protected final SerializablePoolManagerHandler inner;
public SpaceManagerHandler(CellAddressCore destination, SerializablePoolManagerHandler inner)
{
this.destination = requireNonNull(destination);
this.inner = inner;
}
@Override
public <T extends PoolManagerMessage> ListenableFuture<T> sendAsync(CellEndpoint endpoint, T msg, long timeout)
{
if (shouldIntercept(msg)) {
return submit(endpoint, new CellPath(destination), msg, timeout);
} else {
return inner.sendAsync(endpoint, msg, timeout);
}
}
@Override
public void send(CellEndpoint endpoint, CellMessage envelope, PoolManagerMessage msg)
{
if (shouldIntercept(msg)) {
checkArgument(envelope.getSourcePath().hops() > 0, "Envelope is missing source address.");
envelope.getDestinationPath().insert(this.destination);
envelope.setMessageObject(msg);
endpoint.sendMessage(envelope, CellEndpoint.SendFlag.PASS_THROUGH);
} else {
inner.send(endpoint, envelope, msg);
}
}
@Override
public <T extends PoolIoFileMessage> ListenableFuture<T> startAsync(
CellEndpoint endpoint, CellAddressCore pool, T msg, long timeout)
{
if (shouldIntercept(msg)) {
return submit(endpoint, new CellPath(destination, pool), msg, timeout);
} else {
return inner.startAsync(endpoint, pool, msg, timeout);
}
}
@Override
public void start(CellEndpoint endpoint, CellMessage envelope, PoolIoFileMessage msg)
{
if (shouldIntercept(msg)) {
checkArgument(envelope.getSourcePath().hops() > 0, "Envelope is missing source address.");
envelope.getDestinationPath().insert(destination);
envelope.setMessageObject(msg);
endpoint.sendMessage(envelope, CellEndpoint.SendFlag.PASS_THROUGH);
} else {
inner.start(endpoint, envelope, msg);
}
}
@Override
public SerializablePoolManagerHandler.Version getVersion()
{
return new Version(destination, inner.getVersion());
}
@Override
public String toString()
{
return "spacemanager={" + destination + ", " + inner + "}";
}
@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;
}
protected <T extends PoolManagerMessage> boolean shouldIntercept(T msg)
{
return msg instanceof PoolMgrSelectWritePoolMsg;
}
protected <T extends PoolIoFileMessage> boolean shouldIntercept(T msg)
{
return (msg instanceof PoolAcceptFileMessage) && msg.getFileAttributes().getStorageInfo().getKey("LinkGroupId") != null;
}
public static SerializablePoolManagerHandler.Version extractWrappedVersion(SerializablePoolManagerHandler.Version version)
{
return (version instanceof Version) ? ((Version) version).version : version;
}
protected static class Version implements SerializablePoolManagerHandler.Version
{
private static final long serialVersionUID = -464748685222944300L;
private final CellAddressCore destination;
private final SerializablePoolManagerHandler.Version version;
public Version(CellAddressCore destination, SerializablePoolManagerHandler.Version version)
{
this.destination = requireNonNull(destination);
this.version = requireNonNull(version);
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Version version1 = (Version) o;
return destination.equals(version1.destination) && version.equals(version1.version);
}
@Override
public int hashCode()
{
return 31 * destination.hashCode() + version.hashCode();
}
}
}