/* dCache - http://www.dcache.org/ * * Copyright (C) 2013 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.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.Uninterruptibles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.channels.CompletionHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import diskCacheV111.util.CacheException; import diskCacheV111.vehicles.DoorTransferFinishedMessage; import diskCacheV111.vehicles.MoverInfoMessage; import dmg.cells.nucleus.AbstractCellComponent; import dmg.cells.nucleus.CellInfoProvider; import org.dcache.cells.CellStub; import org.dcache.pool.movers.IoMode; import org.dcache.pool.movers.Mover; import org.dcache.pool.repository.ReplicaDescriptor; import org.dcache.util.CDCExecutorServiceDecorator; import org.dcache.util.FireAndForgetTask; import org.dcache.vehicles.FileAttributes; public class DefaultPostTransferService extends AbstractCellComponent implements PostTransferService, CellInfoProvider { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPostTransferService.class); private final ExecutorService _executor = new CDCExecutorServiceDecorator<>( Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNameFormat("post-transfer-%d").build())); private CellStub _billing; private String _poolName; private ChecksumModule _checksumModule; private CellStub _door; @Required public void setBillingStub(CellStub billing) { _billing = billing; } @Required public void setPoolName(String poolName) { _poolName = poolName; } @Required public void setChecksumModule(ChecksumModule checksumModule) { _checksumModule = checksumModule; } public void init() { _door = new CellStub(getCellEndpoint()); } @Override public void execute(final Mover<?> mover, final CompletionHandler<Void, Void> completionHandler) { _executor.execute(new FireAndForgetTask(() -> { ReplicaDescriptor handle = mover.getIoHandle(); try { try { if (mover.getIoMode() == IoMode.WRITE) { handle.addChecksums(mover.getExpectedChecksums()); _checksumModule.enforcePostTransferPolicy(handle, mover.getActualChecksums()); } handle.commit(); } finally { handle.close(); } completionHandler.completed(null, null); } catch (InterruptedIOException | InterruptedException e) { LOGGER.warn("Transfer was forcefully killed during post-processing"); mover.setTransferStatus(CacheException.DEFAULT_ERROR_CODE, "Transfer was forcefully killed"); completionHandler.failed(e, null); } catch (CacheException e) { LOGGER.warn("Transfer failed in post-processing: {}", e.getMessage()); mover.setTransferStatus(e.getRc(), "Post-processing failed: " + e.getMessage()); completionHandler.failed(e, null); } catch (IOException e) { LOGGER.warn("Transfer failed in post-processing: {}", e.toString()); mover.setTransferStatus(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, "Transfer failed in post-processing: " + e.getMessage()); completionHandler.failed(e, null); } catch (RuntimeException e) { LOGGER.error( "Transfer failed in post-processing. Please report this bug to support@dcache.org.", e); mover.setTransferStatus(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, "Transfer failed due to unexpected exception: " + e.getMessage()); completionHandler.failed(e, null); } catch (Throwable e) { Thread t = Thread.currentThread(); t.getUncaughtExceptionHandler().uncaughtException(t, e); mover.setTransferStatus(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, "Transfer failed due to unexpected exception: " + e.getMessage()); completionHandler.failed(e, null); } sendBillingMessage(mover, handle.getReplicaSize()); sendFinished(mover); })); } public void sendBillingMessage(Mover<?> mover, long fileSize) { FileAttributes fileAttributes = mover.getFileAttributes(); MoverInfoMessage info = new MoverInfoMessage(getCellAddress(), fileAttributes.getPnfsId()); info.setSubject(mover.getSubject()); info.setInitiator(mover.getInitiator()); info.setFileCreated(mover.getIoMode() == IoMode.WRITE); info.setStorageInfo(fileAttributes.getStorageInfo()); info.setP2P(mover.isPoolToPoolTransfer()); info.setFileSize(fileSize); info.setResult(mover.getErrorCode(), mover.getErrorMessage()); info.setTransferAttributes(mover.getBytesTransferred(), mover.getTransferTime(), mover.getProtocolInfo()); info.setBillingPath(mover.getBillingPath()); info.setTransferPath(mover.getTransferPath()); _billing.notify(info); } public void sendFinished(Mover<?> mover) { DoorTransferFinishedMessage finished = new DoorTransferFinishedMessage(mover.getClientId(), mover.getFileAttributes().getPnfsId(), mover.getProtocolInfo(), mover.getFileAttributes(), _poolName, mover.getQueueName()); if (mover.getErrorCode() == 0) { finished.setSucceeded(); } else { finished.setReply(mover.getErrorCode(), mover.getErrorMessage()); } _door.notify(mover.getPathToDoor(), finished); } public void shutdown() { MoreExecutors.shutdownAndAwaitTermination(_executor, 10, TimeUnit.SECONDS); } }