package org.dcache.srm.handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessException; import java.net.URI; import java.util.concurrent.Semaphore; import org.dcache.srm.AbstractStorageElement; import org.dcache.srm.RemoveFileCallback; import org.dcache.srm.SRM; import org.dcache.srm.SRMException; import org.dcache.srm.SRMInternalErrorException; import org.dcache.srm.SRMInvalidRequestException; import org.dcache.srm.SRMUser; import org.dcache.srm.request.GetFileRequest; import org.dcache.srm.request.PutFileRequest; import org.dcache.srm.scheduler.IllegalStateTransition; import org.dcache.srm.util.JDC; import org.dcache.srm.v2_2.ArrayOfTSURLReturnStatus; import org.dcache.srm.v2_2.SrmRmRequest; import org.dcache.srm.v2_2.SrmRmResponse; import org.dcache.srm.v2_2.TReturnStatus; import org.dcache.srm.v2_2.TSURLReturnStatus; import org.dcache.srm.v2_2.TStatusCode; import static com.google.common.base.Preconditions.checkNotNull; public class SrmRm { private static final Logger LOGGER = LoggerFactory.getLogger(SrmRm.class); private final AbstractStorageElement storage; private final SrmRmRequest request; private final SRMUser user; private final int sizeOfSingleRemoveBatch; private final SRM srm; private SrmRmResponse response; public SrmRm(SRMUser user, SrmRmRequest request, AbstractStorageElement storage, SRM srm, String clientHost) { this.srm = srm; this.request = checkNotNull(request); this.user = checkNotNull(user); this.storage = checkNotNull(storage); this.sizeOfSingleRemoveBatch = srm.getConfiguration().getSizeOfSingleRemoveBatch(); } public SrmRmResponse getResponse() { if (response == null) { try { response = srmRm(); } catch (DataAccessException e) { LOGGER.error(e.toString()); response = getResponse("Internal database failure", TStatusCode.SRM_INTERNAL_ERROR); } catch (InterruptedException e) { response = getResponse("Operation interrupted", TStatusCode.SRM_INTERNAL_ERROR); } catch (SRMInternalErrorException e) { response = getResponse(e.getMessage(), TStatusCode.SRM_INTERNAL_ERROR); } catch (SRMInvalidRequestException e) { response = getResponse(e.getMessage(), TStatusCode.SRM_INVALID_REQUEST); } } return response; } private SrmRmResponse srmRm() throws DataAccessException, InterruptedException, SRMInternalErrorException, SRMInvalidRequestException { if (request.getArrayOfSURLs() == null) { throw new SRMInvalidRequestException("arrayOfSURLs is empty"); } org.apache.axis.types.URI[] surls = request.getArrayOfSURLs().getUrlArray(); if (surls == null || surls.length == 0) { throw new SRMInvalidRequestException("arrayOfSURLs is empty"); } TSURLReturnStatus[] returnStatuses = new TSURLReturnStatus[surls.length]; Semaphore semaphore = new Semaphore(sizeOfSingleRemoveBatch); for (int i = 0; i < surls.length; i++) { semaphore.acquire(); returnStatuses[i] = new TSURLReturnStatus(surls[i], null); URI surl = URI.create(surls[i].toString()); storage.removeFile(user, surl, new Callback(semaphore, returnStatuses[i])); } semaphore.acquire(sizeOfSingleRemoveBatch); for (int i = 0; i < surls.length; i++) { TSURLReturnStatus returnStatus = returnStatuses[i]; if (returnStatus.getStatus().getStatusCode() == TStatusCode.SRM_INTERNAL_ERROR) { throw new SRMInternalErrorException(returnStatus.getStatus().getExplanation()); } if (returnStatus.getStatus().getStatusCode() == TStatusCode.SRM_SUCCESS || returnStatus.getStatus().getStatusCode() == TStatusCode.SRM_INVALID_PATH) { // [SRM 2.2, 4.3.2, e)] srmRm aborts the SURLs from srmPrepareToPut requests not yet // in SRM_PUT_DONE state, and must set its file status as SRM_ABORTED. // // [SRM 2.2, 4.3.2, f)] srmRm must remove SURLs even if the statuses of the SURLs // are SRM_FILE_BUSY. In this case, operations such as srmPrepareToPut or srmCopy // that holds the SURL status as SRM_FILE_BUSY must return SRM_INVALID_PATH upon // status request or srmPutDone. // // It seems the SRM specs is undecided about whether to move put requests to // SRM_ABORTED or SRM_INVALID_PATH. We choose SRM_ABORTED as it seems like the saner // of the two options. // [SRM 2.2, 4.3.2, d)] srmLs,srmPrepareToGet or srmBringOnline must not find these // removed files any more. It must set file requests on SURL from srmPrepareToGet // as SRM_ABORTED. URI surl = URI.create(surls[i].toString()); try { if (srm.abortTransfers(surl, "File was deleted by request " + JDC.getSession() + ".")) { returnStatus.setStatus(new TReturnStatus(TStatusCode.SRM_SUCCESS, "Upload was aborted.")); } } catch (SRMException e) { returnStatus.setStatus(new TReturnStatus(e.getStatusCode(), e.getMessage())); } } } return new SrmRmResponse( ReturnStatuses.getSummaryReturnStatus(returnStatuses), new ArrayOfTSURLReturnStatus(returnStatuses)); } private static class Callback implements RemoveFileCallback { private final Semaphore semaphore; private final TSURLReturnStatus returnStatus; public Callback(Semaphore semaphore, TSURLReturnStatus returnStatus) { this.semaphore = semaphore; this.returnStatus = returnStatus; } @Override public void failure(String reason) { returnStatus.setStatus(new TReturnStatus( TStatusCode.SRM_FAILURE, reason)); LOGGER.error("RemoveFileFailed: {}", reason); done(); } @Override public void notFound(String reason) { returnStatus.setStatus(new TReturnStatus(TStatusCode.SRM_INVALID_PATH, reason)); done(); } @Override public void success() { returnStatus.setStatus(new TReturnStatus(TStatusCode.SRM_SUCCESS, null)); done(); } @Override public void timeout() { returnStatus.setStatus(new TReturnStatus(TStatusCode.SRM_INTERNAL_ERROR, "Internal timeout")); done(); } @Override public void permissionDenied() { returnStatus.setStatus(new TReturnStatus(TStatusCode.SRM_AUTHORIZATION_FAILURE, "Permission denied")); done(); } private void done() { semaphore.release(); } } public static final SrmRmResponse getFailedResponse(String error) { return getResponse(error, TStatusCode.SRM_FAILURE); } public static final SrmRmResponse getResponse(String error, TStatusCode statusCode) { SrmRmResponse response = new SrmRmResponse(); response.setReturnStatus(new TReturnStatus(statusCode, error)); return response; } }