private static FlowFuture<BookingRes> cancel(String cancelFn, Object input, Throwable e) { Flows.currentFlow().invokeFunction(cancelFn, input, BookingRes.class); return Flows.currentFlow().failedFuture(e); }
@Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { switch (type) { case StageTimeout: return new StageTimeoutException(message); case StageLost: return new StageLostException(message); case StageFailed: return new StageInvokeFailedException(message); case FunctionTimeout: return new FunctionTimeoutException(message); case FunctionInvokeFailed: return new FunctionInvokeFailedException(message); case InvalidStageResponse: return new InvalidStageResponseException(message); default: return new PlatformException(message); } }
/** * Invoke a function by ID with headers and an empty body * <p> * * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName` * @param method HTTP method to invoke function * @param headers Headers to add to the HTTP request representing the function invocation * @return a future which completes normally if the function succeeded and fails if it fails * @see #invokeFunction(String, HttpMethod, Headers, byte[]) */ default FlowFuture<HttpResponse> invokeFunction(String functionId, HttpMethod method, Headers headers) { return invokeFunction(functionId, method, headers, new byte[]{}); }
public Response handleRequest(byte[] imageBuffer) { String id = java.util.UUID.randomUUID().toString(); Flow runtime = Flows.currentFlow(); runtime.allOf( runtime.invokeFunction(resize128ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer) .thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-128.png")), runtime.invokeFunction(resize256ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer) .thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-256.png")), runtime.invokeFunction(resize512ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer) .thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-512.png")), runtime.supply(() -> objectUpload(imageBuffer, id + ".png")) ); return new Response(id); }
public void book2(TripReq input) { Flow f = Flows.currentFlow(); FlowFuture<BookingRes> flightFuture = f.invokeFunction("./flight/book", input.flight, BookingRes.class); FlowFuture<BookingRes> hotelFuture = f.invokeFunction("./hotel/book", input.hotel, BookingRes.class); FlowFuture<BookingRes> carFuture = f.invokeFunction("./car/book", input.carRental, BookingRes.class); flightFuture.thenCompose( (flightRes) -> hotelFuture.thenCompose( (hotelRes) -> carFuture.whenComplete( (carRes, e) -> EmailReq.sendSuccessMail(flightRes, hotelRes, carRes) ) .exceptionallyCompose( (e) -> cancel("./car/cancel", input.carRental, e) ) ) .exceptionallyCompose( (e) -> cancel("./hotel/cancel", input.hotel, e) ) ) .exceptionallyCompose( (e) -> cancel("./flight/cancel", input.flight, e) ) .exceptionally( (err) -> {EmailReq.sendFailEmail(); return null;} ); }
public void book1(TripReq input) { Flow f = Flows.currentFlow(); FlowFuture<BookingRes> flightFuture = f.invokeFunction("./flight/book", input.flight, BookingRes.class); FlowFuture<BookingRes> hotelFuture = f.invokeFunction("./hotel/book", input.hotel, BookingRes.class); FlowFuture<BookingRes> carFuture = f.invokeFunction("./car/book", input.carRental, BookingRes.class); flightFuture.thenCompose( (flightRes) -> hotelFuture.thenCompose( (hotelRes) -> carFuture.whenComplete( (carRes, e) -> EmailReq.sendSuccessMail(flightRes, hotelRes, carRes) ) ) ); }
public static void sendSuccessMail(BookingRes flightRes, BookingRes hotelRes, BookingRes carRes) { EmailReq message = composeSuccessEmail(flightRes, hotelRes, carRes); Flows.currentFlow().invokeFunction("./email", message); } }
public FunctionInvocationException(HttpResponse functionResponse) { super(new String(functionResponse.getBodyAsBytes())); this.functionResponse = functionResponse; }
private static byte[] serializeClosure(Object data) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { oos.writeObject(data); oos.close(); return bos.toByteArray(); } catch (NotSerializableException nse) { throw new LambdaSerializationException("Closure not serializable", nse); } catch (IOException e) { throw new PlatformException("Failed to write closure", e); } }
@Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { return ((RemoteFlow) Flows.currentFlow()).createFlowFuture(new CompletionId(stageId)); } }
private <T> T withStage(CompletionId cid, Function<Stage, T> function) { Stage stage = stages.get(cid); if (stage == null) { throw new PlatformException("Stage not found in graph :" + cid); } return function.apply(stage); }
@Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { HttpResponse resp = new RemoteHTTPResponse(flowId, this.resp, blobStore); if (successful) { return resp; } else { return new FunctionInvocationException(resp); } }
/** * Return the current flow runtime - this will create a new flow if the current is not already bound to the invocation or * an existing flow if one is already bound to the current invocation or if the invocation was triggered from within a flow stage. * * @return the current flow runtime */ public synchronized static Flow currentFlow() { Objects.requireNonNull(flowSource, "Flows.flowSource is not set - Flows.currentFlow() is the @FnFeature(FlowFeature.class) annotation set on your function?"); return flowSource.currentFlow(); }
public WrappedFunctionException(Throwable cause){ super(cause.getMessage()); this.setStackTrace(cause.getStackTrace()); this.originalExceptionType = cause.getClass(); }
public void book3(TripReq input) { Flow f = Flows.currentFlow(); FlowFuture<BookingRes> flightFuture = f.invokeFunction("./flight/book", input.flight, BookingRes.class); FlowFuture<BookingRes> hotelFuture = f.invokeFunction("./hotel/book", input.hotel, BookingRes.class); FlowFuture<BookingRes> carFuture = f.invokeFunction("./car/book", input.carRental, BookingRes.class); flightFuture.thenCompose( (flightRes) -> hotelFuture.thenCompose( (hotelRes) -> carFuture.whenComplete( (carRes, e) -> EmailReq.sendSuccessMail(flightRes, hotelRes, carRes) ) .exceptionallyCompose( (e) -> retryCancel("./car/cancel", input.carRental, e) ) ) .exceptionallyCompose( (e) -> retryCancel("./hotel/cancel", input.hotel, e) ) ) .exceptionallyCompose( (e) -> retryCancel("./flight/cancel", input.flight, e) ) .exceptionally( (err) -> {EmailReq.sendFailEmail(); return null;} ); }
private static FlowFuture<BookingRes> retryCancel(String cancelFn, Object input, Throwable e) { Retry.exponentialWithJitter( () -> Flows.currentFlow().invokeFunction(cancelFn, input, BookingRes.class)); return Flows.currentFlow().failedFuture(e); } }
public static void sendFailEmail() { Flows.currentFlow().invokeFunction("./email", composeFailEmail()); }
/** * Invoke a function by ID with no headers * <p> * * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName` * @param method HTTP method to invoke function * @return a future which completes normally if the function succeeded and fails if it fails * @see #invokeFunction(String, HttpMethod, Headers, byte[]) */ default FlowFuture<HttpResponse> invokeFunction(String functionId, HttpMethod method) { return invokeFunction(functionId, method, Headers.emptyHeaders(), new byte[]{}); }
/** * Invoke a function by ID using input and output coercion * <p> * This currently only maps to JSON via the default JSON mapper in the FDK * * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName` * @param input The input object to send to the function input * @param responseType The expected response type of the target function * @param <T> The Response type * @param <U> The Input type of the function * @return a flow future that completes with the result of the function, or an error if the function invocation failed * @throws IllegalArgumentException if the input cannot be coerced to the callA */ default <T extends Serializable, U> FlowFuture<T> invokeFunction(String functionId, U input, Class<T> responseType) { return invokeFunction(functionId, HttpMethod.POST, Headers.emptyHeaders(), input, responseType); }
/** * Invoke a function by ID using input and output coercion, default method (POST) and no response type * <p> * Returns a future that completes with the HttpResponse of the function on success * if the function returns a successful http response, and completes with an {@link FunctionInvocationException} if the function invocation fails with a non-succesful http status * <p> * This currently only maps to JSON via the default JSON mapper in the FDK * * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName` * @param input The input object to send to the function input * @param <U> The Input type of the function * @return a flow future that completes with the result of the function, or an error if the function invocation failed * @throws IllegalArgumentException if the input cannot be coerced to the call */ default <U> FlowFuture<HttpResponse> invokeFunction(String functionId, U input) { return invokeFunction(functionId, HttpMethod.POST, Headers.emptyHeaders(), input); }