package org.dcache.srm.handler;
import com.google.common.base.Optional;
import org.apache.axis.types.UnsignedLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.Arrays;
import org.dcache.srm.AbstractStorageElement;
import org.dcache.srm.SRM;
import org.dcache.srm.SRMInternalErrorException;
import org.dcache.srm.SRMInvalidRequestException;
import org.dcache.srm.SRMNotSupportedException;
import org.dcache.srm.SRMUser;
import org.dcache.srm.request.PutRequest;
import org.dcache.srm.scheduler.IllegalStateTransition;
import org.dcache.srm.util.Configuration;
import org.dcache.srm.util.JDC;
import org.dcache.srm.util.Lifetimes;
import org.dcache.srm.util.Tools;
import org.dcache.srm.v2_2.ArrayOfTExtraInfo;
import org.dcache.srm.v2_2.SrmPrepareToPutRequest;
import org.dcache.srm.v2_2.SrmPrepareToPutResponse;
import org.dcache.srm.v2_2.TAccessLatency;
import org.dcache.srm.v2_2.TExtraInfo;
import org.dcache.srm.v2_2.TFileStorageType;
import org.dcache.srm.v2_2.TOverwriteMode;
import org.dcache.srm.v2_2.TPutFileRequest;
import org.dcache.srm.v2_2.TRetentionPolicy;
import org.dcache.srm.v2_2.TReturnStatus;
import org.dcache.srm.v2_2.TStatusCode;
import org.dcache.srm.v2_2.TTransferParameters;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.in;
import static com.google.common.collect.Iterables.any;
import static java.util.Arrays.asList;
public class SrmPrepareToPut
{
private static final Logger LOGGER =
LoggerFactory.getLogger(SrmPrepareToPut.class);
private final AbstractStorageElement storage;
private final SrmPrepareToPutRequest request;
private SrmPrepareToPutResponse response;
private final SRMUser user;
private final Configuration configuration;
private final String clientHost;
private final SRM srm;
public SrmPrepareToPut(SRMUser user,
SrmPrepareToPutRequest request,
AbstractStorageElement storage,
SRM srm,
String clientHost)
{
this.request = checkNotNull(request);
this.user = checkNotNull(user);
this.storage = checkNotNull(storage);
this.configuration = checkNotNull(srm.getConfiguration());
this.clientHost = clientHost;
this.srm = checkNotNull(srm);
}
public SrmPrepareToPutResponse getResponse()
{
if (response == null) {
try {
response = srmPrepareToPut();
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
response = getFailedResponse("Operation interrupted.", TStatusCode.SRM_INTERNAL_ERROR);
} catch (IllegalStateTransition e) {
LOGGER.error(e.getMessage());
response = getFailedResponse("Failed to schedule operation.", TStatusCode.SRM_INTERNAL_ERROR);
} catch (SRMInvalidRequestException e) {
response = getFailedResponse(e.getMessage(), TStatusCode.SRM_INVALID_REQUEST);
} catch (SRMInternalErrorException e) {
LOGGER.error(e.getMessage());
response = getFailedResponse(e.getMessage(), TStatusCode.SRM_INTERNAL_ERROR);
} catch (SRMNotSupportedException e) {
response = getFailedResponse(e.getMessage(), TStatusCode.SRM_NOT_SUPPORTED);
}
}
return response;
}
private SrmPrepareToPutResponse srmPrepareToPut()
throws IllegalStateTransition, InterruptedException, SRMNotSupportedException, SRMInvalidRequestException,
SRMInternalErrorException
{
checkFileStorageType(request, TFileStorageType.PERMANENT);
String[] protocols = getProtocols();
String clientHost = getClientHost(request).or(this.clientHost);
String spaceToken = request.getTargetSpaceToken();
TRetentionPolicy retentionPolicy = null;
TAccessLatency accessLatency = null;
if (request.getTargetFileRetentionPolicyInfo() != null) {
retentionPolicy =
request.getTargetFileRetentionPolicyInfo().getRetentionPolicy();
accessLatency =
request.getTargetFileRetentionPolicyInfo().getAccessLatency();
}
TPutFileRequest[] fileRequests = getFileRequests(request);
// assume transfers will take place in parallel
long effectiveSize = largestFileOf(fileRequests);
long lifetime = Lifetimes.calculateLifetime(request.getDesiredTotalRequestTime(),
effectiveSize, configuration.getMaximumClientAssumedBandwidth(),
configuration.getPutLifetime());
TOverwriteMode overwriteMode = getOverwriteMode(request);
String[] supportedProtocols = storage.supportedPutProtocols();
boolean isAnyProtocolSupported = any(asList(protocols), in(asList(supportedProtocols)));
if (!isAnyProtocolSupported) {
throw new SRMNotSupportedException("Protocol(s) not supported: " + Arrays.toString(protocols));
}
URI[] surls = new URI[fileRequests.length];
Long[] sizes = new Long[fileRequests.length];
boolean[] wantPermanent = new boolean[fileRequests.length];
for (int i = 0; i < fileRequests.length; ++i) {
TPutFileRequest fileRequest = fileRequests[i];
if (fileRequest == null) {
throw new SRMInvalidRequestException("file request #" + (i + 1) + " is null.");
}
if (fileRequest.getTargetSURL() == null) {
throw new SRMInvalidRequestException("surl of file request #" + (i + 1) + " is null.");
}
URI surl = URI.create(fileRequest.getTargetSURL().toString());
UnsignedLong knownSize = fileRequest.getExpectedFileSize();
if (knownSize != null) {
sizes[i] = knownSize.longValue();
if (sizes[i] < 0) {
throw new SRMInvalidRequestException("Negative file size is not allowed.");
}
}
wantPermanent[i] = true; //for now, extract type info from space token in the future
/* nextRequest.getFileStorageType()==
TFileStorageType.PERMANENT;*/
surls[i] = surl;
}
PutRequest r =
new PutRequest(
user,
surls,
sizes,
wantPermanent,
protocols,
lifetime,
configuration.getPutMaxPollPeriod(),
clientHost,
spaceToken,
retentionPolicy,
accessLatency,
request.getUserRequestDescription());
try (JDC ignored = r.applyJdc()) {
if (overwriteMode != null) {
r.setOverwriteMode(overwriteMode);
}
srm.schedule(r);
// RequestScheduler will take care of the rest
//getRequestScheduler.add(r);
// Return the request status
return r.getSrmPrepareToPutResponse(configuration.getPutSwitchToAsynchronousModeDelay());
}
}
private long largestFileOf(TPutFileRequest[] requests)
{
long effectiveSize = 0;
for (TPutFileRequest request : requests) {
UnsignedLong size = request.getExpectedFileSize();
if (size != null && size.longValue() > effectiveSize) {
effectiveSize = size.longValue();
}
}
return effectiveSize;
}
private static TOverwriteMode getOverwriteMode(SrmPrepareToPutRequest request)
throws SRMNotSupportedException
{
TOverwriteMode overwriteMode = request.getOverwriteOption();
if (overwriteMode != null && overwriteMode.equals(TOverwriteMode.WHEN_FILES_ARE_DIFFERENT)) {
throw new SRMNotSupportedException(
"Overwrite Mode WHEN_FILES_ARE_DIFFERENT is not supported.");
}
return overwriteMode;
}
private static TPutFileRequest[] getFileRequests(SrmPrepareToPutRequest request)
throws SRMInvalidRequestException
{
TPutFileRequest[] fileRequests = null;
if (request.getArrayOfFileRequests() != null) {
fileRequests = request.getArrayOfFileRequests().getRequestArray();
}
if (fileRequests == null || fileRequests.length < 1) {
throw new SRMInvalidRequestException("request contains no file requests.");
}
return fileRequests;
}
private static Optional<String> getClientHost(SrmPrepareToPutRequest request)
{
if (request.getTransferParameters() != null &&
request.getTransferParameters().getArrayOfClientNetworks() != null) {
String[] clientNetworks =
request.getTransferParameters().getArrayOfClientNetworks().getStringArray();
if (clientNetworks != null &&
clientNetworks.length > 0 &&
clientNetworks[0] != null) {
return Optional.of(clientNetworks[0]);
}
}
return Optional.absent();
}
private static void checkFileStorageType(SrmPrepareToPutRequest request, TFileStorageType expectedStorageType)
throws SRMNotSupportedException
{
TFileStorageType storageType = request.getDesiredFileStorageType();
if (storageType != null && !storageType.equals(expectedStorageType)) {
throw new SRMNotSupportedException("DesiredFileStorageType " + storageType + " is not supported.");
}
}
private String[] getProtocols() throws SRMInvalidRequestException
{
String[] protocols = null;
TTransferParameters transferParameters = request.getTransferParameters();
if (transferParameters != null && transferParameters.getArrayOfTransferProtocols() != null) {
protocols = transferParameters.getArrayOfTransferProtocols().getStringArray();
}
protocols = Tools.trimStringArray(protocols);
if (protocols == null || protocols.length < 1) {
throw new SRMInvalidRequestException("request contains no transfer protocols.");
}
return protocols;
}
public static final SrmPrepareToPutResponse getFailedResponse(String error)
{
return getFailedResponse(error, TStatusCode.SRM_FAILURE);
}
public static final SrmPrepareToPutResponse getFailedResponse(String error, TStatusCode statusCode)
{
SrmPrepareToPutResponse srmPrepareToPutResponse = new SrmPrepareToPutResponse();
srmPrepareToPutResponse.setReturnStatus(new TReturnStatus(statusCode, error));
return srmPrepareToPutResponse;
}
}