org.hyperledger.fabric.client.GatewayClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fabric-gateway Show documentation
Show all versions of fabric-gateway Show documentation
Hyperledger Fabric Gateway client API for Java
The newest version!
/*
* Copyright 2021 IBM All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.fabric.client;
import com.google.protobuf.InvalidProtocolBufferException;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.Context;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedTransferQueue;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import org.hyperledger.fabric.protos.common.Envelope;
import org.hyperledger.fabric.protos.gateway.ChaincodeEventsResponse;
import org.hyperledger.fabric.protos.gateway.CommitStatusRequest;
import org.hyperledger.fabric.protos.gateway.CommitStatusResponse;
import org.hyperledger.fabric.protos.gateway.EndorseRequest;
import org.hyperledger.fabric.protos.gateway.EndorseResponse;
import org.hyperledger.fabric.protos.gateway.EvaluateRequest;
import org.hyperledger.fabric.protos.gateway.EvaluateResponse;
import org.hyperledger.fabric.protos.gateway.GatewayGrpc;
import org.hyperledger.fabric.protos.gateway.SignedChaincodeEventsRequest;
import org.hyperledger.fabric.protos.gateway.SignedCommitStatusRequest;
import org.hyperledger.fabric.protos.gateway.SubmitRequest;
import org.hyperledger.fabric.protos.gateway.SubmitResponse;
import org.hyperledger.fabric.protos.peer.DeliverGrpc;
import org.hyperledger.fabric.protos.peer.DeliverResponse;
final class GatewayClient {
private final GatewayGrpc.GatewayBlockingStub gatewayBlockingStub;
private final DeliverGrpc.DeliverStub deliverAsyncStub;
private final DefaultCallOptions defaultOptions;
GatewayClient(final Channel channel, final DefaultCallOptions defaultOptions) {
GatewayUtils.requireNonNullArgument(channel, "No connection details supplied");
GatewayUtils.requireNonNullArgument(defaultOptions, "defaultOptions");
this.gatewayBlockingStub = GatewayGrpc.newBlockingStub(channel);
this.deliverAsyncStub = DeliverGrpc.newStub(channel);
this.defaultOptions = defaultOptions;
}
public EvaluateResponse evaluate(final EvaluateRequest request, final UnaryOperator options)
throws GatewayException {
GatewayGrpc.GatewayBlockingStub stub = defaultOptions.applyEvaluate(gatewayBlockingStub, options);
try {
return stub.evaluate(request);
} catch (StatusRuntimeException e) {
throw new GatewayException(e);
}
}
public EndorseResponse endorse(final EndorseRequest request, final UnaryOperator options)
throws EndorseException {
GatewayGrpc.GatewayBlockingStub stub = defaultOptions.applyEndorse(gatewayBlockingStub, options);
try {
return stub.endorse(request);
} catch (StatusRuntimeException e) {
throw new EndorseException(request.getTransactionId(), e);
}
}
public SubmitResponse submit(final SubmitRequest request, final UnaryOperator options)
throws SubmitException {
GatewayGrpc.GatewayBlockingStub stub = defaultOptions.applySubmit(gatewayBlockingStub, options);
try {
return stub.submit(request);
} catch (StatusRuntimeException e) {
throw new SubmitException(request.getTransactionId(), e);
}
}
public CommitStatusResponse commitStatus(
final SignedCommitStatusRequest request, final UnaryOperator options)
throws CommitStatusException {
GatewayGrpc.GatewayBlockingStub stub = defaultOptions.applyCommitStatus(gatewayBlockingStub, options);
try {
return stub.commitStatus(request);
} catch (StatusRuntimeException e) {
try {
CommitStatusRequest req = CommitStatusRequest.parseFrom(request.getRequest());
throw new CommitStatusException(req.getTransactionId(), e);
} catch (InvalidProtocolBufferException protoErr) {
// Should never happen
CommitStatusException commitErr = new CommitStatusException("", e);
commitErr.addSuppressed(protoErr);
throw commitErr;
}
}
}
public CloseableIterator chaincodeEvents(
final SignedChaincodeEventsRequest request, final UnaryOperator options) {
GatewayGrpc.GatewayBlockingStub stub = defaultOptions.applyChaincodeEvents(gatewayBlockingStub, options);
return invokeServerStreamingCall(() -> stub.chaincodeEvents(request));
}
public CloseableIterator blockEvents(
final Envelope request, final UnaryOperator options) {
DeliverGrpc.DeliverStub stub = defaultOptions.applyBlockEvents(deliverAsyncStub, options);
return invokeDuplexStreamingCall(stub::deliver, request);
}
public CloseableIterator filteredBlockEvents(
final Envelope request, final UnaryOperator options) {
DeliverGrpc.DeliverStub stub = defaultOptions.applyFilteredBlockEvents(deliverAsyncStub, options);
return invokeDuplexStreamingCall(stub::deliverFiltered, request);
}
public CloseableIterator blockAndPrivateDataEvents(
final Envelope request, final UnaryOperator options) {
DeliverGrpc.DeliverStub stub = defaultOptions.applyBlockAndPrivateDataEvents(deliverAsyncStub, options);
return invokeDuplexStreamingCall(stub::deliverWithPrivateData, request);
}
private CloseableIterator invokeServerStreamingCall(final Supplier> call) {
Context.CancellableContext context = Context.current().withCancellation();
return invokeStreamingCall(context, call);
}
@SuppressWarnings({"PMD.AvoidCatchingGenericException", "PMD.AvoidCatchingGenericException"})
private CloseableIterator invokeStreamingCall(
final Context.CancellableContext context, final Supplier> call) {
try {
Iterator iterator = context.wrap(call::get).call();
return new ResponseIterator<>(context, iterator);
} catch (StatusRuntimeException e) {
context.cancel(e);
throw new GatewayRuntimeException(e);
} catch (RuntimeException e) {
context.cancel(e);
throw e;
} catch (Exception e) {
// Should never happen calling a Supplier
context.cancel(e);
throw new AssertionError(e);
}
}
private static final class ResponseIterator implements CloseableIterator {
private final Context.CancellableContext context;
private final Iterator iterator;
ResponseIterator(final Context.CancellableContext context, final Iterator iterator) {
this.context = context;
this.iterator = iterator;
}
@Override
public void close() {
context.close();
}
@Override
public boolean hasNext() {
try {
return iterator.hasNext();
} catch (StatusRuntimeException e) {
throw new GatewayRuntimeException(e);
}
}
@Override
public T next() {
try {
return iterator.next();
} catch (StatusRuntimeException e) {
throw new GatewayRuntimeException(e);
}
}
}
@SuppressWarnings("PMD.GenericsNaming")
private CloseableIterator invokeDuplexStreamingCall(
final Function, StreamObserver> call, final ReqT request) {
ResponseObserver responseObserver = new ResponseObserver<>();
Context.CancellableContext context = Context.current().withCancellation();
// Complete response observer synchronously if client cancels the context
context.addListener(context1 -> responseObserver.onCompleted(), Runnable::run);
return invokeStreamingCall(context, () -> {
StreamObserver requestObserver = call.apply(responseObserver);
requestObserver.onNext(request);
return responseObserver;
});
}
private static final class ResponseObserver implements StreamObserver, Iterator {
@SuppressWarnings("PMD.LooseCoupling")
private final LinkedTransferQueue> queue = new LinkedTransferQueue<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private Supplier next;
@Override
public void onNext(final T response) {
Future> future = executor.submit(() -> transfer(response));
try {
future.get();
} catch (CancellationException | InterruptedException ignored) {
// Ignore cancellation
} catch (ExecutionException e) {
// Should never happen
throw new AssertionError(e);
}
}
private void transfer(final T response) {
try {
queue.transfer(() -> response);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt(); // Preserve interrupt status
}
}
@Override
public void onError(final Throwable t) {
final StatusRuntimeException err;
if (t instanceof StatusRuntimeException) {
err = (StatusRuntimeException) t;
} else {
err = io.grpc.Status.fromThrowable(t).asRuntimeException();
}
queue.put(() -> {
throw err;
});
}
@Override
public void onCompleted() {
queue.put(() -> null); // Queue close marker to ensure consumers are not blocked
List liveTasks = executor.shutdownNow().stream()
.filter(waitingTask -> {
if (!(waitingTask instanceof Future)) {
return true;
}
Future> future = (Future>) waitingTask;
future.cancel(true);
return !future.isCancelled();
})
.collect(Collectors.toList());
if (!liveTasks.isEmpty()) {
throw new IllegalStateException("Failed to cancel tasks: " + liveTasks);
}
}
@Override
public boolean hasNext() {
return readNext().get() != null;
}
@Override
@SuppressWarnings("PMD.NullAssignment")
public T next() {
T result = readNext().get();
if (result == null) {
throw new NoSuchElementException();
}
next = null;
return result;
}
private Supplier readNext() {
if (next == null) {
try {
next = queue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
next = () -> null;
}
}
return next;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy