/* dCache - http://www.dcache.org/ * * Copyright (C) 2014 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.srm.shell; import com.google.common.base.Throwables; import org.apache.axis.types.URI; import org.apache.axis.types.UnsignedLong; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import java.io.File; import java.rmi.RemoteException; import java.util.Map; import java.util.concurrent.TimeUnit; import eu.emi.security.authn.x509.X509Credential; import java.util.Collections; import org.dcache.srm.SRMException; import org.dcache.srm.SRMInvalidPathException; import org.dcache.srm.v2_2.ArrayOfAnyURI; import org.dcache.srm.v2_2.ArrayOfString; import org.dcache.srm.v2_2.ArrayOfTGroupPermission; import org.dcache.srm.v2_2.ArrayOfTMetaDataPathDetail; import org.dcache.srm.v2_2.ArrayOfTUserPermission; import org.dcache.srm.v2_2.ISRM; import org.dcache.srm.v2_2.SrmCheckPermissionRequest; import org.dcache.srm.v2_2.SrmCheckPermissionResponse; import org.dcache.srm.v2_2.SrmGetPermissionRequest; import org.dcache.srm.v2_2.SrmGetPermissionResponse; import org.dcache.srm.v2_2.SrmGetSpaceMetaDataRequest; import org.dcache.srm.v2_2.SrmGetSpaceMetaDataResponse; import org.dcache.srm.v2_2.SrmGetSpaceTokensRequest; import org.dcache.srm.v2_2.SrmGetSpaceTokensResponse; import org.dcache.srm.v2_2.SrmGetTransferProtocolsRequest; import org.dcache.srm.v2_2.SrmGetTransferProtocolsResponse; import org.dcache.srm.v2_2.SrmLsRequest; import org.dcache.srm.v2_2.SrmLsResponse; import org.dcache.srm.v2_2.SrmMkdirRequest; import org.dcache.srm.v2_2.SrmMkdirResponse; import org.dcache.srm.v2_2.SrmMvRequest; import org.dcache.srm.v2_2.SrmMvResponse; import org.dcache.srm.v2_2.SrmPingRequest; import org.dcache.srm.v2_2.SrmPingResponse; import org.dcache.srm.v2_2.SrmReleaseSpaceRequest; import org.dcache.srm.v2_2.SrmReleaseSpaceResponse; import org.dcache.srm.v2_2.SrmReserveSpaceRequest; import org.dcache.srm.v2_2.SrmReserveSpaceResponse; import org.dcache.srm.v2_2.SrmRmRequest; import org.dcache.srm.v2_2.SrmRmResponse; import org.dcache.srm.v2_2.SrmRmdirRequest; import org.dcache.srm.v2_2.SrmRmdirResponse; import org.dcache.srm.v2_2.SrmStatusOfLsRequestRequest; import org.dcache.srm.v2_2.SrmStatusOfLsRequestResponse; import org.dcache.srm.v2_2.SrmStatusOfReserveSpaceRequestRequest; import org.dcache.srm.v2_2.SrmStatusOfReserveSpaceRequestResponse; import org.dcache.srm.v2_2.TAccessLatency; import org.dcache.srm.v2_2.TFileType; import org.dcache.srm.v2_2.TGroupPermission; import org.dcache.srm.v2_2.TMetaDataPathDetail; import org.dcache.srm.v2_2.TMetaDataSpace; import org.dcache.srm.v2_2.TPermissionMode; import org.dcache.srm.v2_2.TPermissionReturn; import org.dcache.srm.v2_2.TRetentionPolicy; import org.dcache.srm.v2_2.TRetentionPolicyInfo; import org.dcache.srm.v2_2.TSURLPermissionReturn; import org.dcache.srm.v2_2.TStatusCode; import org.dcache.srm.v2_2.TSupportedTransferProtocol; import org.dcache.srm.v2_2.TUserPermission; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ObjectArrays.concat; import static org.dcache.srm.shell.TStatusCodes.checkBulkSuccess; import static org.dcache.srm.shell.TStatusCodes.checkSuccess; @ParametersAreNonnullByDefault public class AxisSrmFileSystem implements SrmFileSystem { private final int MAX_BULK_STAT = 1_000; private final int MAX_LS_RESPONSE = 2_000; private final ISRM srm; private final SrmTransferAgent srmAgent = new SrmTransferAgent(); private X509Credential credential; public AxisSrmFileSystem(ISRM srm) { this.srm = srm; } @Override public void setCredential(X509Credential credential) { this.credential = credential; } @Override public void start() { ExtendableFileTransferAgent transferAgent = new ExtendableFileTransferAgent(); transferAgent.setCredential(credential); transferAgent.start(); srmAgent.setSrm(srm); srmAgent.setFileTransferAgent(transferAgent); } @Override public void close() { try { srmAgent.close(); } catch (Exception e) { throw Throwables.propagate(e); } } @Nonnull @Override public TMetaDataPathDetail stat(URI surl) throws RemoteException, SRMException, InterruptedException { TMetaDataPathDetail result = stat(new URI[]{surl})[0]; checkSuccess(result.getStatus()); return result; } @Nonnull @Override public TMetaDataPathDetail[] stat(URI... surls) throws RemoteException, SRMException, InterruptedException { if (surls.length <= MAX_BULK_STAT) { return doBulkStat(surls); } else { TMetaDataPathDetail[] response = new TMetaDataPathDetail[surls.length]; URI[] surlsPart = new URI [MAX_BULK_STAT]; int index = 0; while (index < surls.length) { int count = Math.min(surls.length - index, MAX_BULK_STAT); if (surlsPart.length != count) { surlsPart = new URI [count]; } System.arraycopy(surls, index, surlsPart, 0, count); TMetaDataPathDetail[] responsePart = doBulkStat(surlsPart); System.arraycopy(responsePart, 0, response, index, count); index += count; } return response; } } private TMetaDataPathDetail[] doBulkStat(URI[] surls) throws RemoteException, SRMException, InterruptedException { SrmLsResponse response = srm.srmLs( new SrmLsRequest(null, new ArrayOfAnyURI(surls), null, null, true, false, 0, 0, surls.length)); ArrayOfTMetaDataPathDetail details; if (response.getReturnStatus().getStatusCode() != TStatusCode.SRM_REQUEST_QUEUED && response.getReturnStatus().getStatusCode() != TStatusCode.SRM_REQUEST_INPROGRESS) { checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS, TStatusCode.SRM_FAILURE); details = response.getDetails(); } else { SrmStatusOfLsRequestResponse status; do { TimeUnit.SECONDS.sleep(1); status = srm.srmStatusOfLsRequest( new SrmStatusOfLsRequestRequest(null, response.getRequestToken(), 0, surls.length)); } while (status.getReturnStatus().getStatusCode() == TStatusCode.SRM_REQUEST_QUEUED || status.getReturnStatus().getStatusCode() == TStatusCode.SRM_REQUEST_INPROGRESS); checkSuccess(status.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS, TStatusCode.SRM_FAILURE); details = status.getDetails(); } return details.getPathDetailArray(); } @Nonnull @Override public TPermissionMode checkPermission(URI surl) throws RemoteException, SRMException { TSURLPermissionReturn[] permission = checkPermissions(surl); checkSuccess(permission[0].getStatus()); return permission[0].getPermission(); } @Nonnull @Override public TSURLPermissionReturn[] checkPermissions(URI... surls) throws RemoteException, SRMException { checkArgument(surls.length > 0); SrmCheckPermissionResponse response = srm.srmCheckPermission( new SrmCheckPermissionRequest(new ArrayOfAnyURI(surls), null, null)); checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS, TStatusCode.SRM_FAILURE); TSURLPermissionReturn[] permissionArray = response.getArrayOfPermissions() == null ? null : response.getArrayOfPermissions().getSurlPermissionArray(); if (permissionArray == null || permissionArray.length == 0) { checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS); throw new SrmProtocolException("Server reply lacks permission array."); } if (permissionArray.length != surls.length) { throw new SrmProtocolException("Server returns permissionArray " + "with wrong size (" + permissionArray.length+" != " + surls.length + ")"); } return permissionArray; } @Nonnull @Override public TPermissionReturn getPermission(URI surl) throws RemoteException, SRMException { TPermissionReturn[] permission = getPermissions(surl); checkSuccess(permission[0].getStatus()); return permission[0]; } @Nonnull @Override public TPermissionReturn[] getPermissions(URI... surls) throws RemoteException, SRMException { checkArgument(surls.length > 0); SrmGetPermissionResponse response = srm.srmGetPermission( new SrmGetPermissionRequest(null, new ArrayOfAnyURI(surls), null)); TPermissionReturn[] permissionArray = response.getArrayOfPermissionReturns().getPermissionArray(); if (permissionArray == null || permissionArray.length == 0) { checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS); throw new SrmProtocolException("Server reply lacks permission array."); } checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS, TStatusCode.SRM_FAILURE); // Simplify things for the caller for (TPermissionReturn permission: permissionArray) { if (permission.getArrayOfUserPermissions() == null) { permission.setArrayOfUserPermissions(new ArrayOfTUserPermission()); } if (permission.getArrayOfUserPermissions().getUserPermissionArray() == null) { permission.getArrayOfUserPermissions().setUserPermissionArray(new TUserPermission[0]); } if (permission.getArrayOfGroupPermissions() == null) { permission.setArrayOfGroupPermissions(new ArrayOfTGroupPermission()); } if (permission.getArrayOfGroupPermissions().getGroupPermissionArray() == null) { permission.getArrayOfGroupPermissions().setGroupPermissionArray(new TGroupPermission[0]); } } return permissionArray; } private TMetaDataPathDetail list(URI surl, boolean verbose, int offset, int count) throws RemoteException, SRMException, InterruptedException { SrmLsResponse response = srm.srmLs( new SrmLsRequest(null, new ArrayOfAnyURI(new URI[]{surl}), null, null, verbose, false, 1, offset, count)); while (response.getReturnStatus().getStatusCode() == TStatusCode.SRM_REQUEST_QUEUED || response.getReturnStatus().getStatusCode() == TStatusCode.SRM_REQUEST_INPROGRESS) { TimeUnit.SECONDS.sleep(1); SrmStatusOfLsRequestResponse status = srm.srmStatusOfLsRequest( new SrmStatusOfLsRequestRequest(null, response.getRequestToken(), offset, count)); response.setDetails(status.getDetails()); response.setReturnStatus(status.getReturnStatus()); } TMetaDataPathDetail details = response.getDetails().getPathDetailArray()[0]; checkBulkSuccess(response.getReturnStatus(), Collections.singletonList(details.getStatus())); return details; } @Nonnull @Override public TMetaDataPathDetail[] list(URI surl, boolean verbose) throws RemoteException, SRMException, InterruptedException { int offset = 0; int count = MAX_LS_RESPONSE; TMetaDataPathDetail[] list = {}; do { TMetaDataPathDetail detail = list(surl, verbose, offset, count); if (detail.getType() != TFileType.DIRECTORY) { throw new SRMInvalidPathException("Not a directory"); } offset += count; TMetaDataPathDetail[] pathDetailArray = detail.getArrayOfSubPaths() == null ? null : detail.getArrayOfSubPaths().getPathDetailArray(); if (pathDetailArray != null) { list = concat(list, pathDetailArray, TMetaDataPathDetail.class); } } while (list.length == offset); return list; } @Nonnull @Override public SrmPingResponse ping() throws RemoteException, SRMException { return srm.srmPing(new SrmPingRequest()); } @Nonnull @Override public TSupportedTransferProtocol[] getTransferProtocols() throws SRMException, RemoteException { SrmGetTransferProtocolsResponse response = srm.srmGetTransferProtocols(new SrmGetTransferProtocolsRequest()); checkSuccess(response.getReturnStatus()); TSupportedTransferProtocol[] protocolArray = response.getProtocolInfo().getProtocolArray(); return (protocolArray == null) ? new TSupportedTransferProtocol[0] : protocolArray; } @Override public void mkdir(URI surl) throws RemoteException, SRMException { SrmMkdirResponse response = srm.srmMkdir(new SrmMkdirRequest(null, surl, null)); checkSuccess(response.getReturnStatus()); } @Override public void rmdir(URI lookup, boolean recursive) throws RemoteException, SRMException { SrmRmdirResponse response = srm.srmRmdir(new SrmRmdirRequest(null, lookup, null, recursive)); checkSuccess(response.getReturnStatus()); } @Nonnull @Override public SrmRmResponse rm(URI... surls) throws RemoteException, SRMException { SrmRmResponse response = srm.srmRm(new SrmRmRequest(null, new ArrayOfAnyURI(surls), null)); if (response.getArrayOfFileStatuses().getStatusArray() == null) { checkSuccess(response.getReturnStatus()); } else { checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS, TStatusCode.SRM_FAILURE); } return response; } @Override public void mv(URI fromSurl, URI toSurl) throws RemoteException, SRMException { SrmMvResponse response = srm.srmMv(new SrmMvRequest(null, fromSurl, toSurl, null)); checkSuccess(response.getReturnStatus()); } @Nonnull @Override public String[] getSpaceTokens(String userSpaceTokenDescription) throws RemoteException, SRMException { SrmGetSpaceTokensResponse response = srm.srmGetSpaceTokens( new SrmGetSpaceTokensRequest(userSpaceTokenDescription, null)); checkSuccess(response.getReturnStatus()); ArrayOfString arrayOfSpaceTokens = response.getArrayOfSpaceTokens(); return (arrayOfSpaceTokens != null) ? arrayOfSpaceTokens.getStringArray() : new String[0]; } @Nonnull @Override public TMetaDataSpace reserveSpace(long size, @Nullable String description, @Nullable TAccessLatency al, TRetentionPolicy rp, @Nullable Integer lifetime) throws SRMException, RemoteException, InterruptedException { SrmReserveSpaceResponse response = srm.srmReserveSpace( new SrmReserveSpaceRequest(null, description, new TRetentionPolicyInfo(rp, al), new UnsignedLong(size), new UnsignedLong(size), lifetime, null, null, null)); while (response.getReturnStatus().getStatusCode() == TStatusCode.SRM_REQUEST_QUEUED || response.getReturnStatus().getStatusCode() == TStatusCode.SRM_REQUEST_INPROGRESS) { if (response.getEstimatedProcessingTime() != null) { TimeUnit.SECONDS.sleep(response.getEstimatedProcessingTime()); } else { TimeUnit.SECONDS.sleep(2); } SrmStatusOfReserveSpaceRequestResponse status = srm.srmStatusOfReserveSpaceRequest( new SrmStatusOfReserveSpaceRequestRequest(null, response.getRequestToken())); response.setReturnStatus(status.getReturnStatus()); response.setLifetimeOfReservedSpace(status.getLifetimeOfReservedSpace()); response.setRetentionPolicyInfo(status.getRetentionPolicyInfo()); response.setSizeOfGuaranteedReservedSpace(status.getSizeOfGuaranteedReservedSpace()); response.setSizeOfTotalReservedSpace(status.getSizeOfTotalReservedSpace()); response.setSpaceToken(status.getSpaceToken()); response.setEstimatedProcessingTime(status.getEstimatedProcessingTime()); } checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_LOWER_SPACE_GRANTED); TMetaDataSpace space = new TMetaDataSpace(); space.setLifetimeAssigned(response.getLifetimeOfReservedSpace()); space.setRetentionPolicyInfo(response.getRetentionPolicyInfo()); space.setGuaranteedSize(response.getSizeOfGuaranteedReservedSpace()); space.setTotalSize(response.getSizeOfTotalReservedSpace()); space.setSpaceToken(response.getSpaceToken()); space.setStatus(response.getReturnStatus()); return space; } @Override public void releaseSpace(String spaceToken) throws RemoteException, SRMException { SrmReleaseSpaceResponse response = srm.srmReleaseSpace( new SrmReleaseSpaceRequest(null, spaceToken, null, null)); checkSuccess(response.getReturnStatus()); } @Nonnull @Override public TMetaDataSpace[] getSpaceMetaData(String... spaceTokens) throws RemoteException, SRMException { checkArgument(spaceTokens.length > 0); SrmGetSpaceMetaDataResponse response = srm.srmGetSpaceMetaData( new SrmGetSpaceMetaDataRequest(null, new ArrayOfString(spaceTokens))); TMetaDataSpace[] spaceDataArray = response.getArrayOfSpaceDetails().getSpaceDataArray(); if (spaceDataArray == null || spaceDataArray.length == 0) { checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS); throw new SrmProtocolException("Server reply lacks space meta data."); } else { checkSuccess(response.getReturnStatus(), TStatusCode.SRM_SUCCESS, TStatusCode.SRM_PARTIAL_SUCCESS, TStatusCode.SRM_FAILURE); } return spaceDataArray; } @Nonnull @Override public TMetaDataSpace getSpaceMetaData(String spaceToken) throws RemoteException, SRMException { TMetaDataSpace space = getSpaceMetaData(new String[]{spaceToken})[0]; checkSuccess(space.getStatus()); return space; } @Nullable @Override public FileTransfer get(URI source, File target) { return srmAgent.download(source, target); } @Nullable @Override public FileTransfer put(File source, URI target) { return srmAgent.upload(source, target); } @Override @Nonnull public Map<String,String> getTransportOptions() { return srmAgent.getOptions(); } @Override public void setTransportOption(String key, String value) { srmAgent.setOption(key, value); } }