diff --git a/impl/core/pom.xml b/impl/core/pom.xml index 53fb5a656..00c34df8c 100644 --- a/impl/core/pom.xml +++ b/impl/core/pom.xml @@ -33,5 +33,10 @@ junit-jupiter-engine test + + org.assertj + assertj-core + test + diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/FunctionReader.java b/impl/core/src/main/java/io/serverlessworkflow/impl/FunctionReader.java index 755a98c1e..0b7066359 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/FunctionReader.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/FunctionReader.java @@ -19,4 +19,4 @@ import io.serverlessworkflow.impl.resources.ExternalResourceHandler; import java.util.function.Function; -public interface FunctionReader extends Function {} +public interface FunctionReader extends Function, ServicePriority {} 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 cd70f881a..550cddb2f 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -15,6 +15,7 @@ */ package io.serverlessworkflow.impl; +import static io.serverlessworkflow.impl.WorkflowUtils.loadFirst; import static io.serverlessworkflow.impl.WorkflowUtils.safeClose; import io.serverlessworkflow.api.types.SchemaInline; @@ -305,8 +306,7 @@ public Builder withDefaultCatalogURI(URI defaultCatalogURI) { public WorkflowApplication build() { if (modelFactory == null) { modelFactory = - ServiceLoader.load(WorkflowModelFactory.class) - .findFirst() + loadFirst(WorkflowModelFactory.class) .orElseThrow( () -> new IllegalStateException( @@ -318,21 +318,17 @@ public WorkflowApplication build() { ServiceLoader.load(ExpressionFactory.class).forEach(exprFactories::add); if (schemaValidatorFactory == null) { schemaValidatorFactory = - ServiceLoader.load(SchemaValidatorFactory.class) - .findFirst() + loadFirst(SchemaValidatorFactory.class) .orElseGet(() -> EmptySchemaValidatorHolder.instance); } if (taskFactory == null) { taskFactory = - ServiceLoader.load(TaskExecutorFactory.class) - .findFirst() - .orElseGet(() -> DefaultTaskExecutorFactory.get()); + loadFirst(TaskExecutorFactory.class).orElseGet(() -> DefaultTaskExecutorFactory.get()); } ServiceLoader.load(EventPublisher.class).forEach(e -> eventPublishers.add(e)); if (eventConsumer == null) { eventConsumer = - ServiceLoader.load(EventConsumer.class) - .findFirst() + loadFirst(EventConsumer.class) .orElseGet( () -> { InMemoryEvents inMemory = new InMemoryEvents(executorFactory); @@ -353,18 +349,14 @@ public WorkflowApplication build() { if (configManager == null) { configManager = - ServiceLoader.load(ConfigManager.class) - .findFirst() - .orElseGet(() -> new SystemPropertyConfigManager()); + loadFirst(ConfigManager.class).orElseGet(() -> new SystemPropertyConfigManager()); } if (secretManager == null) { secretManager = - ServiceLoader.load(SecretManager.class) - .findFirst() - .orElseGet(() -> new ConfigSecretManager(configManager)); + loadFirst(SecretManager.class).orElseGet(() -> new ConfigSecretManager(configManager)); } - templateResolver = ServiceLoader.load(URITemplateResolver.class).findFirst(); - functionReader = ServiceLoader.load(FunctionReader.class).findFirst(); + templateResolver = loadFirst(URITemplateResolver.class); + functionReader = loadFirst(FunctionReader.class); if (defaultCatalogURI == null) { defaultCatalogURI = URI.create("https://github.com/serverlessworkflow/catalog"); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java index 75fcf8910..16979fc1f 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java @@ -39,6 +39,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.ServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -304,4 +305,11 @@ public static WorkflowValueResolver getURISupplier( } throw new IllegalArgumentException("Invalid uritemplate definition " + template); } + + public static Optional loadFirst(Class serviceClass) { + return ServiceLoader.load(serviceClass).stream() + .map(ServiceLoader.Provider::get) + .sorted() + .findFirst(); + } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AccessTokenProviderFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AccessTokenProviderFactory.java index 4aed1fa2c..c18f69f4c 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AccessTokenProviderFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AccessTokenProviderFactory.java @@ -15,9 +15,10 @@ */ package io.serverlessworkflow.impl.auth; +import io.serverlessworkflow.impl.ServicePriority; import java.util.List; -public interface AccessTokenProviderFactory { +public interface AccessTokenProviderFactory extends ServicePriority { AccessTokenProvider build( HttpRequestInfo requestInfo, List issuers, JWTConverter converter); 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 9ead4c59a..73c0e45f7 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,6 +16,7 @@ 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; @@ -28,20 +29,17 @@ import java.net.URI; import java.util.Arrays; import java.util.Map; -import java.util.ServiceLoader; abstract class CommonOAuthProvider implements AuthProvider { private final WorkflowValueResolver tokenProvider; private static JWTConverter jwtConverter = - ServiceLoader.load(JWTConverter.class) - .findFirst() + loadFirst(JWTConverter.class) .orElseThrow(() -> new IllegalStateException("No JWTConverter implementation found")); private static AccessTokenProviderFactory accessTokenProviderFactory = - ServiceLoader.load(AccessTokenProviderFactory.class) - .findFirst() + loadFirst(AccessTokenProviderFactory.class) .orElseThrow(() -> new IllegalStateException("No JWTConverter implementation found")); protected CommonOAuthProvider(WorkflowValueResolver tokenProvider) { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/JWTConverter.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/JWTConverter.java index 85338e5fc..90580d214 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/JWTConverter.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/JWTConverter.java @@ -15,7 +15,9 @@ */ package io.serverlessworkflow.impl.auth; -public interface JWTConverter { +import io.serverlessworkflow.impl.ServicePriority; + +public interface JWTConverter extends ServicePriority { /** * Converts a JWT token string into a JWT object. diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventConsumer.java b/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventConsumer.java index 1c94ed497..68ac9a009 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventConsumer.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventConsumer.java @@ -17,12 +17,13 @@ import io.cloudevents.CloudEvent; import io.serverlessworkflow.api.types.EventFilter; +import io.serverlessworkflow.impl.ServicePriority; import io.serverlessworkflow.impl.WorkflowApplication; import java.util.Collection; import java.util.function.Consumer; public interface EventConsumer - extends AutoCloseable { + extends AutoCloseable, ServicePriority { V listen(EventFilter filter, WorkflowApplication workflowApplication); 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 f51850903..87198d540 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 @@ -68,6 +68,7 @@ public CallableTask build(RunScript taskConfiguration, WorkflowDefinition defini ServiceLoader.load(ScriptRunner.class).stream() .map(ServiceLoader.Provider::get) .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 5bd26a1bc..af2a414d5 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 @@ -44,6 +44,7 @@ protected RunTaskExecutorBuilder( runnables.stream() .map(Provider::get) .filter(r -> r.accept(config.getClass())) + .sorted() .findFirst() .map(r -> r.build(config, definition)) .orElseThrow( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunnableTaskBuilder.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunnableTaskBuilder.java index eb0f4ba09..e0bc9b0b4 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunnableTaskBuilder.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunnableTaskBuilder.java @@ -16,9 +16,10 @@ package io.serverlessworkflow.impl.executors; import io.serverlessworkflow.api.types.RunTaskConfiguration; +import io.serverlessworkflow.impl.ServicePriority; import io.serverlessworkflow.impl.WorkflowDefinition; -public interface RunnableTaskBuilder { +public interface RunnableTaskBuilder extends ServicePriority { boolean accept(Class clazz); CallableTask build(T taskConfiguration, WorkflowDefinition definition); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/resources/URITemplateResolver.java b/impl/core/src/main/java/io/serverlessworkflow/impl/resources/URITemplateResolver.java index 72e4255c8..97bb62360 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/resources/URITemplateResolver.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/resources/URITemplateResolver.java @@ -15,12 +15,13 @@ */ package io.serverlessworkflow.impl.resources; +import io.serverlessworkflow.impl.ServicePriority; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; import java.net.URI; -public interface URITemplateResolver { +public interface URITemplateResolver extends ServicePriority { URI resolveTemplates( String uri, WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel model); } diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/LowestPriority.java b/impl/core/src/test/java/io/serverlessworkflow/impl/LowestPriority.java new file mode 100644 index 000000000..b4410e198 --- /dev/null +++ b/impl/core/src/test/java/io/serverlessworkflow/impl/LowestPriority.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; + +public class LowestPriority implements ServicePriority { + public int priority() { + return DEFAULT_PRIORITY + 1; + } +} diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/MediumPriority.java b/impl/core/src/test/java/io/serverlessworkflow/impl/MediumPriority.java new file mode 100644 index 000000000..2963409f8 --- /dev/null +++ b/impl/core/src/test/java/io/serverlessworkflow/impl/MediumPriority.java @@ -0,0 +1,18 @@ +/* + * 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; + +public class MediumPriority implements ServicePriority {} diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/ServicePriorityTest.java b/impl/core/src/test/java/io/serverlessworkflow/impl/ServicePriorityTest.java new file mode 100644 index 000000000..f58af79df --- /dev/null +++ b/impl/core/src/test/java/io/serverlessworkflow/impl/ServicePriorityTest.java @@ -0,0 +1,29 @@ +/* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ServicePriorityTest { + + @Test + void testRightOrder() { + assertThat(WorkflowUtils.loadFirst(ServicePriority.class).orElseThrow()) + .isInstanceOf(TopPriority.class); + } +} diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/TopPriority.java b/impl/core/src/test/java/io/serverlessworkflow/impl/TopPriority.java new file mode 100644 index 000000000..ce7bd88ff --- /dev/null +++ b/impl/core/src/test/java/io/serverlessworkflow/impl/TopPriority.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; + +public class TopPriority implements ServicePriority { + public int priority() { + return DEFAULT_PRIORITY - 1; + } +} diff --git a/impl/core/src/test/resources/META-INF/services/io.serverlessworkflow.impl.ServicePriority b/impl/core/src/test/resources/META-INF/services/io.serverlessworkflow.impl.ServicePriority new file mode 100644 index 000000000..0c5166da3 --- /dev/null +++ b/impl/core/src/test/resources/META-INF/services/io.serverlessworkflow.impl.ServicePriority @@ -0,0 +1,3 @@ +io.serverlessworkflow.impl.LowestPriority +io.serverlessworkflow.impl.TopPriority +io.serverlessworkflow.impl.MediumPriority diff --git a/impl/jq/src/test/java/io/serverlessworkflow/impl/expressions/jq/JQExpressionFactoryTest.java b/impl/jq/src/test/java/io/serverlessworkflow/impl/expressions/jq/JQExpressionFactoryTest.java index 784de6885..aa9bcc8b2 100644 --- a/impl/jq/src/test/java/io/serverlessworkflow/impl/expressions/jq/JQExpressionFactoryTest.java +++ b/impl/jq/src/test/java/io/serverlessworkflow/impl/expressions/jq/JQExpressionFactoryTest.java @@ -15,6 +15,7 @@ */ package io.serverlessworkflow.impl.expressions.jq; +import static io.serverlessworkflow.impl.WorkflowUtils.loadFirst; import static org.assertj.core.api.Assertions.assertThat; import io.serverlessworkflow.impl.WorkflowContext; @@ -26,7 +27,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.ServiceLoader; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -41,7 +41,7 @@ class JQExpressionFactoryTest { void setup() { workflowContext = Mockito.mock(WorkflowContext.class); factory = new JQExpressionFactory(); - modelFactory = ServiceLoader.load(WorkflowModelFactory.class).findFirst().orElseThrow(); + modelFactory = loadFirst(WorkflowModelFactory.class).orElseThrow(); } @Test diff --git a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactory.java b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactory.java index 684a60a61..6cc04cf24 100644 --- a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactory.java +++ b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactory.java @@ -16,6 +16,7 @@ package io.serverlessworkflow.impl.jackson; import com.fasterxml.jackson.databind.ObjectMapper; +import io.serverlessworkflow.impl.ServicePriority; import java.util.function.Supplier; -public interface ObjectMapperFactory extends Supplier {} +public interface ObjectMapperFactory extends Supplier, ServicePriority {} diff --git a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java index df6d8a91e..3a12ea3d9 100644 --- a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java +++ b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java @@ -15,9 +15,10 @@ */ package io.serverlessworkflow.impl.jackson; +import static io.serverlessworkflow.impl.WorkflowUtils.loadFirst; + import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Objects; -import java.util.ServiceLoader; import java.util.function.Supplier; public class ObjectMapperFactoryProvider implements Supplier { @@ -40,8 +41,7 @@ public void setFactory(ObjectMapperFactory objectMapperFactory) { public ObjectMapperFactory get() { return objectMapperFactory != null ? objectMapperFactory - : ServiceLoader.load(ObjectMapperFactory.class) - .findFirst() + : loadFirst(ObjectMapperFactory.class) .orElseGet(() -> () -> new ObjectMapper().findAndRegisterModules()); } } diff --git a/impl/persistence/api/src/main/java/io/serverlessworkflow/impl/marshaller/CustomObjectMarshaller.java b/impl/persistence/api/src/main/java/io/serverlessworkflow/impl/marshaller/CustomObjectMarshaller.java index b96283b64..23fbe1bb1 100644 --- a/impl/persistence/api/src/main/java/io/serverlessworkflow/impl/marshaller/CustomObjectMarshaller.java +++ b/impl/persistence/api/src/main/java/io/serverlessworkflow/impl/marshaller/CustomObjectMarshaller.java @@ -15,7 +15,9 @@ */ package io.serverlessworkflow.impl.marshaller; -public interface CustomObjectMarshaller { +import io.serverlessworkflow.impl.ServicePriority; + +public interface CustomObjectMarshaller extends ServicePriority { void write(WorkflowOutputBuffer buffer, T object); T read(WorkflowInputBuffer buffer);