All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fnproject.fn.runtime.flow.APIModel Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.fnproject.fn.runtime.flow;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fnproject.fn.api.Headers;
import com.fnproject.fn.api.exception.FunctionInputHandlingException;
import com.fnproject.fn.api.flow.*;
import com.fnproject.fn.runtime.exception.PlatformCommunicationException;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.util.*;

/**
 * Created on 22/11/2017.
 * 

* (c) 2017 Oracle Corporation */ public class APIModel { enum CompletionOperation { @JsonProperty("unknown_operation") UNKNOWN_OPERATION, @JsonProperty("acceptEither") ACCEPT_EITHER, @JsonProperty("applyToEither") APPLY_TO_EITHER, @JsonProperty("thenAcceptBoth") THEN_ACCEPT_BOTH, @JsonProperty("thenApply") THEN_APPLY, @JsonProperty("thenRun") THEN_RUN, @JsonProperty("thenAccept") THEN_ACCEPT, @JsonProperty("thenCompose") THEN_COMPOSE, @JsonProperty("thenCombine") THEN_COMBINE, @JsonProperty("whenComplete") WHEN_COMPLETE, @JsonProperty("handle") HANDLE(), @JsonProperty("supply") SUPPLY(), @JsonProperty("invokeFunction") INVOKE_FUNCTION(), @JsonProperty("completedValue") COMPLETED_VALUE(), @JsonProperty("delay") DELAY(), @JsonProperty("allOf") ALL_OF(), @JsonProperty("anyOf") ANY_OF(), @JsonProperty("externalCompletion") EXTERNAL_COMPLETION(), @JsonProperty("exceptionally") EXCEPTIONALLY(), @JsonProperty("terminationHook") TERMINATION_HOOK(), @JsonProperty("exceptionallyCompose") EXCEPTIONALLY_COMPOSE(); CompletionOperation() { } } public static final class Blob { @JsonProperty("blob_id") public String blobId; @JsonProperty("length") public Long blobLength; @JsonProperty("content_type") public String contentType; public static Blob fromBlobResponse(BlobResponse blobResponse) { Blob blob= new Blob(); blob.blobId = blobResponse.blobId; blob.contentType = blobResponse.contentType; blob.blobLength = blobResponse.blobLength; return blob; } } public static class CompletionResult { @JsonProperty("datum") public Datum result; @JsonProperty("successful") public boolean successful; public Object toJava(FlowId flowId, BlobStoreClient blobClient, ClassLoader classLoader) { return result.toJava(successful, flowId, blobClient, classLoader); } public static CompletionResult failure(Datum datum) { CompletionResult result = new CompletionResult(); result.successful = false; result.result = datum; return result; } public static CompletionResult success(Datum datum) { CompletionResult result = new CompletionResult(); result.successful = true; result.result = datum; return result; } } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) @JsonSubTypes({ @JsonSubTypes.Type(name = "empty", value = EmptyDatum.class), @JsonSubTypes.Type(name = "blob", value = BlobDatum.class), @JsonSubTypes.Type(name = "stage_ref", value = StageRefDatum.class), @JsonSubTypes.Type(name = "error", value = ErrorDatum.class), @JsonSubTypes.Type(name = "http_req", value = HTTPReqDatum.class), @JsonSubTypes.Type(name = "http_resp", value = HTTPRespDatum.class), @JsonSubTypes.Type(name = "status", value = StatusDatum.class), }) public static abstract class Datum { public abstract Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader); } public static final class EmptyDatum extends Datum { @Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { return null; } } public static final class BlobDatum extends Datum { @JsonUnwrapped public Blob blob; @Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { return blobStore.readBlob(flowId.getId(), blob.blobId, (requestInputStream) -> { try (ObjectInputStream ois = new ObjectInputStream(requestInputStream) { @Override protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { String cname = classDesc.getName(); try { return classLoader.loadClass(cname); } catch (ClassNotFoundException ex) { return super.resolveClass(classDesc); } } }) { return ois.readObject(); } catch (ClassNotFoundException | InvalidClassException | StreamCorruptedException | OptionalDataException e) { throw new FunctionInputHandlingException("Error reading continuation content", e); } catch (IOException e) { throw new PlatformException("Error reading blob data", e); } }, blob.contentType); } public static BlobDatum fromBlob(Blob blob) { BlobDatum datum = new BlobDatum(); datum.blob = blob; return datum; } } public static final class StageRefDatum extends Datum { @JsonProperty("stage_id") public String stageId; @Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { return ((RemoteFlow) Flows.currentFlow()).createFlowFuture(new CompletionId(stageId)); } } public enum ErrorType { @JsonProperty("unknown_error") UnknownError(), @JsonProperty("stage_timeout") StageTimeout(), @JsonProperty("stage_failed") StageFailed(), @JsonProperty("function_timeout") FunctionTimeout(), @JsonProperty("function_invoke_failed") FunctionInvokeFailed(), @JsonProperty("stage_lost") StageLost(), @JsonProperty("invalid_stage_response") InvalidStageResponse(); } public static final class ErrorDatum extends Datum { @JsonProperty("type") public ErrorType type; @JsonProperty("message") public String message; @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); } } public static ErrorDatum newError(ErrorType type, String message) { ErrorDatum datum = new ErrorDatum(); datum.type = type; datum.message = message; return datum; } } public enum HTTPMethod { @JsonProperty("unknown_method") UnknownMethod(HttpMethod.GET), @JsonProperty("get") Get(HttpMethod.GET), @JsonProperty("head") Head(HttpMethod.HEAD), @JsonProperty("post") Post(HttpMethod.POST), @JsonProperty("put") Put(HttpMethod.PUT), @JsonProperty("delete") Delete(HttpMethod.DELETE), @JsonProperty("options") Options(HttpMethod.OPTIONS), @JsonProperty("patch") Patch(HttpMethod.PATCH); final HttpMethod flowMethod; HTTPMethod(HttpMethod flowMethod) { this.flowMethod = flowMethod; } public static HTTPMethod fromFlow(HttpMethod f) { return Arrays.stream(values()) .filter((x) -> x.flowMethod == f) .findFirst().orElseThrow(() -> new IllegalArgumentException("Invalid flow method")); } } public static final class HTTPHeader { @JsonProperty("key") public String key; @JsonProperty("value") public String value; public static HTTPHeader create(String key, String value) { HTTPHeader header = new HTTPHeader(); header.key = key; header.value = value; return header; } } public static final class HTTPReq { @JsonProperty("body") public Blob body; @JsonProperty("headers") public List headers; @JsonProperty("method") public HTTPMethod method; } public static final class HTTPReqDatum extends Datum { @JsonUnwrapped public HTTPReq req; @Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { return null; } } public enum StatusDatumType { @JsonProperty("unknown_state") UnknownState(Flow.FlowState.UNKNOWN), @JsonProperty("succeeded") Succeeded(Flow.FlowState.SUCCEEDED), @JsonProperty("failed") Failed(Flow.FlowState.FAILED), @JsonProperty("cancelled") Cancelled(Flow.FlowState.CANCELLED), @JsonProperty("killed") Killed(Flow.FlowState.KILLED); private final Flow.FlowState flowState; StatusDatumType(Flow.FlowState flowState) { this.flowState = flowState; } public Flow.FlowState getFlowState() { return flowState; } } public static final class StatusDatum extends Datum { @JsonProperty("type") public StatusDatumType type; @Override public Object toJava(boolean successful, FlowId flowId, BlobStoreClient blobStore, ClassLoader classLoader) { return type.getFlowState(); } public static StatusDatum fromType(StatusDatumType type) { StatusDatum datum = new StatusDatum(); datum.type = type; return datum; } } public static final class HTTPResp { @JsonProperty("body") public Blob body; @JsonProperty("headers") public List headers = new ArrayList<>(); @JsonProperty("status_code") public Integer statusCode; } public static final class HTTPRespDatum extends Datum { @JsonUnwrapped public HTTPResp resp; @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); } } public static HTTPRespDatum create(HTTPResp res) { HTTPRespDatum datum = new HTTPRespDatum(); datum.resp = res; return datum; } } public static Datum datumFromJava(FlowId flow, Object value, BlobStoreClient blobStore) { if (value == null) { return new EmptyDatum(); } else if (value instanceof FlowFuture) { if (!(value instanceof RemoteFlow.RemoteFlowFuture)) { throw new IllegalStateException("Unsupported flow future type return by function"); } StageRefDatum datum = new StageRefDatum(); datum.stageId = ((RemoteFlow.RemoteFlowFuture) value).id(); return datum; } else if (value instanceof RemoteHTTPResponse) { HTTPRespDatum datum = new HTTPRespDatum(); datum.resp = ((RemoteHTTPResponse) value).response; return datum; } else if (value instanceof RemoteHTTPRequest) { HTTPReqDatum datum = new HTTPReqDatum(); datum.req = ((RemoteHTTPRequest) value).req; return datum; } else { byte[] data; try { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(value); oos.close(); data = bos.toByteArray(); } catch (NotSerializableException e) { if (value instanceof Throwable) { // unserializable errors are wrapped WrappedFunctionException wrapped = new WrappedFunctionException((Throwable) value); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(wrapped); oos.close(); data = bos.toByteArray(); } else { throw new ResultSerializationException("Value not serializable", e); } } BlobResponse blobResponse = blobStore.writeBlob(flow.getId(), data, RemoteFlowApiClient.CONTENT_TYPE_JAVA_OBJECT); APIModel.Blob blob = APIModel.Blob.fromBlobResponse(blobResponse); BlobDatum bd = new BlobDatum(); bd.blob = blob; return bd; } catch (IOException e) { throw new PlatformCommunicationException("Failed to store blob", e); } } } public static class AddStageResponse { @JsonProperty("flow_id") public String flowId; @JsonProperty("stage_id") public String stageId; } static class CreateGraphResponse { @JsonProperty("flow_id") public String flowId; } public static class CreateGraphRequest { public CreateGraphRequest() { } public CreateGraphRequest(String functionId) { this.functionId = functionId; } @JsonProperty("function_id") public String functionId; } public static class AddStageRequest { @JsonProperty("operation") public CompletionOperation operation; @JsonProperty("closure") public Blob closure; @JsonProperty("deps") public List deps; @JsonProperty("code_location") public String codeLocation; @JsonProperty("caller_id") public String callerId; } public static class CompleteStageExternallyRequest { @JsonProperty("value") public CompletionResult value; @JsonProperty("code_location") public String codeLocation; @JsonProperty("caller_id") public String callerId; } public static class AddCompletedValueStageRequest { @JsonProperty("value") public CompletionResult value; @JsonProperty("code_location") public String codeLocation; @JsonProperty("caller_id") public String callerId; } public static class AddDelayStageRequest { @JsonProperty("delay_ms") public Long delayMs; @JsonProperty("code_location") public String codeLocation; @JsonProperty("caller_id") public String callerId; } public static class AddInvokeFunctionStageRequest { @JsonProperty("function_id") public String functionId; @JsonProperty("arg") public HTTPReq arg; @JsonProperty("code_location") public String codeLocation; @JsonProperty("caller_id") public String callerId; } public static class AwaitStageResponse { @JsonProperty("flow_id") public String flowId; @JsonProperty("stage_id") public String stageId; @JsonProperty("result") public CompletionResult result; } public static class InvokeStageRequest { @JsonProperty("flow_id") public String flowId; @JsonProperty("stage_id") public String stageId; @JsonProperty("closure") public Blob closure; @JsonProperty("args") public List args = new ArrayList<>(); } public static class InvokeStageResponse { @JsonProperty("result") public CompletionResult result; } private static class RemoteHTTPResponse implements HttpResponse { private final HTTPResp response; private final BlobStoreClient blobStoreClient; private final FlowId flowId; byte[] body; RemoteHTTPResponse(FlowId flow, HTTPResp response, BlobStoreClient blobStoreClient) { this.response = response; this.blobStoreClient = blobStoreClient; this.flowId = flow; } @Override public int getStatusCode() { return response.statusCode; } @Override public Headers getHeaders() { Map headers = new HashMap<>(); response.headers.forEach((h) -> headers.put(h.key, h.value)); return Headers.fromMap(headers); } @Override public byte[] getBodyAsBytes() { if (body != null) { return body; } return body = readBlobDataAsBytes(blobStoreClient, flowId, response.body); } } private static class RemoteHTTPRequest implements HttpRequest { private final HTTPReq req; private final BlobStoreClient blobStoreClient; private final FlowId flowId; byte[] body; RemoteHTTPRequest(FlowId flow, HTTPResp response, HTTPReq req, BlobStoreClient blobStoreClient) { this.req = req; this.blobStoreClient = blobStoreClient; this.flowId = flow; } @Override public HttpMethod getMethod() { return req.method.flowMethod; } @Override public Headers getHeaders() { Map headers = new HashMap<>(); req.headers.forEach((h) -> headers.put(h.key, h.value)); return Headers.fromMap(headers); } @Override public byte[] getBodyAsBytes() { if (body != null) { return body; } return body = readBlobDataAsBytes(blobStoreClient, flowId, req.body); } } private static byte[] readBlobDataAsBytes(BlobStoreClient blobStoreClient, FlowId flowId, Blob blob) { if (blob != null) { return blobStoreClient.readBlob(flowId.getId(), blob.blobId, (inputStream) -> { try { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); IOUtils.copy(inputStream, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } catch (IOException e) { throw new FunctionInputHandlingException("Unable to read blob"); } }, blob.contentType); } else { return new byte[0]; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy