[#37] Add tree service methods: getRange(), getRangeHash(), patchObject() #38

Merged
orikik merged 1 commit from orikik/frostfs-sdk-java:feature/tree_methods into master 2025-01-30 11:05:50 +00:00
26 changed files with 642 additions and 20 deletions

View file

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>frostfs-sdk-java</artifactId> <artifactId>frostfs-sdk-java</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</parent> </parent>
<artifactId>client</artifactId> <artifactId>client</artifactId>
@ -21,17 +21,17 @@
<dependency> <dependency>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>cryptography</artifactId> <artifactId>cryptography</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>models</artifactId> <artifactId>models</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>exceptions</artifactId> <artifactId>exceptions</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-codec</groupId> <groupId>commons-codec</groupId>

View file

@ -24,6 +24,9 @@ import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete;
import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet;
import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll;
import info.frostfs.sdk.jdo.parameters.object.*; import info.frostfs.sdk.jdo.parameters.object.*;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet;
import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate;
import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.jdo.result.ObjectHeaderResult;
import info.frostfs.sdk.pool.SessionCache; import info.frostfs.sdk.pool.SessionCache;
@ -33,6 +36,7 @@ import info.frostfs.sdk.services.impl.*;
import info.frostfs.sdk.services.impl.interceptor.Configuration; import info.frostfs.sdk.services.impl.interceptor.Configuration;
import info.frostfs.sdk.services.impl.interceptor.MonitoringClientInterceptor; import info.frostfs.sdk.services.impl.interceptor.MonitoringClientInterceptor;
import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter;
import info.frostfs.sdk.services.impl.rwhelper.RangeReader;
import info.frostfs.sdk.utils.Validator; import info.frostfs.sdk.utils.Validator;
import io.grpc.Channel; import io.grpc.Channel;
import io.grpc.ClientInterceptors; import io.grpc.ClientInterceptors;
@ -164,6 +168,21 @@ public class FrostFSClient implements CommonClient {
return objectClientImpl.searchObjects(args, ctx); return objectClientImpl.searchObjects(args, ctx);
} }
@Override
public RangeReader getRange(PrmRangeGet args, CallContext ctx) {
return objectClientImpl.getRange(args, ctx);
}
@Override
public byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx) {
return objectClientImpl.getRangeHash(args, ctx);
}
@Override
public ObjectId patchObject(PrmObjectPatch args, CallContext ctx) {
return objectClientImpl.patchObject(args, ctx);
}
@Override @Override
public byte[] addChain(PrmApeChainAdd args, CallContext ctx) { public byte[] addChain(PrmApeChainAdd args, CallContext ctx) {
return apeManagerClient.addChain(args, ctx); return apeManagerClient.addChain(args, ctx);
@ -213,6 +232,7 @@ public class FrostFSClient implements CommonClient {
return accountingClient.getBalance(ctx); return accountingClient.getBalance(ctx);
} }
@Override
public String dial(CallContext ctx) { public String dial(CallContext ctx) {
accountingClient.getBalance(ctx); accountingClient.getBalance(ctx);
return null; return null;

View file

@ -0,0 +1,42 @@
package info.frostfs.sdk.jdo.parameters.object.patch;
import info.frostfs.sdk.annotations.NotNull;
import info.frostfs.sdk.dto.object.ObjectAttribute;
import info.frostfs.sdk.dto.object.patch.Address;
import info.frostfs.sdk.dto.object.patch.Range;
import info.frostfs.sdk.dto.session.SessionToken;
import info.frostfs.sdk.jdo.parameters.session.SessionContext;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
@Getter
@Builder
@AllArgsConstructor
public class PrmObjectPatch implements SessionContext {
@NotNull
private Address address;
@NotNull
private Range range;
@NotNull
private InputStream payload;
private List<ObjectAttribute> newAttributes;
private boolean replaceAttributes;
private int maxChunkLength;
private SessionToken sessionToken;
private Map<String, String> xHeaders;
public PrmObjectPatch(Address address, Range range, InputStream payload, int maxChunkLength) {
this.address = address;
this.range = range;
this.payload = payload;
this.maxChunkLength = maxChunkLength;
}
}

View file

@ -0,0 +1,35 @@
package info.frostfs.sdk.jdo.parameters.object.patch;
import info.frostfs.sdk.annotations.NotNull;
import info.frostfs.sdk.dto.container.ContainerId;
import info.frostfs.sdk.dto.object.ObjectId;
import info.frostfs.sdk.dto.object.patch.Range;
import info.frostfs.sdk.dto.session.SessionToken;
import info.frostfs.sdk.jdo.parameters.session.SessionContext;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import java.util.Map;
@Getter
@Builder
@AllArgsConstructor
public class PrmRangeGet implements SessionContext {
@NotNull
private ContainerId containerId;
@NotNull
private ObjectId objectId;
@NotNull
private Range range;
private boolean raw;
private SessionToken sessionToken;
private Map<String, String> xHeaders;
public PrmRangeGet(ContainerId containerId, ObjectId objectId, Range range) {
this.containerId = containerId;
this.objectId = objectId;
this.range = range;
}
}

View file

@ -0,0 +1,38 @@
package info.frostfs.sdk.jdo.parameters.object.patch;
import info.frostfs.sdk.annotations.NotNull;
import info.frostfs.sdk.dto.container.ContainerId;
import info.frostfs.sdk.dto.object.ObjectId;
import info.frostfs.sdk.dto.object.patch.Range;
import info.frostfs.sdk.dto.session.SessionToken;
import info.frostfs.sdk.jdo.parameters.session.SessionContext;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
import java.util.Map;
@Getter
@Builder
@AllArgsConstructor
public class PrmRangeHashGet implements SessionContext {
@NotNull
private ContainerId containerId;
@NotNull
private ObjectId objectId;
@NotNull
private List<Range> ranges;
@NotNull
private byte[] salt;
private SessionToken sessionToken;
private Map<String, String> xHeaders;
public PrmRangeHashGet(ContainerId containerId, ObjectId objectId, List<Range> ranges, byte[] salt) {
this.containerId = containerId;
this.objectId = objectId;
this.ranges = ranges;
this.salt = salt;
}
}

View file

@ -26,12 +26,16 @@ import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete;
import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet;
import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll;
import info.frostfs.sdk.jdo.parameters.object.*; import info.frostfs.sdk.jdo.parameters.object.*;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet;
import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate;
import info.frostfs.sdk.jdo.pool.NodeParameters; import info.frostfs.sdk.jdo.pool.NodeParameters;
import info.frostfs.sdk.jdo.pool.PoolInitParameters; import info.frostfs.sdk.jdo.pool.PoolInitParameters;
import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.jdo.result.ObjectHeaderResult;
import info.frostfs.sdk.services.CommonClient; import info.frostfs.sdk.services.CommonClient;
import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter;
import info.frostfs.sdk.services.impl.rwhelper.RangeReader;
import info.frostfs.sdk.utils.FrostFSMessages; import info.frostfs.sdk.utils.FrostFSMessages;
import info.frostfs.sdk.utils.WaitUtil; import info.frostfs.sdk.utils.WaitUtil;
import lombok.Getter; import lombok.Getter;
@ -198,6 +202,7 @@ public class Pool implements CommonClient {
return address + key; return address + key;
} }
@Override
public String dial(CallContext ctx) { public String dial(CallContext ctx) {
InnerPool[] inner = new InnerPool[rebalanceParams.getNodesParams().length]; InnerPool[] inner = new InnerPool[rebalanceParams.getNodesParams().length];
boolean atLeastOneHealthy = false; boolean atLeastOneHealthy = false;
@ -479,6 +484,24 @@ public class Pool implements CommonClient {
return client.getClient().searchObjects(args, ctx); return client.getClient().searchObjects(args, ctx);
} }
@Override
public RangeReader getRange(PrmRangeGet args, CallContext ctx) {
ClientWrapper client = connection();
return client.getClient().getRange(args, ctx);
}
@Override
public byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx) {
ClientWrapper client = connection();
return client.getClient().getRangeHash(args, ctx);
}
@Override
public ObjectId patchObject(PrmObjectPatch args, CallContext ctx) {
ClientWrapper client = connection();
return client.getClient().patchObject(args, ctx);
}
@Override @Override
public byte[] addChain(PrmApeChainAdd args, CallContext ctx) { public byte[] addChain(PrmApeChainAdd args, CallContext ctx) {
ClientWrapper client = connection(); ClientWrapper client = connection();

View file

@ -1,5 +1,9 @@
package info.frostfs.sdk.services; package info.frostfs.sdk.services;
import info.frostfs.sdk.jdo.parameters.CallContext;
public interface CommonClient extends public interface CommonClient extends
AccountingClient, ApeManagerClient, ContainerClient, NetmapClient, ObjectClient, SessionClient, ToolsClient { AccountingClient, ApeManagerClient, ContainerClient, NetmapClient, ObjectClient, SessionClient, ToolsClient {
String dial(CallContext ctx);
} }

View file

@ -4,8 +4,12 @@ import info.frostfs.sdk.dto.object.ObjectFrostFS;
import info.frostfs.sdk.dto.object.ObjectId; import info.frostfs.sdk.dto.object.ObjectId;
import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.CallContext;
import info.frostfs.sdk.jdo.parameters.object.*; import info.frostfs.sdk.jdo.parameters.object.*;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet;
import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.jdo.result.ObjectHeaderResult;
import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter;
import info.frostfs.sdk.services.impl.rwhelper.RangeReader;
public interface ObjectClient { public interface ObjectClient {
ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx); ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx);
@ -21,4 +25,10 @@ public interface ObjectClient {
void deleteObject(PrmObjectDelete args, CallContext ctx); void deleteObject(PrmObjectDelete args, CallContext ctx);
Iterable<ObjectId> searchObjects(PrmObjectSearch args, CallContext ctx); Iterable<ObjectId> searchObjects(PrmObjectSearch args, CallContext ctx);
RangeReader getRange(PrmRangeGet args, CallContext ctx);
byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx);
ObjectId patchObject(PrmObjectPatch args, CallContext ctx);
} }

View file

@ -14,16 +14,18 @@ import info.frostfs.sdk.jdo.ClientEnvironment;
import info.frostfs.sdk.jdo.PutObjectResult; import info.frostfs.sdk.jdo.PutObjectResult;
import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.CallContext;
import info.frostfs.sdk.jdo.parameters.object.*; import info.frostfs.sdk.jdo.parameters.object.*;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet;
import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet;
import info.frostfs.sdk.jdo.parameters.session.SessionContext; import info.frostfs.sdk.jdo.parameters.session.SessionContext;
import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.jdo.result.ObjectHeaderResult;
import info.frostfs.sdk.mappers.container.ContainerIdMapper; import info.frostfs.sdk.mappers.container.ContainerIdMapper;
import info.frostfs.sdk.mappers.object.*; import info.frostfs.sdk.mappers.object.*;
import info.frostfs.sdk.mappers.object.patch.AddressMapper;
import info.frostfs.sdk.mappers.object.patch.RangeMapper;
import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.ContextAccessor;
import info.frostfs.sdk.services.ObjectClient; import info.frostfs.sdk.services.ObjectClient;
import info.frostfs.sdk.services.impl.rwhelper.ObjectReaderImpl; import info.frostfs.sdk.services.impl.rwhelper.*;
import info.frostfs.sdk.services.impl.rwhelper.ObjectStreamer;
import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter;
import info.frostfs.sdk.services.impl.rwhelper.SearchReader;
import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestConstructor;
import info.frostfs.sdk.tools.Verifier; import info.frostfs.sdk.tools.Verifier;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
@ -200,6 +202,77 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient {
return new ObjectId(grpcObject.getObjectId().getValue().toByteArray()); return new ObjectId(grpcObject.getObjectId().getValue().toByteArray());
} }
@Override
public RangeReader getRange(PrmRangeGet args, CallContext ctx) {
validate(args);
var request = createGetRangeRequest(args, ctx);
var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit());
return new RangeReader(service.getRange(request));
}
@Override
public byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx) {
validate(args);
var request = createGetRangeHashRequest(args, ctx);
var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit());
var response = service.getRangeHash(request);
Verifier.checkResponse(response);
return response.getBody().getHashListList().stream().map(ByteString::toByteArray).toArray(byte[][]::new);
}
@Override
public ObjectId patchObject(PrmObjectPatch args, CallContext ctx) {
validate(args);
var request = createInitPatchRequest(args);
var protoToken = RequestConstructor.createObjectTokenContext(
getOrCreateSession(args, ctx),
request.getBody().getAddress(),
frostfs.session.Types.ObjectSessionContext.Verb.PATCH,
getContext().getKey()
);
var currentPos = args.getRange().getOffset();
var chunkSize = args.getMaxChunkLength();
byte[] chunkBuffer = new byte[chunkSize];
var service = deadLineAfter(objectServiceClient, ctx.getTimeout(), ctx.getTimeUnit());
PatchStreamer writer = new PatchStreamer(service);
var bytesCount = readNBytes(args.getPayload(), chunkBuffer, chunkSize);
while (bytesCount > 0) {
var range = Service.Range.newBuilder()
.setOffset(currentPos)
.setLength(bytesCount)
.build();
Service.PatchRequest.Body.Patch.newBuilder()
.setChunk(ByteString.copyFrom(chunkBuffer, 0, bytesCount))
.setSourceRange(range)
.build();
currentPos += bytesCount;
RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken);
sign(request, getContext().getKey());
writer.write(request.build());
bytesCount = readNBytes(args.getPayload(), chunkBuffer, chunkSize);
}
var response = writer.complete();
Verifier.checkResponse(response);
return ObjectIdMapper.toModel(response.getBody().getObjectId());
}
private ObjectFrostFS getObject(Service.GetRequest request, CallContext ctx) { private ObjectFrostFS getObject(Service.GetRequest request, CallContext ctx) {
var reader = getObjectInit(request, ctx); var reader = getObjectInit(request, ctx);
@ -371,7 +444,6 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient {
} }
private Service.GetRequest createGetRequest(PrmObjectGet args, CallContext ctx) { private Service.GetRequest createGetRequest(PrmObjectGet args, CallContext ctx) {
var address = Types.Address.newBuilder() var address = Types.Address.newBuilder()
.setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId())) .setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId()))
.setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId())) .setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId()))
@ -510,4 +582,70 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient {
return request.build(); return request.build();
} }
private Service.GetRangeRequest createGetRangeRequest(PrmRangeGet args, CallContext ctx) {
var address = Types.Address.newBuilder()
.setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId()))
.setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId()))
.build();
var body = Service.GetRangeRequest.Body.newBuilder()
.setAddress(address)
.setRange(RangeMapper.toGrpcMessage(args.getRange()))
.setRaw(args.isRaw())
.build();
var request = Service.GetRangeRequest.newBuilder()
.setBody(body);
var sessionToken = getOrCreateSession(args, ctx);
var protoToken = RequestConstructor.createObjectTokenContext(
sessionToken,
address,
frostfs.session.Types.ObjectSessionContext.Verb.RANGE,
getContext().getKey()
);
RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken);
sign(request, getContext().getKey());
return request.build();
}
private Service.GetRangeHashRequest createGetRangeHashRequest(PrmRangeHashGet args, CallContext ctx) {
var address = Types.Address.newBuilder()
.setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId()))
.setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId()))
.build();
var body = Service.GetRangeHashRequest.Body.newBuilder()
.setAddress(address)
.setType(Types.ChecksumType.SHA256)
.setSalt(ByteString.copyFrom(args.getSalt()))
.addAllRanges(RangeMapper.toGrpcMessages(args.getRanges()))
.build();
var request = Service.GetRangeHashRequest.newBuilder()
.setBody(body);
var sessionToken = getOrCreateSession(args, ctx);
var protoToken = RequestConstructor.createObjectTokenContext(
sessionToken,
address,
frostfs.session.Types.ObjectSessionContext.Verb.RANGEHASH,
getContext().getKey()
);
RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken);
sign(request, getContext().getKey());
return request.build();
}
private Service.PatchRequest.Builder createInitPatchRequest(PrmObjectPatch args) {
var address = AddressMapper.toGrpcMessage(args.getAddress());
var body = Service.PatchRequest.Body.newBuilder()
.setAddress(address)
.setReplaceAttributes(args.isReplaceAttributes())
.addAllNewAttributes(ObjectAttributeMapper.toGrpcMessages(args.getNewAttributes()))
.build();
return Service.PatchRequest.newBuilder()
.setBody(body);
}
} }

