package org.dcache.srm.handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import org.dcache.srm.AbstractStorageElement;
import org.dcache.srm.SRM;
import org.dcache.srm.SRMAbortedException;
import org.dcache.srm.SRMException;
import org.dcache.srm.SRMFileRequestNotFoundException;
import org.dcache.srm.SRMInternalErrorException;
import org.dcache.srm.SRMInvalidRequestException;
import org.dcache.srm.SRMReleasedException;
import org.dcache.srm.SRMUser;
import org.dcache.srm.request.BringOnlineRequest;
import org.dcache.srm.request.ContainerRequest;
import org.dcache.srm.request.CopyRequest;
import org.dcache.srm.request.FileRequest;
import org.dcache.srm.request.PutRequest;
import org.dcache.srm.request.Request;
import org.dcache.srm.util.Configuration;
import org.dcache.srm.util.JDC;
import org.dcache.srm.v2_2.ArrayOfTSURLLifetimeReturnStatus;
import org.dcache.srm.v2_2.SrmExtendFileLifeTimeRequest;
import org.dcache.srm.v2_2.SrmExtendFileLifeTimeResponse;
import org.dcache.srm.v2_2.TReturnStatus;
import org.dcache.srm.v2_2.TSURLLifetimeReturnStatus;
import org.dcache.srm.v2_2.TStatusCode;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.dcache.srm.handler.ReturnStatuses.*;
public class SrmExtendFileLifeTime
{
private static final Logger LOGGER =
LoggerFactory.getLogger(SrmExtendFileLifeTime.class);
private final AbstractStorageElement storage;
private final SrmExtendFileLifeTimeRequest request;
private final SRMUser user;
private final Configuration configuration;
private SrmExtendFileLifeTimeResponse response;
public SrmExtendFileLifeTime(SRMUser user,
SrmExtendFileLifeTimeRequest request,
AbstractStorageElement storage,
SRM srm,
String clientHost)
{
this.request = checkNotNull(request);
this.user = checkNotNull(user);
this.storage = checkNotNull(storage);
this.configuration = srm.getConfiguration();
}
public SrmExtendFileLifeTimeResponse getResponse()
{
if (response == null) {
try {
response = extendFileLifeTime();
} 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);
}
}
return response;
}
private SrmExtendFileLifeTimeResponse extendFileLifeTime()
throws SRMInvalidRequestException, SRMInternalErrorException
{
org.apache.axis.types.URI[] surls = request.getArrayOfSURLs().getUrlArray();
if (surls == null) {
throw new SRMInvalidRequestException("arrayOfSURLs is required");
}
/* [ SRM 2.2, 5.16.2 ]
*
* a) srmExtendFileLifeTime allows to change only one type of lifetime at a
* time (either SURL lifetime by the newFileLifetime or pin lifetime by
* the newPinLifetime), depending on the presence or absence of the request
* token. Either newFileLifetime or newPinLifetime must be provided. When
* both newFileLifetime and newPinLifetime are provided in the same request,
* the request must be invalid, and SRM_INVALID_REQUEST must be returned.
*/
String token = request.getRequestToken();
Integer newFileLifetime = request.getNewFileLifeTime();
Integer newPinLifetime = request.getNewPinLifeTime();
if (newFileLifetime != null && newPinLifetime != null) {
throw new SRMInvalidRequestException("newFileLifetime and newPinLifetime must not be present at the same time");
}
if (token == null) {
if (newFileLifetime == null) {
throw new SRMInvalidRequestException("requestToken and newFileLifetime must not be absent at the same time");
}
return extendSurlLifeTime(surls, newFileLifetime);
} else {
if (newPinLifetime == null) {
throw new SRMInvalidRequestException("newPinLifetime is required when requestToken is present");
}
return extendTurlOrPinLifeTime(token, surls, newPinLifetime);
}
}
private SrmExtendFileLifeTimeResponse extendSurlLifeTime(org.apache.axis.types.URI[] surls,
int newFileLifetime)
throws SRMInternalErrorException
{
long newLifetimeInMillis = toMillis(newFileLifetime, Long.MAX_VALUE);
int len = surls.length;
TSURLLifetimeReturnStatus[] surlStatus = new TSURLLifetimeReturnStatus[len];
for (int i = 0; i < len; ++i) {
surlStatus[i] = extendSurlLifeTime(surls[i], newLifetimeInMillis);
}
return new SrmExtendFileLifeTimeResponse(
getSummaryReturnStatus(surlStatus),
new ArrayOfTSURLLifetimeReturnStatus(surlStatus));
}
private TSURLLifetimeReturnStatus extendSurlLifeTime(org.apache.axis.types.URI surl,
long newLifetimeInMillis)
throws SRMInternalErrorException
{
TSURLLifetimeReturnStatus status = new TSURLLifetimeReturnStatus();
status.setSurl(surl);
TReturnStatus returnStatus;
try {
long lifetimeLeftInMillis =
storage.srmExtendSurlLifetime(user, URI.create(surl.toString()), newLifetimeInMillis);
status.setFileLifetime(toSeconds(lifetimeLeftInMillis));
returnStatus = new TReturnStatus(TStatusCode.SRM_SUCCESS, null);
} catch (SRMInternalErrorException e) {
throw new SRMInternalErrorException("File lifetime extension failed for SURL " +
surl + ": " + e.getMessage(), e);
} catch (SRMException e) {
returnStatus = new TReturnStatus(TStatusCode.SRM_FAILURE, e.toString());
}
status.setStatus(returnStatus);
return status;
}
private long getMaxLifetime(ContainerRequest<?> containerRequest)
{
long configMaximumLifetime;
if (containerRequest instanceof CopyRequest) {
configMaximumLifetime = configuration.getCopyLifetime();
} else if (containerRequest instanceof PutRequest) {
configMaximumLifetime = configuration.getPutLifetime();
} else if (containerRequest instanceof BringOnlineRequest) {
configMaximumLifetime = configuration.getBringOnlineLifetime();
} else {
configMaximumLifetime = configuration.getGetLifetime();
}
return configMaximumLifetime;
}
private SrmExtendFileLifeTimeResponse extendTurlOrPinLifeTime(
String requestToken, org.apache.axis.types.URI[] surls, int newLifetime)
throws SRMInvalidRequestException, SRMInternalErrorException
{
ContainerRequest<?> containerRequest =
Request.getRequest(requestToken, ContainerRequest.class);
try (JDC ignored = containerRequest.applyJdc()) {
long maxLifetime = getMaxLifetime(containerRequest);
long newLifetimeInMillis = toMillis(newLifetime, maxLifetime);
int len = surls.length;
TSURLLifetimeReturnStatus[] surlStatus = new TSURLLifetimeReturnStatus[len];
for (int i = 0; i < len; ++i) {
surlStatus[i] = extendTurlOrPinLifeTime(containerRequest, surls[i], newLifetimeInMillis);
}
return new SrmExtendFileLifeTimeResponse(
getSummaryReturnStatus(surlStatus),
new ArrayOfTSURLLifetimeReturnStatus(surlStatus));
}
}
private TSURLLifetimeReturnStatus extendTurlOrPinLifeTime(
ContainerRequest<?> request, org.apache.axis.types.URI surl, long newLifetimeInMillis)
throws SRMInternalErrorException
{
TSURLLifetimeReturnStatus status = new TSURLLifetimeReturnStatus();
status.setSurl(surl);
TReturnStatus returnStatus;
try {
FileRequest<?> fileRequest =
request.getFileRequestBySurl(URI.create(surl.toString()));
long lifetimeLeftMillis =
fileRequest.extendLifetime(newLifetimeInMillis);
status.setPinLifetime(toSeconds(lifetimeLeftMillis));
returnStatus = new TReturnStatus(TStatusCode.SRM_SUCCESS, null);
} catch (SRMFileRequestNotFoundException e) {
returnStatus = new TReturnStatus(
TStatusCode.SRM_INVALID_PATH, "SURL does match any existing file request associated with the request token");
} catch (SRMInvalidRequestException e) {
returnStatus = new TReturnStatus(
TStatusCode.SRM_INVALID_REQUEST, "TURL is no longer valid and cannot be extended");
} catch (SRMReleasedException e) {
returnStatus = new TReturnStatus(
TStatusCode.SRM_RELEASED, "TURL has been released and cannot be extended");
} catch (SRMAbortedException e) {
returnStatus = new TReturnStatus(
TStatusCode.SRM_ABORTED, "TURL has been aborted and cannot be extended");
} catch (SRMInternalErrorException e) {
throw new SRMInternalErrorException("File lifetime extension failed for request " + request.getId()
+ " with SURL " + surl + ": " + e.getMessage(), e);
} catch (SRMException e) {
LOGGER.warn("File lifetime extension failed for request {} with SURL {}: {}",
request.getId(), surl, e.getMessage());
returnStatus = new TReturnStatus(TStatusCode.SRM_FAILURE,
"TURL for request " + request.getId() + " with SURL " + surl + " cannot be extended: " + e.getMessage());
}
status.setStatus(returnStatus);
return status;
}
private static long toMillis(int seconds, long max)
{
/* [ SRM 2.2, 1.20 ]
*
* o A negative value (-1) indicates “infinite (indefinite)” time.
*/
return (seconds >= 0) ? Math.min(SECONDS.toMillis(seconds), max) : -1;
}
private static int toSeconds(long millis)
{
/* [ SRM 2.2, 1.20 ]
*
* o A negative value (-1) indicates “infinite (indefinite)” time.
*/
return (millis >= 0) ? (int) MILLISECONDS.toSeconds(millis) : -1;
}
public static final SrmExtendFileLifeTimeResponse getFailedResponse(String error)
{
return getFailedResponse(error, TStatusCode.SRM_FAILURE);
}
public static final SrmExtendFileLifeTimeResponse getFailedResponse(
String error, TStatusCode statusCode)
{
SrmExtendFileLifeTimeResponse response = new SrmExtendFileLifeTimeResponse();
response.setReturnStatus(new TReturnStatus(statusCode, error));
return response;
}
}