/* dCache - http://www.dcache.org/ * * Copyright (C) 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.pool.classic; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import java.io.SyncFailedException; import java.lang.reflect.InvocationTargetException; import java.nio.channels.CompletionHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import diskCacheV111.util.CacheException; import diskCacheV111.vehicles.PoolIoFileMessage; import diskCacheV111.vehicles.ProtocolInfo; import dmg.cells.nucleus.AbstractCellComponent; import dmg.cells.nucleus.CellInfoProvider; import dmg.cells.nucleus.CellPath; import org.dcache.pool.movers.Mover; import org.dcache.pool.movers.MoverFactory; import org.dcache.pool.movers.MoverProtocol; import org.dcache.pool.movers.MoverProtocolMover; import org.dcache.pool.repository.ReplicaDescriptor; import org.dcache.pool.repository.RepositoryChannel; import org.dcache.util.CDCExecutorServiceDecorator; public abstract class AbstractMoverProtocolTransferService extends AbstractCellComponent implements TransferService<MoverProtocolMover>,MoverFactory, CellInfoProvider { private static final Logger LOGGER = LoggerFactory.getLogger(MoverMapTransferService.class); private final ExecutorService _executor = new CDCExecutorServiceDecorator<>( Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNameFormat(getClass().getSimpleName() + "-transfer-service-%d").build())); private ChecksumModule _checksumModule; private PostTransferService _postTransferService; @Required public void setChecksumModule(ChecksumModule checksumModule) { _checksumModule = checksumModule; } @Required public void setPostTransferService(PostTransferService postTransferService) { _postTransferService = postTransferService; } @Override public Mover<?> createMover(ReplicaDescriptor handle, PoolIoFileMessage message, CellPath pathToDoor) throws CacheException { ProtocolInfo info = message.getProtocolInfo(); try { MoverProtocol moverProtocol = createMoverProtocol(info); return new MoverProtocolMover(handle, message, pathToDoor, this, moverProtocol, _checksumModule); } catch (InvocationTargetException e) { throw new CacheException(27, "Could not create mover for " + info, e.getTargetException()); } catch (ClassNotFoundException e) { throw new CacheException(27, "Protocol " + info + " is not supported", e); } catch (Exception e) { LOGGER.error("Invalid mover for " + info + ": " + e.toString(), e); throw new CacheException(27, "Could not create mover for " + info, e); } } protected abstract MoverProtocol createMoverProtocol(ProtocolInfo info) throws Exception; @Override public Cancellable executeMover(MoverProtocolMover mover, CompletionHandler<Void, Void> completionHandler) { MoverTask task = new MoverTask(mover, completionHandler); _executor.execute(task); return task; } @Override public void closeMover(MoverProtocolMover mover, CompletionHandler<Void, Void> completionHandler) { _postTransferService.execute(mover, completionHandler); } public void shutdown() { _executor.shutdown(); } private class MoverTask implements Runnable, Cancellable { private final MoverProtocolMover _mover; private final CompletionHandler<Void,Void> _completionHandler; private Thread _thread; private boolean _needInterruption; private String _explanation; public MoverTask(MoverProtocolMover mover, CompletionHandler<Void,Void> completionHandler) { _mover = mover; _completionHandler = completionHandler; } @Override public void run() { try { setThread(); try (RepositoryChannel fileIoChannel = _mover.openChannel()) { switch (_mover.getIoMode()) { case WRITE: try { runMover(fileIoChannel); } finally { try { fileIoChannel.sync(); } catch (SyncFailedException e) { fileIoChannel.sync(); LOGGER.info("First sync failed [" + e + "], but second sync suceeded"); } } break; case READ: runMover(fileIoChannel); break; } } catch (Throwable t) { _completionHandler.failed(t, null); return; } _completionHandler.completed(null, null); } catch (InterruptedException e) { InterruptedException why = _explanation == null ? e : new InterruptedException(_explanation); _completionHandler.failed(why, null); } finally { cleanThread(); } } private void runMover(RepositoryChannel fileIoChannel) throws Exception { _mover.getMover().runIO(_mover.getFileAttributes(), fileIoChannel, _mover.getProtocolInfo(), _mover.getIoHandle(), _mover.getIoMode()); } private synchronized void setThread() throws InterruptedException { if (_needInterruption) { throw new InterruptedException("Thread interrupted before execution"); } _thread = Thread.currentThread(); } private synchronized void cleanThread() { _thread = null; } @Override public synchronized void cancel(String explanation) { _explanation = explanation; if (_thread != null) { _thread.interrupt(); } else { _needInterruption = true; } } } }