View file

@ -7,11 +7,11 @@ import info.frostfs.sdk.utils.WaitUtil;
import io.grpc.stub.StreamObserver; import io.grpc.stub.StreamObserver;
import lombok.Getter; import lombok.Getter;
import static info.frostfs.sdk.constants.AppConst.DEFAULT_POLL_INTERVAL;
import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE;
import static java.util.Objects.isNull; import static java.util.Objects.isNull;
public class ObjectStreamer { public class ObjectStreamer {
private static final long POLL_INTERVAL = 10;
private final StreamObserver<Service.PutRequest> requestObserver; private final StreamObserver<Service.PutRequest> requestObserver;
private final PutResponseCallback responseObserver; private final PutResponseCallback responseObserver;
@ -35,7 +35,7 @@ public class ObjectStreamer {
requestObserver.onCompleted(); requestObserver.onCompleted();
while (isNull(responseObserver.getResponse())) { while (isNull(responseObserver.getResponse())) {
WaitUtil.sleep(POLL_INTERVAL); WaitUtil.sleep(DEFAULT_POLL_INTERVAL);
} }
return responseObserver.getResponse(); return responseObserver.getResponse();

View file

@ -0,0 +1,62 @@
package info.frostfs.sdk.services.impl.rwhelper;
import frostfs.object.ObjectServiceGrpc;
import frostfs.object.Service;
import info.frostfs.sdk.exceptions.ProcessFrostFSException;
import info.frostfs.sdk.utils.WaitUtil;
import io.grpc.stub.StreamObserver;
import lombok.Getter;
import static info.frostfs.sdk.constants.AppConst.DEFAULT_POLL_INTERVAL;
import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE;
import static java.util.Objects.isNull;
public class PatchStreamer {
private final StreamObserver<Service.PatchRequest> requestObserver;
private final PatchResponseCallback responseObserver;
public PatchStreamer(ObjectServiceGrpc.ObjectServiceStub objectServiceStub) {
PatchResponseCallback responseObserver = new PatchResponseCallback();
this.responseObserver = responseObserver;
this.requestObserver = objectServiceStub.patch(responseObserver);
}
public void write(Service.PatchRequest request) {
if (isNull(request)) {
throw new ProcessFrostFSException(
String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, Service.PutRequest.class.getName())
);
}
requestObserver.onNext(request);
}
public Service.PatchResponse complete() {
requestObserver.onCompleted();
while (isNull(responseObserver.getResponse())) {
WaitUtil.sleep(DEFAULT_POLL_INTERVAL);
}
return responseObserver.getResponse();
}
@Getter
private static class PatchResponseCallback implements StreamObserver<Service.PatchResponse> {
private Service.PatchResponse response;
@Override
public void onNext(Service.PatchResponse patchResponse) {
this.response = patchResponse;
}
@Override
public void onError(Throwable throwable) {
throw new ProcessFrostFSException(throwable);
}
@Override
public void onCompleted() {
}
}
}

View file

@ -0,0 +1,25 @@
package info.frostfs.sdk.services.impl.rwhelper;
import frostfs.object.Service;
import info.frostfs.sdk.tools.Verifier;
import java.util.Iterator;
public class RangeReader {
public Iterator<Service.GetRangeResponse> call;
public RangeReader(Iterator<Service.GetRangeResponse> call) {
this.call = call;
}
public byte[] readChunk() {
if (!call.hasNext()) {
return null;
}
var response = call.next();
Verifier.checkResponse(response);
return response.getBody().getChunk().toByteArray();
}
}

View file

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>frostfs-sdk-java</artifactId> <artifactId>frostfs-sdk-java</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</parent> </parent>
<artifactId>cryptography</artifactId> <artifactId>cryptography</artifactId>
@ -21,7 +21,7 @@
<dependency> <dependency>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>exceptions</artifactId> <artifactId>exceptions</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>

View file

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>frostfs-sdk-java</artifactId> <artifactId>frostfs-sdk-java</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</parent> </parent>
<artifactId>exceptions</artifactId> <artifactId>exceptions</artifactId>

View file

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>frostfs-sdk-java</artifactId> <artifactId>frostfs-sdk-java</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</parent> </parent>
<artifactId>models</artifactId> <artifactId>models</artifactId>
@ -21,17 +21,17 @@
<dependency> <dependency>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>cryptography</artifactId> <artifactId>cryptography</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>protos</artifactId> <artifactId>protos</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>exceptions</artifactId> <artifactId>exceptions</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -13,6 +13,7 @@ public class AppConst {
public static final int SHA256_HASH_LENGTH = 32; public static final int SHA256_HASH_LENGTH = 32;
public static final int UUID_BYTE_ARRAY_LENGTH = 16; public static final int UUID_BYTE_ARRAY_LENGTH = 16;
public static final int DEFAULT_GRPC_TIMEOUT = 5; public static final int DEFAULT_GRPC_TIMEOUT = 5;
public static final long DEFAULT_POLL_INTERVAL = 10;
private AppConst() { private AppConst() {
} }

View file

@ -0,0 +1,13 @@
package info.frostfs.sdk.dto.object.patch;
import info.frostfs.sdk.dto.container.ContainerId;
import info.frostfs.sdk.dto.object.ObjectId;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class Address {
private final ObjectId objectId;
private final ContainerId containerId;
}

View file

@ -0,0 +1,11 @@
package info.frostfs.sdk.dto.object.patch;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class Range {
private long offset;
private long length;
}

View file

@ -2,6 +2,10 @@ package info.frostfs.sdk.mappers.object;
import frostfs.object.Types; import frostfs.object.Types;
import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectAttribute;
import org.apache.commons.collections4.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Objects.isNull; import static java.util.Objects.isNull;
@ -10,6 +14,14 @@ public class ObjectAttributeMapper {
private ObjectAttributeMapper() { private ObjectAttributeMapper() {
} }
public static List<Types.Header.Attribute> toGrpcMessages(List<ObjectAttribute> attributes) {
if (CollectionUtils.isEmpty(attributes)) {
return null;
}
return attributes.stream().map(ObjectAttributeMapper::toGrpcMessage).collect(Collectors.toList());
}
public static Types.Header.Attribute toGrpcMessage(ObjectAttribute attribute) { public static Types.Header.Attribute toGrpcMessage(ObjectAttribute attribute) {
if (isNull(attribute)) { if (isNull(attribute)) {
return null; return null;

View file

@ -0,0 +1,25 @@
package info.frostfs.sdk.mappers.object.patch;
import frostfs.refs.Types;
import info.frostfs.sdk.dto.object.patch.Address;
import info.frostfs.sdk.mappers.container.ContainerIdMapper;
import info.frostfs.sdk.mappers.object.ObjectIdMapper;
import static java.util.Objects.isNull;
public class AddressMapper {
private AddressMapper() {
}
public static Types.Address toGrpcMessage(Address address) {
if (isNull(address)) {
return null;
}
return Types.Address.newBuilder()
.setContainerId(ContainerIdMapper.toGrpcMessage(address.getContainerId()))
.setObjectId(ObjectIdMapper.toGrpcMessage(address.getObjectId()))
.build();
}
}

View file

@ -0,0 +1,35 @@
package info.frostfs.sdk.mappers.object.patch;
import frostfs.object.Service;
import info.frostfs.sdk.dto.object.patch.Range;
import org.apache.commons.collections4.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Objects.isNull;
public class RangeMapper {
private RangeMapper() {
}
public static List<Service.Range> toGrpcMessages(List<Range> ranges) {
if (CollectionUtils.isEmpty(ranges)) {
return null;
}
return ranges.stream().map(RangeMapper::toGrpcMessage).collect(Collectors.toList());
}
public static Service.Range toGrpcMessage(Range range) {
if (isNull(range)) {
return null;
}
return Service.Range.newBuilder()
.setOffset(range.getOffset())
.setLength(range.getLength())
.build();
}
}

View file

@ -4,10 +4,39 @@ import frostfs.object.Types;
import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectAttribute;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
public class ObjectAttributeMapperTest { public class ObjectAttributeMapperTest {
@Test
void toGrpcMessages_success() {
//Given
var objectAttribute1 = new ObjectAttribute("key1", "value1");
var objectAttribute2 = new ObjectAttribute("key2", "value2");
//When
var result = ObjectAttributeMapper.toGrpcMessages(Arrays.asList(objectAttribute1, objectAttribute2));
//Then
assertNotNull(result);
assertThat(result).isNotNull().hasSize(2);
assertEquals(objectAttribute1.getKey(), result.get(0).getKey());
assertEquals(objectAttribute1.getValue(), result.get(0).getValue());
assertEquals(objectAttribute2.getKey(), result.get(1).getKey());
assertEquals(objectAttribute2.getValue(), result.get(1).getValue());
}
@Test
void toGrpcMessages_null() {
//When + Then
assertNull(ObjectAttributeMapper.toGrpcMessages(null));
assertNull(ObjectAttributeMapper.toGrpcMessages(Collections.emptyList()));
}
@Test @Test
void toGrpcMessage_success() { void toGrpcMessage_success() {
//Given //Given

View file

@ -0,0 +1,39 @@
package info.frostfs.sdk.mappers.object.patch;
import info.frostfs.sdk.dto.container.ContainerId;
import info.frostfs.sdk.dto.object.ObjectId;
import info.frostfs.sdk.dto.object.patch.Address;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class AddressMapperTest {
@Test
void toGrpcMessage_success() {
//Given
var objectId = new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww");
var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R");
var address = new Address(objectId, containerId);
//When
var result = AddressMapper.toGrpcMessage(address);
//Then
assertNotNull(result);
assertEquals(
address.getContainerId().getValue(),
new ContainerId(result.getContainerId().getValue().toByteArray()).getValue()
);
assertEquals(
address.getObjectId().getValue(),
new ObjectId(result.getObjectId().getValue().toByteArray()).getValue()
);
}
@Test
void toGrpcMessage_null() {
//When + Then
assertNull(AddressMapper.toGrpcMessage(null));
}
}

View file

@ -0,0 +1,58 @@
package info.frostfs.sdk.mappers.object.patch;
import info.frostfs.sdk.dto.object.patch.Range;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
public class RangeMapperTest {
@Test
void toGrpcMessages_success() {
//Given
var range1 = new Range(1, 10);
var range2 = new Range(2, 20);
//When
var result = RangeMapper.toGrpcMessages(Arrays.asList(range1, range2));
//Then
assertNotNull(result);
assertThat(result).isNotNull().hasSize(2);
assertEquals(range1.getOffset(), result.get(0).getOffset());
assertEquals(range1.getLength(), result.get(0).getLength());
assertEquals(range2.getOffset(), result.get(1).getOffset());
assertEquals(range2.getLength(), result.get(1).getLength());
}
@Test
void toGrpcMessages_null() {
//When + Then
assertNull(RangeMapper.toGrpcMessages(null));
assertNull(RangeMapper.toGrpcMessages(Collections.emptyList()));
}
@Test
void toGrpcMessage_success() {
//Given
var range = new Range(1, 10);
//When
var result = RangeMapper.toGrpcMessage(range);
//Then
assertNotNull(result);
assertEquals(range.getOffset(), result.getOffset());
assertEquals(range.getLength(), result.getLength());
}
@Test
void toGrpcMessage_null() {
//When + Then
assertNull(RangeMapper.toGrpcMessage(null));
}
}

View file

@ -6,7 +6,7 @@
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>frostfs-sdk-java</artifactId> <artifactId>frostfs-sdk-java</artifactId>
<version>0.3.0</version> <version>${revision}</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>client</module> <module>client</module>
@ -17,6 +17,8 @@
</modules> </modules>
<properties> <properties>
<revision>0.4.0</revision>
<maven.compiler.source>11</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View file

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>info.frostfs.sdk</groupId> <groupId>info.frostfs.sdk</groupId>
<artifactId>frostfs-sdk-java</artifactId> <artifactId>frostfs-sdk-java</artifactId>
<version>0.3.0</version> <version>${revision}</version>
</parent> </parent>
<artifactId>protos</artifactId> <artifactId>protos</artifactId>