diff --git a/README.md b/README.md
index cb516899c..ceb6bd900 100644
--- a/README.md
+++ b/README.md
@@ -137,7 +137,7 @@ try (OutputStream out = new FileOutputStream("simple.json")) {
```
For additional writing helper methods, check [WorkflowWriter](api/src/main/java/org/openworkflow/sdk/api/WorkflowWriter.java) class.
-## Jackson dependency
+#### Jackson dependency
The `openworkflow-api` module depends heavily on Jackson for loading file content into an in-memory POJO. For users who use a JSON handling library other than Jackson, the `openworkflow-types` module contains Java POJOs generated from the OpenWorkflow schema without any Jackson annotations. Using this module as a dependency, they can build a reader/writer module equivalent to `openworkflow-api` with their preferred JSON library.
@@ -148,4 +148,14 @@ The reference implementation provides a ready-to-use runtime that supports the O
## License
-[](https://app.fossa.com/projects/git%2Bgithub.com%2Fopen-workflow-specification%2Fsdk-java?ref=badge_large)
\ No newline at end of file
+[](https://app.fossa.com/projects/git%2Bgithub.com%2Fopen-workflow-specification%2Fsdk-java?ref=badge_large)
+
+## Third-Party Code Attributions
+
+This project contains source code copied and modified from the following third-party project:
+
+* **Name:** std-uritemplate
+* **Source:** [std-uritemplate GitHub Repository](https://github.com/std-uritemplate/std-uritemplate)
+* **File Copied:** `java/src/main/java/io/github/stduritemplate/StdUriTemplate.java`
+* **License:** Apache License 2.0
+* **Copyright:** Copyright 2024-2026 std-uritemplate contributors
\ No newline at end of file
diff --git a/examples/simpleGet/pom.xml b/examples/simpleGet/pom.xml
index cb3cd8327..ab0d158ca 100644
--- a/examples/simpleGet/pom.xml
+++ b/examples/simpleGet/pom.xml
@@ -17,18 +17,6 @@
org.openworkflow.sdk
openworkflow-impl-jackson
-
- org.openworkflow.sdk
- openworkflow-impl-http
-
-
- org.glassfish.jersey.media
- jersey-media-json-jackson3
-
-
- org.glassfish.jersey.core
- jersey-client
-
org.slf4j
slf4j-simple
diff --git a/impl/README.md b/impl/README.md
index bff553e2c..01dee15b8 100644
--- a/impl/README.md
+++ b/impl/README.md
@@ -81,36 +81,43 @@ This SDK is modular by design—pull in only what you need:
* **openworkflow-impl-core**
Workflow engine & core interfaces. Depends on generated types and CloudEvents SDK.
+
* **openworkflow-impl-jackson**
- 👉 **Most users add this one.**
-
- Adds Jackson integration, JQ expressions, JSON Schema validation, and CloudEvents (de)serialization.
-
- Internally, this module is an aggregation of smaller modules to allow part replacement if needed:
- * **openworkflow-impl-jq** JQ expression implementation
- * **openworkflow-impl-model** Jackson model implementation
- * **openworkflow-impl-validation** Schema validation implementation
- * **openworkflow-impl-lifecycle-events** Enable publication of lifecycle events as Json cloud events.
- * **openworkflow-impl-function** Function support.
- * **openworkflow-impl-template-resolver** JaxRS URI template implementation.
- * **openworkflow-impl-json** Json common utilities shared by all modules in this list
- * **openworkflow-impl-cron** Cron parsing
- * **openworkflow-impl-http** HTTP `Call` task handler, based on Jax-RS client
- * **openworkflow-impl-jackson-jwt** Optimized entity converter for Jackson model and OAuth2/OIDC helpers for HTTP calls.
- * **openworkflow-impl-openapi** OpenAPI support using Jackson
- * **openworkflow-impl-openapi-base** Base OpenAPI support intended to be extended for a specific JSON library
- * **openworkflow-impl-grpc** gRPC support
- * **openworkflow-impl-script-python** Python support
- * **openworkflow-impl-script-js** Javascript support
- * **openworkflow-impl-container** Docker container support
+ 👉 **Most users add this one.** Contains implementation of core features using Jackson library, like JQ expressions, JSON Schema validation, and CloudEvents (de)serialization. Internally, it is an aggregation of smaller modules:
+
+
+ * **openworkflow-impl-jq** JQ expression implementation
+ * **openworkflow-impl-model** Jackson model implementation
+ * **openworkflow-impl-validation** Schema validation implementation
+ * **openworkflow-impl-lifecycle-events** Enable publication of lifecycle events as Json cloud events.
+ * **openworkflow-impl-function** Jackson Function support.
+ * **openworkflow-impl-json** Jackson common utilities shared by all modules in this list
+ * **openworkflow-impl-cron** Cron parsing for workflow scheduled execution
+
+ Additionally, The OpenWokflow specification defines a set of [call task](https://github.com/open-workflow-specification/specification/blob/main/dsl-reference.md#call), which requires additional dependencies, with the exception of HTTP (see section below). You might add those ones needed depending on the feature you are using in your definition:
+
+ * **openworkflow-impl-openapi** OpenAPI support using Jackson
+ * **openworkflow-impl-openapi-base** Base OpenAPI support intended to be extended for a specific JSON library
+ * **openworkflow-impl-grpc** gRPC support
+ * **openworkflow-impl-script-python** Python support
+ * **openworkflow-impl-script-js** Javascript support
+ * **openworkflow-impl-container** Docker container support
There are also companion modules/docs for:
* **Fluent DSL** (programmatic builder)
* **Mermaid** (diagramming workflows)
-Links below.
+### Http dependencies
+
+By default, the SDK reference implementation use `java.net.HttpRequest` to support Http calls. Therefore, no dependency needs to be added for using Http Call task or downloading of http resources, like OpenAPI schemas.
+
+However, for further configuration of Http functionality, users might include the Jakarta based modules, which will allow customizing `jakarta.ws.rs.client.Client` in Jakarta EE environments.
+
+ * **openworkflow-impl-http** HTTP `Call` task handler, based on JAX-RS client
+ * **openworkflow-impl-jackson-jwt** Optimized entity converter for Jackson model and OAuth2/OIDC helpers for Jakarta HTTP calls.
+
---
## Installation
diff --git a/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowApplication.java b/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowApplication.java
index 55df748ab..b98915394 100644
--- a/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowApplication.java
+++ b/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowApplication.java
@@ -43,6 +43,7 @@
import java.util.stream.Collectors;
import org.openworkflow.sdk.api.types.SchemaInline;
import org.openworkflow.sdk.api.types.Workflow;
+import org.openworkflow.sdk.impl.additional.NamedSuppliedAdditionalObject;
import org.openworkflow.sdk.impl.additional.NamedWorkflowAdditionalObject;
import org.openworkflow.sdk.impl.additional.WorkflowAdditionalObject;
import org.openworkflow.sdk.impl.auth.AuthProviderFactory;
@@ -62,6 +63,10 @@
import org.openworkflow.sdk.impl.executors.TaskExecutorFactory;
import org.openworkflow.sdk.impl.expressions.ExpressionFactory;
import org.openworkflow.sdk.impl.expressions.RuntimeDescriptor;
+import org.openworkflow.sdk.impl.http.DefaultHttpRequestBuilderFactory;
+import org.openworkflow.sdk.impl.http.DefaultURITemplateResolver;
+import org.openworkflow.sdk.impl.http.URITemplateResolver;
+import org.openworkflow.sdk.impl.http.WorkflowHttpRequestFactory;
import org.openworkflow.sdk.impl.lifecycle.LifeCycleEventType;
import org.openworkflow.sdk.impl.lifecycle.WorkflowExecutionCompletableListener;
import org.openworkflow.sdk.impl.lifecycle.WorkflowExecutionListener;
@@ -71,7 +76,6 @@
import org.openworkflow.sdk.impl.resources.DefaultResourceLoaderFactory;
import org.openworkflow.sdk.impl.resources.ExternalResourceHandler;
import org.openworkflow.sdk.impl.resources.ResourceLoaderFactory;
-import org.openworkflow.sdk.impl.resources.URITemplateResolver;
import org.openworkflow.sdk.impl.scheduler.AllStrategyCorrelationInfoFactory;
import org.openworkflow.sdk.impl.scheduler.CronResolver;
import org.openworkflow.sdk.impl.scheduler.CronResolverFactory;
@@ -107,7 +111,7 @@ public class WorkflowApplication implements AutoCloseable {
private final ConfigManager configManager;
private final SecretManager secretManager;
private final SchedulerListener schedulerListener;
- private final Optional templateResolver;
+ private final URITemplateResolver templateResolver;
private final Optional functionReader;
private final URI defaultCatalogURI;
private final WorkflowValueResolver defaultEventSource;
@@ -117,6 +121,7 @@ public class WorkflowApplication implements AutoCloseable {
private final WorkflowLifeCycleCloudEventFactory lifeCycleCloudEventFactory;
private final ScheduledExecutorService schedulerExecutorService;
private final Set allowedCommands;
+ private final WorkflowHttpRequestFactory httpRequestBuilderFactory;
private final Map, ServiceLoader>> servicesLoaded = new ConcurrentHashMap<>();
private final Set lifeCycleEventTypes;
@@ -156,6 +161,7 @@ private WorkflowApplication(Builder builder) {
this.schedulerExecutorService = builder.schedulerExecutorService;
this.allowedCommands = Collections.unmodifiableSet(builder.allowedCommands);
this.lifeCycleEventTypes = builder.lifeCycleEventTypes;
+ this.httpRequestBuilderFactory = builder.httpRequestBuilderFactory;
}
public TaskExecutorFactory taskFactory() {
@@ -274,7 +280,7 @@ public SchemaValidator getValidator(SchemaInline inline) {
private SecretManager secretManager;
private ConfigManager configManager;
private SchedulerListener schedulerListener;
- private Optional templateResolver;
+ private URITemplateResolver templateResolver;
private Optional functionReader;
private URI defaultCatalogURI;
private WorkflowValueResolver defaultEventSource;
@@ -285,10 +291,13 @@ public SchemaValidator getValidator(SchemaInline inline) {
private ScheduledExecutorService schedulerExecutorService;
private Set allowedCommands = new HashSet<>();
private Set lifeCycleEventTypes;
+ private WorkflowHttpRequestFactory httpRequestBuilderFactory;
private Builder() {
ServiceLoader.load(NamedWorkflowAdditionalObject.class)
.forEach(a -> additionalObjects.put(a.name(), a));
+ ServiceLoader.load(NamedSuppliedAdditionalObject.class)
+ .forEach(a -> this.additionalObjectSuppliers.put(a.name(), a));
}
public Builder withId(String id) {
@@ -468,6 +477,17 @@ public Builder withLifeCycleEventTypes(Set lifeCycleEventTyp
return this;
}
+ public Builder withHttpRequestBuilderFactory(
+ WorkflowHttpRequestFactory httpRequestBuilderFactory) {
+ this.httpRequestBuilderFactory = httpRequestBuilderFactory;
+ return this;
+ }
+
+ public Builder withURITemplateResolver(URITemplateResolver templateResolver) {
+ this.templateResolver = templateResolver;
+ return this;
+ }
+
public WorkflowApplication build() {
if (executorFactory == null) {
executorFactory = new DefaultExecutorServiceFactory();
@@ -546,7 +566,10 @@ public CronResolver parseCron(String cron) {
secretManager =
loadFirst(SecretManager.class).orElseGet(() -> new ConfigSecretManager(configManager));
}
- templateResolver = loadFirst(URITemplateResolver.class);
+ if (templateResolver == null) {
+ templateResolver =
+ loadFirst(URITemplateResolver.class).orElseGet(() -> new DefaultURITemplateResolver());
+ }
functionReader = loadFirst(FunctionReader.class);
if (cloudEventPredicateFactory == null) {
cloudEventPredicateFactory =
@@ -578,7 +601,14 @@ public CronResolver parseCron(String cron) {
if (lifeCycleEventTypes == null) {
lifeCycleEventTypes = EnumSet.of(LifeCycleEventType.V8);
}
- return new WorkflowApplication(this);
+ if (httpRequestBuilderFactory == null) {
+ httpRequestBuilderFactory =
+ loadFirst(WorkflowHttpRequestFactory.class)
+ .orElseGet(() -> new DefaultHttpRequestBuilderFactory());
+ }
+ WorkflowApplication app = new WorkflowApplication(this);
+ httpRequestBuilderFactory.init(app);
+ return app;
}
private List loadFromServiceLoader(Class clazz) {
@@ -617,6 +647,7 @@ public void close() {
}
listenersByPriority.clear();
}
+ safeClose(httpRequestBuilderFactory);
}
public WorkflowPositionFactory positionFactory() {
@@ -668,7 +699,7 @@ SchedulerListener schedulerListener() {
return schedulerListener;
}
- public Optional templateResolver() {
+ public URITemplateResolver templateResolver() {
return templateResolver;
}
@@ -699,7 +730,8 @@ public Optional additionalObject(String name) {
public Optional additionalObject(
String name, WorkflowContext workflowContext, TaskContext taskContext) {
return Optional.ofNullable(additionalObjects.get(name))
- .map(v -> (T) v.apply(workflowContext, taskContext));
+ .map(v -> (T) v.apply(workflowContext, taskContext))
+ .or(() -> additionalObject(name));
}
public AuthProviderFactory authProviderFactory() {
@@ -726,6 +758,10 @@ public Set lifeCycleEventTypes() {
return lifeCycleEventTypes;
}
+ public WorkflowHttpRequestFactory httpRequestFactory() {
+ return httpRequestBuilderFactory;
+ }
+
@SuppressWarnings("unchecked")
public > List serviceLoadedClasses(Class clazz) {
ServiceLoader> serviceLoader = servicesLoaded.computeIfAbsent(clazz, ServiceLoader::load);
diff --git a/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowError.java b/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowError.java
index 1acbef092..533e659b2 100644
--- a/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowError.java
+++ b/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowError.java
@@ -17,6 +17,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.Optional;
import org.openworkflow.sdk.impl.lifecycle.TaskFailedEvent;
import org.openworkflow.sdk.impl.lifecycle.WorkflowFailedEvent;
import org.openworkflow.sdk.types.Errors;
@@ -39,15 +40,30 @@ public static Builder communication(int status, TaskContext context, Throwable e
return communication(status, context, ex.getMessage());
}
+ public static Builder communication(int status, Optional context, Throwable ex) {
+ return communication(status, context, ex.getMessage());
+ }
+
public static Builder communication(int status, TaskContext context, String title) {
- return new Builder(Errors.COMMUNICATION.toString(), status)
- .instance(context.position().jsonPointer())
- .title(title);
+ return communication(status, context).title(title);
+ }
+
+ public static Builder communication(int status, Optional context, String title) {
+ return communication(status, context).title(title);
}
public static Builder communication(int status, TaskContext context) {
- return new Builder(Errors.COMMUNICATION.toString(), status)
- .instance(context.position().jsonPointer());
+ Builder builder = new Builder(Errors.COMMUNICATION.toString(), status);
+ if (context != null) {
+ builder.instance(context.position().jsonPointer());
+ }
+ return builder;
+ }
+
+ public static Builder communication(int status, Optional context) {
+ Builder result = new Builder(Errors.COMMUNICATION.toString(), status);
+ context.ifPresent(ctx -> result.instance(ctx.position().jsonPointer()));
+ return result;
}
public static Builder communication(TaskContext context, String title) {
diff --git a/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowModel.java b/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowModel.java
index efb817a0d..197cfbf41 100644
--- a/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowModel.java
+++ b/impl/core/src/main/java/org/openworkflow/sdk/impl/WorkflowModel.java
@@ -34,6 +34,10 @@ public interface WorkflowModel {
Optional