diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index 58276eeb..cd97368a 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -19,6 +19,7 @@ import io.serverlessworkflow.api.types.SchemaInline; import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.additional.WorkflowAdditionalObject; import io.serverlessworkflow.impl.events.EventConsumer; import io.serverlessworkflow.impl.events.EventPublisher; import io.serverlessworkflow.impl.events.InMemoryEvents; @@ -38,6 +39,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.ServiceLoader; import java.util.ServiceLoader.Provider; import java.util.concurrent.ConcurrentHashMap; @@ -61,6 +63,7 @@ public class WorkflowApplication implements AutoCloseable { private final boolean lifeCycleCEPublishingEnabled; private final WorkflowModelFactory modelFactory; private final WorkflowScheduler scheduler; + private final Map> additionalObjects; private WorkflowApplication(Builder builder) { this.taskFactory = builder.taskFactory; @@ -78,6 +81,7 @@ private WorkflowApplication(Builder builder) { this.lifeCycleCEPublishingEnabled = builder.lifeCycleCEPublishingEnabled; this.modelFactory = builder.modelFactory; this.scheduler = builder.scheduler; + this.additionalObjects = builder.additionalObjects; } public TaskExecutorFactory taskFactory() { @@ -153,6 +157,7 @@ public SchemaValidator getValidator(SchemaInline inline) { () -> new RuntimeDescriptor("reference impl", "1.0.0_alpha", Collections.emptyMap()); private boolean lifeCycleCEPublishingEnabled = true; private WorkflowModelFactory modelFactory; + private Map> additionalObjects; private Builder() {} @@ -221,6 +226,15 @@ public Builder withEventPublisher(EventPublisher eventPublisher) { return this; } + public Builder withAdditionalObject( + String name, WorkflowAdditionalObject additionalObject) { + if (additionalObjects == null) { + additionalObjects = new ConcurrentHashMap<>(); + } + additionalObjects.put(name, additionalObject); + return this; + } + public Builder withModelFactory(WorkflowModelFactory modelFactory) { this.modelFactory = modelFactory; return this; @@ -269,6 +283,10 @@ public WorkflowApplication build() { if (scheduler == null) { scheduler = new DefaultWorkflowScheduler(); } + if (additionalObjects == null) { + additionalObjects = Collections.emptyMap(); + } + return new WorkflowApplication(this); } } @@ -329,4 +347,10 @@ public boolean isLifeCycleCEPublishingEnabled() { public WorkflowScheduler scheduler() { return scheduler; } + + public Optional additionalObject( + String name, WorkflowContext workflowContext, TaskContext taskContext) { + return Optional.ofNullable(additionalObjects.get(name)) + .map(v -> (T) v.apply(workflowContext, taskContext)); + } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/additional/ConstantAdditionalObject.java b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/ConstantAdditionalObject.java new file mode 100644 index 00000000..659af537 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/ConstantAdditionalObject.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * 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 io.serverlessworkflow.impl.additional; + +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowContext; + +public class ConstantAdditionalObject implements WorkflowAdditionalObject { + + private final T object; + + public ConstantAdditionalObject(T object) { + this.object = object; + } + + @Override + public T apply(WorkflowContext t, TaskContext u) { + return object; + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/additional/SuppliedAdditionalObject.java b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/SuppliedAdditionalObject.java new file mode 100644 index 00000000..d1eb1910 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/SuppliedAdditionalObject.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * 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 io.serverlessworkflow.impl.additional; + +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowContext; +import java.util.function.Supplier; + +public class SuppliedAdditionalObject implements WorkflowAdditionalObject { + + private final Supplier supplier; + + public SuppliedAdditionalObject(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public T apply(WorkflowContext t, TaskContext u) { + return supplier.get(); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/additional/WorkflowAdditionalObject.java b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/WorkflowAdditionalObject.java new file mode 100644 index 00000000..f45c5edc --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/WorkflowAdditionalObject.java @@ -0,0 +1,22 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * 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 io.serverlessworkflow.impl.additional; + +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowContext; +import java.util.function.BiFunction; + +public interface WorkflowAdditionalObject extends BiFunction {} diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java new file mode 100644 index 00000000..00c165cf --- /dev/null +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * 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 io.serverlessworkflow.impl.executors.http; + +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowContext; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; + +public class HttpClientResolver { + + public static final String HTTP_CLIENT_PROVIDER = "httpClientProvider"; + + private static class DefaultHolder { + private static final Client client = ClientBuilder.newClient(); + } + + public static Client client(WorkflowContext workflowContext, TaskContext taskContext) { + return workflowContext + .definition() + .application() + .additionalObject(HTTP_CLIENT_PROVIDER, workflowContext, taskContext) + .orElseGet(() -> DefaultHolder.client); + } + + private HttpClientResolver() {} +} diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java index f912cb58..6498dbad 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java @@ -34,8 +34,6 @@ import io.serverlessworkflow.impl.expressions.ExpressionDescriptor; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.WebApplicationException; -import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.client.Invocation.Builder; import jakarta.ws.rs.client.WebTarget; import java.net.URI; @@ -46,7 +44,6 @@ public class HttpExecutor implements CallableTask { - private static final Client client = ClientBuilder.newClient(); // TODO allow changing default converter private static final HttpModelConverter defaultConverter = new HttpModelConverter() {}; @@ -262,13 +259,14 @@ public boolean accept(Class clazz) { } private static TargetSupplier getTargetSupplier(WorkflowValueResolver uriSupplier) { - return (w, t, n) -> client.target(uriSupplier.apply(w, t, n)); + return (w, t, n) -> HttpClientResolver.client(w, t).target(uriSupplier.apply(w, t, n)); } private static TargetSupplier getTargetSupplier( WorkflowValueResolver uriSupplier, WorkflowValueResolver pathSupplier) { return (w, t, n) -> - client.target(uriSupplier.apply(w, t, n).resolve(pathSupplier.apply(w, t, n))); + HttpClientResolver.client(w, t) + .target(uriSupplier.apply(w, t, n).resolve(pathSupplier.apply(w, t, n))); } private static interface TargetSupplier {