/* dCache - http://www.dcache.org/
*
* Copyright (C) 2013 - 2015 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.chimera.nfsv41.mover;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.channels.CompletionHandler;
import diskCacheV111.util.CacheException;
import diskCacheV111.util.DiskErrorCacheException;
import diskCacheV111.util.PnfsHandler;
import diskCacheV111.vehicles.PoolIoFileMessage;
import dmg.cells.nucleus.CellPath;
import org.dcache.nfs.v4.NFS4State;
import org.dcache.nfs.v4.NFSv41Session;
import org.dcache.nfs.v4.xdr.state_owner4;
import org.dcache.nfs.v4.xdr.stateid4;
import org.dcache.nfs.v4.xdr.verifier4;
import org.dcache.pool.classic.Cancellable;
import org.dcache.pool.classic.ChecksumModule;
import org.dcache.pool.movers.MoverChannel;
import org.dcache.pool.movers.MoverChannelMover;
import org.dcache.pool.repository.ReplicaDescriptor;
import org.dcache.vehicles.FileAttributes;
public class NfsMover extends MoverChannelMover<NFS4ProtocolInfo, NfsMover> {
private static final Logger _log = LoggerFactory.getLogger(NfsTransferService.class);
private NFSv41Session _session;
private final NFSv4MoverHandler _nfsIO;
private final NFS4State _state;
private final PnfsHandler _namespace;
private volatile CompletionHandler<Void, Void> _completionHandler;
private final verifier4 _bootVerifier;
public NfsMover(ReplicaDescriptor handle, PoolIoFileMessage message, CellPath pathToDoor,
NfsTransferService nfsTransferService, PnfsHandler pnfsHandler, ChecksumModule checksumModule) {
super(handle, message, pathToDoor, nfsTransferService, MoverChannel.AllocatorMode.SOFT, checksumModule);
_nfsIO = nfsTransferService.getNfsMoverHandler();
org.dcache.chimera.nfs.v4.xdr.stateid4 legacyStateid = getProtocolInfo().stateId();
_state = new MoverState(null, new stateid4(legacyStateid.other, legacyStateid.seqid.value));
_namespace = pnfsHandler;
_bootVerifier = nfsTransferService.getBootVerifier();
}
public stateid4 getStateId() {
return _state.stateid();
}
public byte[] getNfsFilehandle() {
return getProtocolInfo().getNfsFileHandle();
}
@Override
protected String getStatus() {
StringBuilder s = new StringBuilder();
s.append("NFSv4.1/pNFS,OS=")
.append(getStateId())
.append(",cl=[")
.append(getProtocolInfo().getSocketAddress().getAddress().getHostAddress())
.append("]");
return s.toString();
}
/**
* Enable access with this mover.
* @param completionHandler to be called when mover finishes.
* @return handle to cancel mover if needed
* @throws DiskErrorCacheException
*/
public Cancellable enable(final CompletionHandler<Void,Void> completionHandler) throws DiskErrorCacheException {
open();
_completionHandler = completionHandler;
_nfsIO.add(this);
return (e) -> disable(null);
}
/**
* Disable access with this mover. If {@code error} is not a {@code null},
* the {@link CompletionHandler#failed(Throwable, A)} method will be called.
* @param error error to report, or {@code null} on success
*/
void disable(Throwable error) {
_nfsIO.remove(NfsMover.this);
detachSession();
try {
getMoverChannel().close();
} catch (IOException e) {
_log.error("failed to close RAF {}", e.toString());
}
if(error == null) {
_completionHandler.completed(null, null);
} else {
_completionHandler.failed(error, null);
}
}
/**
* Attach mover tho the client's NFSv41 session.
* @param session to attach to
*/
synchronized void attachSession(NFSv41Session session) {
if (_session == null) {
_session = session;
_session.getClient().attachState(_state);
}
}
/**
* Detach mover from the client's session.
*/
synchronized void detachSession() {
if (_session != null) {
_session.getClient().detachState(_state);
_session = null;
}
}
/**
* A special {@link NFS4State} to kill the mover when disposed.
*/
private class MoverState extends NFS4State {
MoverState(state_owner4 owner, stateid4 stateid) {
super(owner, stateid);
}
@Override
protected void dispose() {
detachSession();
}
}
public void commitFileSize(long size) throws CacheException {
_namespace.setFileAttributes(getFileAttributes().getPnfsId(),
FileAttributes.ofSize(size));
}
public verifier4 getBootVerifier() {
return _bootVerifier;
}
public synchronized boolean hasSession() {
return (_session != null);
}
}