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 d125572cd..6675902cb 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -114,6 +114,7 @@ public class WorkflowApplication implements AutoCloseable { private final WorkflowLifeCycleCloudEventFactory lifeCycleCloudEventFactory; private final ScheduledExecutorService schedulerExecutorService; private final Set allowedCommands; + private final Map, List> serviceLoadedClasses = new ConcurrentHashMap<>(); private WorkflowApplication(Builder builder) { this.taskFactory = builder.taskFactory; @@ -708,4 +709,24 @@ public WorkflowLifeCycleCloudEventFactory lifeCycleCloudEventFactory() { public Set allowedCommands() { return allowedCommands; } + + @SuppressWarnings("unchecked") + public > List serviceLoadedClasses(Class clazz) { + return (List) + serviceLoadedClasses.computeIfAbsent( + clazz, + c -> + ServiceLoader.load(clazz).stream() + .map(ServiceLoader.Provider::get) + .sorted() + .toList()); + } + + public > T serviceLoadedClass(Class serviceClass) { + List list = serviceLoadedClasses(serviceClass); + if (list.isEmpty()) { + throw new IllegalStateException("No " + serviceClass + " implementation found"); + } + return list.get(0); + } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java index e1e8fb6e2..3404ac1d2 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java @@ -16,13 +16,13 @@ package io.serverlessworkflow.impl.auth; import static io.serverlessworkflow.impl.WorkflowUtils.checkSecret; -import static io.serverlessworkflow.impl.WorkflowUtils.loadFirst; import static io.serverlessworkflow.impl.WorkflowUtils.secret; import io.serverlessworkflow.api.types.OAuth2AuthenticationData; import io.serverlessworkflow.api.types.SecretBasedAuthenticationPolicy; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowValueResolver; @@ -35,14 +35,6 @@ public abstract class CommonOAuthProvider implements AuthProvider { private final WorkflowValueResolver tokenProvider; - private static JWTConverter jwtConverter = - loadFirst(JWTConverter.class) - .orElseThrow(() -> new IllegalStateException("No JWTConverter implementation found")); - - private static AccessTokenProviderFactory accessTokenProviderFactory = - loadFirst(AccessTokenProviderFactory.class) - .orElseThrow(() -> new IllegalStateException("No JWTConverter implementation found")); - protected CommonOAuthProvider(WorkflowValueResolver tokenProvider) { this.tokenProvider = tokenProvider; } @@ -67,35 +59,42 @@ protected static OAuth2AuthenticationData fillFromMap( } protected static WorkflowValueResolver accessToken( + WorkflowApplication app, Workflow workflow, OAuth2AuthenticationData authenticationData, SecretBasedAuthenticationPolicy secret, AuthRequestBuilder builder) { if (authenticationData != null) { - return build(authenticationData, builder); + return build(authenticationData, builder, app); } else if (secret != null) { - return build(checkSecret(workflow, secret), builder); + return build(checkSecret(workflow, secret), builder, app); } throw new IllegalStateException("Both policy and secret are null"); } private static WorkflowValueResolver build( - OAuth2AuthenticationData authenticationData, AuthRequestBuilder authBuilder) { + OAuth2AuthenticationData authenticationData, + AuthRequestBuilder authBuilder, + WorkflowApplication app) { AccessTokenProvider tokenProvider = - accessTokenProviderFactory.build( - authBuilder.apply(authenticationData), authenticationData.getIssuers(), jwtConverter); + app.serviceLoadedClass(AccessTokenProviderFactory.class) + .build( + authBuilder.apply(authenticationData), + authenticationData.getIssuers(), + app.serviceLoadedClass(JWTConverter.class)); return (w, t, m) -> tokenProvider; } private static WorkflowValueResolver build( - String secretName, AuthRequestBuilder authBuilder) { + String secretName, AuthRequestBuilder authBuilder, WorkflowApplication app) { return (w, t, m) -> { Map secret = secret(w, secretName); String issuers = (String) secret.get("issuers"); - return accessTokenProviderFactory.build( - authBuilder.apply(secret), - issuers != null ? Arrays.asList(issuers.split(",")) : null, - jwtConverter); + return app.serviceLoadedClass(AccessTokenProviderFactory.class) + .build( + authBuilder.apply(secret), + issuers != null ? Arrays.asList(issuers.split(",")) : null, + app.serviceLoadedClass(JWTConverter.class)); }; } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java index 6bce3d814..16e0e1ea7 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuth2AuthProvider.java @@ -24,6 +24,7 @@ public OAuth2AuthProvider( WorkflowApplication application, Workflow workflow, OAuthPolicyData policyData) { super( accessToken( + application, workflow, policyData.data(), policyData.secret(), diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java index 80dd4138b..425e3c082 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/OpenIdAuthProvider.java @@ -24,6 +24,7 @@ public OpenIdAuthProvider( WorkflowApplication application, Workflow workflow, OAuthPolicyData policyData) { super( accessToken( + application, workflow, policyData.data(), policyData.secret(), diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java index f34253471..1d42b9b5d 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java @@ -41,16 +41,10 @@ import java.util.Collection; import java.util.Map; import java.util.Optional; -import java.util.ServiceLoader; import java.util.concurrent.CompletableFuture; public class EmitExecutor extends RegularTaskExecutor { - private static final Collection emittedDecorators = - ServiceLoader.load(EmittedEventDecorator.class).stream() - .map(ServiceLoader.Provider::get) - .sorted() - .toList(); private final EventPropertiesBuilder props; public static class EmitExecutorBuilder @@ -139,7 +133,11 @@ private CloudEvent buildCloudEvent(WorkflowContext workflow, TaskContext taskCon .additionalFilter() .map(filter -> filter.apply(workflow, taskContext, taskContext.input())) .ifPresent(value -> value.forEach((k, v) -> addExtension(ceBuilder, k, v))); - emittedDecorators.forEach(d -> d.decorate(ceBuilder, workflow, taskContext)); + workflow + .definition() + .application() + .serviceLoadedClasses(EmittedEventDecorator.class) + .forEach(d -> d.decorate(ceBuilder, workflow, taskContext)); return ceBuilder.build(); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunScriptExecutorBuilder.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunScriptExecutorBuilder.java index 87198d540..02d2b813b 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunScriptExecutorBuilder.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunScriptExecutorBuilder.java @@ -27,7 +27,6 @@ import io.serverlessworkflow.impl.scripts.ScriptRunner; import java.util.Objects; import java.util.Optional; -import java.util.ServiceLoader; public class RunScriptExecutorBuilder implements RunnableTaskBuilder { @@ -65,10 +64,8 @@ public CallableTask build(RunScript taskConfiguration, WorkflowDefinition defini m), taskConfiguration.isAwait(), taskConfiguration.getReturn(), - ServiceLoader.load(ScriptRunner.class).stream() - .map(ServiceLoader.Provider::get) + application.serviceLoadedClasses(ScriptRunner.class).stream() .filter(s -> s.identifier().equals(language)) - .sorted() .findFirst() .orElseThrow( () -> diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunTaskExecutor.java index c398a36d7..f1fe7861e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunTaskExecutor.java @@ -22,17 +22,12 @@ import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowMutablePosition; -import java.util.ServiceLoader; -import java.util.ServiceLoader.Provider; import java.util.concurrent.CompletableFuture; public class RunTaskExecutor extends RegularTaskExecutor { private final CallableTask runnable; - private static final ServiceLoader runnables = - ServiceLoader.load(RunnableTaskBuilder.class); - public static class RunTaskExecutorBuilder extends RegularTaskExecutorBuilder { private CallableTask runnable; @@ -42,10 +37,8 @@ protected RunTaskExecutorBuilder( super(position, task, definition); RunTaskConfiguration config = task.getRun().get(); this.runnable = - runnables.stream() - .map(Provider::get) + definition.application().serviceLoadedClasses(RunnableTaskBuilder.class).stream() .filter(r -> r.accept(config.getClass())) - .sorted() .findFirst() .map(r -> r.build(config, definition)) .orElseThrow( diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java index 5c9f025bb..50693a5ee 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java @@ -30,13 +30,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.ServiceLoader; public class HttpExecutorBuilder { public static final String HTTP_REQUEST_DECORATOR_KEY = "HttpRequestDecorators"; private final WorkflowDefinition definition; - private final List requestDecorators; + private List requestDecorators; private WorkflowValueResolver pathSupplier; private Object body; private String method = HttpMethod.GET; @@ -47,13 +46,13 @@ public class HttpExecutorBuilder { private HttpExecutorBuilder(WorkflowDefinition definition) { this.definition = definition; - this.requestDecorators = new ArrayList<>(); + this.requestDecorators = + new ArrayList<>(definition.application().serviceLoadedClasses(HttpRequestDecorator.class)); requestDecorators.addAll( definition .application() .>additionalObject(HTTP_REQUEST_DECORATOR_KEY) .orElse(List.of())); - ServiceLoader.load(HttpRequestDecorator.class).forEach(requestDecorators::add); Collections.sort(requestDecorators); }