@Override public RpcController newRpcController() { return new ClientRpcController(this); }
@Override public boolean failed() { checkStatus(Status.COMPLETE); return state.failed; }
@Override public String errorText() { return failed() ? state.errorText : null; }
@Override public void message(int sequenceNo, Message message) { final ClientRpcController controller; synchronized (activeMethodMap) { controller = activeMethodMap.get(sequenceNo); // TODO: remove controller from activeMethodMap } if (message instanceof Rpc.RpcFinished) { Rpc.RpcFinished finished = (Rpc.RpcFinished) message; if (finished.getFailed()) { controller.failure(finished.getErrorText()); } else { controller.response(null); } } else { controller.response(message); } } };
@Override public void startCancel() { Status status = status(); if (status == Status.PENDING) { throw new IllegalStateException("Can't cancel this RPC, not currently active."); } else if (status == Status.COMPLETE) { // We drop these requests silently - since there is no way for the client // to know whether the RPC has finished while they are setting up their // cancellation. } else { state.cancelRpc.run(); } } }
controller.configure(rpcStatus); synchronized (activeMethodMap) { activeMethodMap.put(sequenceNo, controller);
/** * Assert that this controller is in the given status. */ private void checkStatus(Status statusToAssert) { Status currentStatus = status(); if (!currentStatus.equals(statusToAssert)) { throw new IllegalStateException("Controller expected status " + statusToAssert + ", was " + currentStatus); } }
@Override public void reset() { checkStatus(Status.COMPLETE); state = null; }
/** * Configure this RpcController with a new RpcStatus instance. */ void configure(RpcState state) { checkStatus(Status.PENDING); if (this.state != null) { throw new IllegalStateException("Can't configure this RPC, already configured."); } else if (!owner.equals(state.creator)) { throw new IllegalArgumentException("Should only be configured by " + owner + ", configuration attempted by " + state.creator); } this.state = state; }
/** * Indicate that the RPC has failed. This requires that the RPC is currently * active, and marks this RPC as complete. */ void failure(String errorText) { checkStatus(Status.ACTIVE); state.complete = true; state.failed = true; state.errorText = errorText; // Hint to the internal callback that this RPC is finished (Normal RPCs // will always understand this as an error case, whereas streaming RPCs // will have to check their controller). state.callback.run(null); }
/** * Provide a response to this RpcController. Intercepts valid completion * conditions in order to mark a RPC as complete. Passes through all messages * to the internal callback for the current RPC invocation. */ void response(Message message) { checkStatus(Status.ACTIVE); // Any message will complete a normal RPC, whereas only a null message will // end a streaming RPC. if (!state.isStreamingRpc) { if (message == null) { // The server end-point should not actually allow non-streaming RPCs // to call back with null messages - we should never get here. throw new IllegalStateException("Normal RPCs should not be completed early."); } else { // Normal RPCs will complete on any valid incoming message. state.complete = true; } } else if (message == null) { // Complete this streaming RPC with this blank message. state.complete = true; } try { state.callback.run(message); } catch (RuntimeException e) { e.printStackTrace(); } }