diff --git a/client/pom.xml b/client/pom.xml
index f907964..2d87b21 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -6,7 +6,7 @@
info.frostfs.sdk
frostfs-sdk-java
- 0.3.0
+ ${revision}
client
@@ -21,17 +21,17 @@
info.frostfs.sdk
cryptography
- 0.3.0
+ ${revision}
info.frostfs.sdk
models
- 0.3.0
+ ${revision}
info.frostfs.sdk
exceptions
- 0.3.0
+ ${revision}
commons-codec
diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java
index 31a76b7..e6704ed 100644
--- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java
+++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java
@@ -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.PrmContainerGetAll;
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.result.ObjectHeaderResult;
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.MonitoringClientInterceptor;
import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter;
+import info.frostfs.sdk.services.impl.rwhelper.RangeReader;
import info.frostfs.sdk.utils.Validator;
import io.grpc.Channel;
import io.grpc.ClientInterceptors;
@@ -164,6 +168,21 @@ public class FrostFSClient implements CommonClient {
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
public byte[] addChain(PrmApeChainAdd args, CallContext ctx) {
return apeManagerClient.addChain(args, ctx);
@@ -213,6 +232,7 @@ public class FrostFSClient implements CommonClient {
return accountingClient.getBalance(ctx);
}
+ @Override
public String dial(CallContext ctx) {
accountingClient.getBalance(ctx);
return null;
diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java
new file mode 100644
index 0000000..9f93c9a
--- /dev/null
+++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java
@@ -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 newAttributes;
+ private boolean replaceAttributes;
+ private int maxChunkLength;
+ private SessionToken sessionToken;
+ private Map xHeaders;
+
+ public PrmObjectPatch(Address address, Range range, InputStream payload, int maxChunkLength) {
+ this.address = address;
+ this.range = range;
+ this.payload = payload;
+ this.maxChunkLength = maxChunkLength;
+ }
+}
diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeGet.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeGet.java
new file mode 100644
index 0000000..add15b7
--- /dev/null
+++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeGet.java
@@ -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 xHeaders;
+
+ public PrmRangeGet(ContainerId containerId, ObjectId objectId, Range range) {
+ this.containerId = containerId;
+ this.objectId = objectId;
+ this.range = range;
+ }
+}
diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeHashGet.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeHashGet.java
new file mode 100644
index 0000000..d860133
--- /dev/null
+++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeHashGet.java
@@ -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 ranges;
+ @NotNull
+ private byte[] salt;
+
+ private SessionToken sessionToken;
+ private Map xHeaders;
+
+ public PrmRangeHashGet(ContainerId containerId, ObjectId objectId, List ranges, byte[] salt) {
+ this.containerId = containerId;
+ this.objectId = objectId;
+ this.ranges = ranges;
+ this.salt = salt;
+ }
+}
diff --git a/client/src/main/java/info/frostfs/sdk/pool/Pool.java b/client/src/main/java/info/frostfs/sdk/pool/Pool.java
index 74143a2..45b818e 100644
--- a/client/src/main/java/info/frostfs/sdk/pool/Pool.java
+++ b/client/src/main/java/info/frostfs/sdk/pool/Pool.java
@@ -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.PrmContainerGetAll;
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.pool.NodeParameters;
import info.frostfs.sdk.jdo.pool.PoolInitParameters;
import info.frostfs.sdk.jdo.result.ObjectHeaderResult;
import info.frostfs.sdk.services.CommonClient;
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.WaitUtil;
import lombok.Getter;
@@ -198,6 +202,7 @@ public class Pool implements CommonClient {
return address + key;
}
+ @Override
public String dial(CallContext ctx) {
InnerPool[] inner = new InnerPool[rebalanceParams.getNodesParams().length];
boolean atLeastOneHealthy = false;
@@ -479,6 +484,24 @@ public class Pool implements CommonClient {
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
public byte[] addChain(PrmApeChainAdd args, CallContext ctx) {
ClientWrapper client = connection();
diff --git a/client/src/main/java/info/frostfs/sdk/services/CommonClient.java b/client/src/main/java/info/frostfs/sdk/services/CommonClient.java
index e0482fb..ce0dcba 100644
--- a/client/src/main/java/info/frostfs/sdk/services/CommonClient.java
+++ b/client/src/main/java/info/frostfs/sdk/services/CommonClient.java
@@ -1,5 +1,9 @@
package info.frostfs.sdk.services;
+import info.frostfs.sdk.jdo.parameters.CallContext;
+
public interface CommonClient extends
AccountingClient, ApeManagerClient, ContainerClient, NetmapClient, ObjectClient, SessionClient, ToolsClient {
+
+ String dial(CallContext ctx);
}
diff --git a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java
index 86901b5..5e8362c 100644
--- a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java
+++ b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java
@@ -4,8 +4,12 @@ import info.frostfs.sdk.dto.object.ObjectFrostFS;
import info.frostfs.sdk.dto.object.ObjectId;
import info.frostfs.sdk.jdo.parameters.CallContext;
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.services.impl.rwhelper.ObjectWriter;
+import info.frostfs.sdk.services.impl.rwhelper.RangeReader;
public interface ObjectClient {
ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx);
@@ -21,4 +25,10 @@ public interface ObjectClient {
void deleteObject(PrmObjectDelete args, CallContext ctx);
Iterable searchObjects(PrmObjectSearch args, CallContext ctx);
+
+ RangeReader getRange(PrmRangeGet args, CallContext ctx);
+
+ byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx);
+
+ ObjectId patchObject(PrmObjectPatch args, CallContext ctx);
}
diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java
index 203d1e6..266b630 100644
--- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java
+++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java
@@ -14,16 +14,18 @@ import info.frostfs.sdk.jdo.ClientEnvironment;
import info.frostfs.sdk.jdo.PutObjectResult;
import info.frostfs.sdk.jdo.parameters.CallContext;
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.result.ObjectHeaderResult;
import info.frostfs.sdk.mappers.container.ContainerIdMapper;
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.ObjectClient;
-import info.frostfs.sdk.services.impl.rwhelper.ObjectReaderImpl;
-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.services.impl.rwhelper.*;
import info.frostfs.sdk.tools.RequestConstructor;
import info.frostfs.sdk.tools.Verifier;
import org.apache.commons.collections4.CollectionUtils;
@@ -200,6 +202,77 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient {
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) {
var reader = getObjectInit(request, ctx);
@@ -371,7 +444,6 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient {
}
private Service.GetRequest createGetRequest(PrmObjectGet args, CallContext ctx) {
-
var address = Types.Address.newBuilder()
.setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId()))
.setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId()))
@@ -510,4 +582,70 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient {
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);
+ }
}
diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java
index 0556fb5..cadfbf1 100644
--- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java
+++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java
@@ -7,11 +7,11 @@ 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 ObjectStreamer {
- private static final long POLL_INTERVAL = 10;
private final StreamObserver requestObserver;
private final PutResponseCallback responseObserver;
@@ -35,7 +35,7 @@ public class ObjectStreamer {
requestObserver.onCompleted();
while (isNull(responseObserver.getResponse())) {
- WaitUtil.sleep(POLL_INTERVAL);
+ WaitUtil.sleep(DEFAULT_POLL_INTERVAL);
}
return responseObserver.getResponse();
diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/PatchStreamer.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/PatchStreamer.java
new file mode 100644
index 0000000..4cdee35
--- /dev/null
+++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/PatchStreamer.java
@@ -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 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 {
+ 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() {
+ }
+ }
+}
diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/RangeReader.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/RangeReader.java
new file mode 100644
index 0000000..75c4c64
--- /dev/null
+++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/RangeReader.java
@@ -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 call;
+
+ public RangeReader(Iterator call) {
+ this.call = call;
+ }
+
+ public byte[] readChunk() {
+ if (!call.hasNext()) {
+ return null;
+ }
+
+ var response = call.next();
+ Verifier.checkResponse(response);
+
+ return response.getBody().getChunk().toByteArray();
+ }
+}
diff --git a/cryptography/pom.xml b/cryptography/pom.xml
index ac56a86..e9e2c6b 100644
--- a/cryptography/pom.xml
+++ b/cryptography/pom.xml
@@ -6,7 +6,7 @@
info.frostfs.sdk
frostfs-sdk-java
- 0.3.0
+ ${revision}
cryptography
@@ -21,7 +21,7 @@
info.frostfs.sdk
exceptions
- 0.3.0
+ ${revision}
com.google.protobuf
diff --git a/exceptions/pom.xml b/exceptions/pom.xml
index 4cb8bcc..c3b4546 100644
--- a/exceptions/pom.xml
+++ b/exceptions/pom.xml
@@ -6,7 +6,7 @@
info.frostfs.sdk
frostfs-sdk-java
- 0.3.0
+ ${revision}
exceptions
diff --git a/models/pom.xml b/models/pom.xml
index 32cade4..e913463 100644
--- a/models/pom.xml
+++ b/models/pom.xml
@@ -6,7 +6,7 @@
info.frostfs.sdk
frostfs-sdk-java
- 0.3.0
+ ${revision}
models
@@ -21,17 +21,17 @@
info.frostfs.sdk
cryptography
- 0.3.0
+ ${revision}
info.frostfs.sdk
protos
- 0.3.0
+ ${revision}
info.frostfs.sdk
exceptions
- 0.3.0
+ ${revision}
diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java
index 35d64c6..feb1ca5 100644
--- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java
+++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java
@@ -13,6 +13,7 @@ public class AppConst {
public static final int SHA256_HASH_LENGTH = 32;
public static final int UUID_BYTE_ARRAY_LENGTH = 16;
public static final int DEFAULT_GRPC_TIMEOUT = 5;
+ public static final long DEFAULT_POLL_INTERVAL = 10;
private AppConst() {
}
diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/patch/Address.java b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Address.java
new file mode 100644
index 0000000..ae30880
--- /dev/null
+++ b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Address.java
@@ -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;
+}
diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/patch/Range.java b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Range.java
new file mode 100644
index 0000000..67a56aa
--- /dev/null
+++ b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Range.java
@@ -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;
+}
diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java
index 687aa86..f29c284 100644
--- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java
+++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java
@@ -2,6 +2,10 @@ package info.frostfs.sdk.mappers.object;
import frostfs.object.Types;
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;
@@ -10,6 +14,14 @@ public class ObjectAttributeMapper {
private ObjectAttributeMapper() {
}
+ public static List toGrpcMessages(List attributes) {
+ if (CollectionUtils.isEmpty(attributes)) {
+ return null;
+ }
+
+ return attributes.stream().map(ObjectAttributeMapper::toGrpcMessage).collect(Collectors.toList());
+ }
+
public static Types.Header.Attribute toGrpcMessage(ObjectAttribute attribute) {
if (isNull(attribute)) {
return null;
diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java
new file mode 100644
index 0000000..1510b7a
--- /dev/null
+++ b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java
@@ -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();
+ }
+}
diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java
new file mode 100644
index 0000000..f428545
--- /dev/null
+++ b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java
@@ -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 toGrpcMessages(List 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();
+ }
+}
diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java
index 4ae2902..160af7a 100644
--- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java
+++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java
@@ -4,10 +4,39 @@ import frostfs.object.Types;
import info.frostfs.sdk.dto.object.ObjectAttribute;
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 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
void toGrpcMessage_success() {
//Given
diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/patch/AddressMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/AddressMapperTest.java
new file mode 100644
index 0000000..b53ff87
--- /dev/null
+++ b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/AddressMapperTest.java
@@ -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));
+ }
+}
diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java
new file mode 100644
index 0000000..771a9ac
--- /dev/null
+++ b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java
@@ -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));
+ }
+}
diff --git a/pom.xml b/pom.xml
index 72eb9eb..d390c12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
info.frostfs.sdk
frostfs-sdk-java
- 0.3.0
+ ${revision}
pom
client
@@ -17,6 +17,8 @@
+ 0.4.0
+
11
11
UTF-8
diff --git a/protos/pom.xml b/protos/pom.xml
index d3a2b3b..f5ea90a 100644
--- a/protos/pom.xml
+++ b/protos/pom.xml
@@ -6,7 +6,7 @@
info.frostfs.sdk
frostfs-sdk-java
- 0.3.0
+ ${revision}
protos