diff --git a/core/deployment/pom.xml b/core/deployment/pom.xml index 72b88fa..0967f36 100644 --- a/core/deployment/pom.xml +++ b/core/deployment/pom.xml @@ -20,6 +20,15 @@ quarkus-flow ${project.version} + + + io.quarkus + quarkus-rest-client-jackson-deployment + + + io.quarkiverse.jackson-jq + quarkus-jackson-jq-deployment + io.serverlessworkflow serverlessworkflow-fluent-spec diff --git a/core/deployment/src/main/java/io/quarkiverse/flow/deployment/DiscoveredFlowBuildItem.java b/core/deployment/src/main/java/io/quarkiverse/flow/deployment/DiscoveredFlowBuildItem.java index 7363166..4535100 100644 --- a/core/deployment/src/main/java/io/quarkiverse/flow/deployment/DiscoveredFlowBuildItem.java +++ b/core/deployment/src/main/java/io/quarkiverse/flow/deployment/DiscoveredFlowBuildItem.java @@ -13,6 +13,7 @@ public DiscoveredFlowBuildItem(MethodInfo method, String workflowName) { method.declaringClass().name().toString(), method.name(), workflowName, + method.parameterTypes().stream().map(t -> t.name().toString()).toArray(String[]::new), method.isStaticInitializer()); } diff --git a/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowNativeProcessor.java b/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowNativeProcessor.java index 5a2ea2a..614d27d 100644 --- a/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowNativeProcessor.java +++ b/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowNativeProcessor.java @@ -1,26 +1,58 @@ package io.quarkiverse.flow.deployment; +import com.github.f4b6a3.ulid.UlidCreator; +import com.github.f4b6a3.ulid.UlidFactory; + +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; +import io.serverlessworkflow.impl.events.EventConsumer; +import io.serverlessworkflow.impl.events.EventPublisher; +import io.serverlessworkflow.impl.events.InMemoryEvents; +import io.serverlessworkflow.impl.executors.DefaultTaskExecutorFactory; +import io.serverlessworkflow.impl.executors.TaskExecutorFactory; +import io.serverlessworkflow.impl.expressions.ExpressionFactory; +import io.serverlessworkflow.impl.expressions.jq.JQExpressionFactory; +import io.serverlessworkflow.impl.jackson.schema.JsonSchemaValidatorFactory; +import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; -/** - * see Native-image build fails due to UlidCreator static - * initialization (Random in image heap) - */ final class FlowNativeProcessor { - @BuildStep - RuntimeInitializedClassBuildItem ulidCreatorHolder() { - return new RuntimeInitializedClassBuildItem("com.github.f4b6a3.ulid.UlidCreator$MonotonicFactoryHolder"); - } + /** + * see Native-image build fails due to UlidCreator + * static + * initialization (Random in image heap) + */ @BuildStep - RuntimeInitializedClassBuildItem ulidCreator() { - return new RuntimeInitializedClassBuildItem("com.github.f4b6a3.ulid.UlidCreator"); + void runtimeInitUlid(BuildProducer producer) { + producer.produce(new RuntimeInitializedClassBuildItem( + "com.github.f4b6a3.ulid.UlidCreator$MonotonicFactoryHolder")); + producer.produce(new RuntimeInitializedClassBuildItem( + com.github.f4b6a3.ulid.UlidCreator.class.getName())); + producer.produce(new RuntimeInitializedClassBuildItem( + com.github.f4b6a3.ulid.UlidFactory.class.getName())); } + /** + * Registers the CNCF Java SDK default providers for native compilation. + */ @BuildStep - RuntimeInitializedClassBuildItem ulidFactory() { - return new RuntimeInitializedClassBuildItem("com.github.f4b6a3.ulid.UlidFactory"); + void registerSDKServiceProviders(BuildProducer sp) { + + // TODO: make all of them @DefaultBeans so users can easily replace + // TODO: these providers must be compatible with Quarkus Ecosystem + + sp.produce(new ServiceProviderBuildItem(ExpressionFactory.class.getName(), + JQExpressionFactory.class.getName())); + sp.produce(new ServiceProviderBuildItem(TaskExecutorFactory.class.getName(), + DefaultTaskExecutorFactory.class.getName())); + sp.produce(new ServiceProviderBuildItem(SchemaValidatorFactory.class.getName(), + JsonSchemaValidatorFactory.class.getName())); + sp.produce(new ServiceProviderBuildItem(EventConsumer.class.getName(), + InMemoryEvents.class.getName())); + sp.produce(new ServiceProviderBuildItem(EventPublisher.class.getName(), + InMemoryEvents.class.getName())); } } diff --git a/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowProcessor.java b/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowProcessor.java index 08af9ef..a159399 100644 --- a/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowProcessor.java +++ b/core/deployment/src/main/java/io/quarkiverse/flow/deployment/FlowProcessor.java @@ -11,10 +11,7 @@ import io.quarkiverse.flow.FlowDefinition; import io.quarkiverse.flow.FlowDescriptor; -import io.quarkiverse.flow.producers.DefaultExpressionFactoryProducer; -import io.quarkiverse.flow.producers.DefaultSchemaValidatorFactoryProducer; -import io.quarkiverse.flow.producers.DefaultTaskExecutorFactoryProducer; -import io.quarkiverse.flow.producers.InMemoryEventsBean; +import io.quarkiverse.flow.providers.QuarkusJQExpressionFactory; import io.quarkiverse.flow.recorders.FlowRecorder; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; @@ -26,7 +23,7 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; -import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; @@ -69,9 +66,9 @@ void collectFlowDescriptors(CombinedIndexBuildItem index, BuildProducer discovered, BuildProducer keep, - BuildProducer reflective) { + BuildProducer reflective) { - var owners = discovered.stream() + List owners = discovered.stream() .map(d -> d.workflow.className) .distinct() .toList(); @@ -79,14 +76,8 @@ void keepAndReflectFlowDescriptors( // Keep producers from being removed keep.produce(UnremovableBeanBuildItem.beanClassNames(owners.toArray(String[]::new))); - // Make all declared methods on the owner classes available at runtime - for (String cn : owners) { - reflective.produce( - ReflectiveClassBuildItem.builder(cn) - .methods(true) // keep methods (needed for MethodHandles / reflection) - .fields(false) // not needed here - .constructors(false) - .build()); + for (DiscoveredFlowBuildItem d : discovered) { + reflective.produce(new ReflectiveMethodBuildItem(d.workflow.className, d.workflow.methodName, d.workflow.params)); } } @@ -104,10 +95,7 @@ AdditionalBeanBuildItem coreBeans() { @BuildStep AdditionalBeanBuildItem registerRuntimeDefaults() { return AdditionalBeanBuildItem.builder() - .addBeanClass(InMemoryEventsBean.class) - .addBeanClass(DefaultExpressionFactoryProducer.class) - .addBeanClass(DefaultSchemaValidatorFactoryProducer.class) - .addBeanClass(DefaultTaskExecutorFactoryProducer.class) + .addBeanClass(QuarkusJQExpressionFactory.class) .setUnremovable() .build(); } diff --git a/core/pom.xml b/core/pom.xml index 1aedcab..5820d18 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -16,20 +16,4 @@ runtime deployment - - - - io.serverlessworkflow - serverlessworkflow-impl-core - - - io.serverlessworkflow - serverlessworkflow-impl-http - - - io.serverlessworkflow - serverlessworkflow-impl-jackson - - - diff --git a/core/runtime/pom.xml b/core/runtime/pom.xml index e5d11d1..5ad9acd 100644 --- a/core/runtime/pom.xml +++ b/core/runtime/pom.xml @@ -11,15 +11,40 @@ Quarkus Flow :: Core :: Runtime + - io.quarkus - quarkus-arc + io.serverlessworkflow + serverlessworkflow-impl-core + + + io.serverlessworkflow + serverlessworkflow-impl-http + + + io.serverlessworkflow + serverlessworkflow-impl-jackson io.serverlessworkflow serverlessworkflow-fluent-spec ${io.serverlessworkflow.version} + + + + io.quarkus + quarkus-arc + + + io.quarkiverse.jackson-jq + quarkus-jackson-jq + + + + io.quarkus + quarkus-rest-client-jackson + + diff --git a/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultExpressionFactoryProducer.java b/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultExpressionFactoryProducer.java deleted file mode 100644 index e244ccd..0000000 --- a/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultExpressionFactoryProducer.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.quarkiverse.flow.producers; - -import jakarta.enterprise.inject.Produces; -import jakarta.inject.Singleton; - -import io.quarkus.arc.DefaultBean; -import io.serverlessworkflow.impl.expressions.ExpressionFactory; -import io.serverlessworkflow.impl.expressions.jq.JQExpressionFactory; - -public class DefaultExpressionFactoryProducer { - @Produces - @Singleton - @DefaultBean - ExpressionFactory expressionFactory() { - return new JQExpressionFactory(); - } -} diff --git a/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultSchemaValidatorFactoryProducer.java b/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultSchemaValidatorFactoryProducer.java deleted file mode 100644 index 4d475ea..0000000 --- a/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultSchemaValidatorFactoryProducer.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.quarkiverse.flow.producers; - -import jakarta.enterprise.inject.Produces; -import jakarta.inject.Singleton; - -import io.quarkus.arc.DefaultBean; -import io.serverlessworkflow.impl.jackson.schema.JsonSchemaValidatorFactory; -import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; - -public class DefaultSchemaValidatorFactoryProducer { - - @Produces - @Singleton - @DefaultBean - SchemaValidatorFactory schemaValidatorFactory() { - return new JsonSchemaValidatorFactory(); - } - -} diff --git a/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultTaskExecutorFactoryProducer.java b/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultTaskExecutorFactoryProducer.java deleted file mode 100644 index ce91cae..0000000 --- a/core/runtime/src/main/java/io/quarkiverse/flow/producers/DefaultTaskExecutorFactoryProducer.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.quarkiverse.flow.producers; - -import jakarta.enterprise.inject.Produces; -import jakarta.inject.Singleton; - -import io.quarkus.arc.DefaultBean; -import io.serverlessworkflow.impl.executors.DefaultTaskExecutorFactory; -import io.serverlessworkflow.impl.executors.TaskExecutorFactory; - -public class DefaultTaskExecutorFactoryProducer { - - @Produces - @Singleton - @DefaultBean - TaskExecutorFactory taskExecutorFactory() { - return DefaultTaskExecutorFactory.get(); - } - -} diff --git a/core/runtime/src/main/java/io/quarkiverse/flow/producers/InMemoryEventsBean.java b/core/runtime/src/main/java/io/quarkiverse/flow/producers/InMemoryEventsBean.java deleted file mode 100644 index 9fabd33..0000000 --- a/core/runtime/src/main/java/io/quarkiverse/flow/producers/InMemoryEventsBean.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.quarkiverse.flow.producers; - -import jakarta.enterprise.inject.Produces; -import jakarta.inject.Singleton; - -import io.quarkus.arc.DefaultBean; -import io.serverlessworkflow.impl.events.EventConsumer; -import io.serverlessworkflow.impl.events.EventPublisher; -import io.serverlessworkflow.impl.events.InMemoryEvents; - -public class InMemoryEventsBean { - - @Produces - @Singleton - @DefaultBean - InMemoryEvents inMemoryEvents() { - return new InMemoryEvents(); - } - - @SuppressWarnings("rawtypes") - @Produces - @Singleton - @DefaultBean - EventConsumer eventConsumer(InMemoryEvents delegate) { - return delegate; - } - - @Produces - @Singleton - @DefaultBean - EventPublisher eventPublisher(InMemoryEvents delegate) { - return delegate; - } - -} diff --git a/core/runtime/src/main/java/io/quarkiverse/flow/providers/QuarkusJQExpressionFactory.java b/core/runtime/src/main/java/io/quarkiverse/flow/providers/QuarkusJQExpressionFactory.java new file mode 100644 index 0000000..09e4981 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkiverse/flow/providers/QuarkusJQExpressionFactory.java @@ -0,0 +1,34 @@ +package io.quarkiverse.flow.providers; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import io.quarkus.arc.DefaultBean; +import io.serverlessworkflow.impl.expressions.ExpressionUtils; +import io.serverlessworkflow.impl.expressions.ObjectExpression; +import io.serverlessworkflow.impl.expressions.jq.JQExpression; +import io.serverlessworkflow.impl.expressions.jq.JQExpressionFactory; +import net.thisptr.jackson.jq.Scope; +import net.thisptr.jackson.jq.Versions; +import net.thisptr.jackson.jq.exception.JsonQueryException; + +@ApplicationScoped +@DefaultBean +public class QuarkusJQExpressionFactory extends JQExpressionFactory { + + @Inject + Scope scope; // provided by quarkus-jackson-jq + + public QuarkusJQExpressionFactory() { + } + + @Override + public ObjectExpression buildExpression(String expression) { + try { + return new JQExpression(() -> scope, ExpressionUtils.trimExpr(expression), Versions.JQ_1_6); + } catch (JsonQueryException e) { + throw new IllegalArgumentException(e); + } + } + +} diff --git a/core/runtime/src/main/java/io/quarkiverse/flow/recorders/DiscoveredFlow.java b/core/runtime/src/main/java/io/quarkiverse/flow/recorders/DiscoveredFlow.java index 26a94a3..64dff44 100644 --- a/core/runtime/src/main/java/io/quarkiverse/flow/recorders/DiscoveredFlow.java +++ b/core/runtime/src/main/java/io/quarkiverse/flow/recorders/DiscoveredFlow.java @@ -9,13 +9,15 @@ public final class DiscoveredFlow { public final String className; public final String methodName; public final String workflowName; + public final String[] params; public final boolean isStatic; @RecordableConstructor - public DiscoveredFlow(String className, String methodName, String workflowName, boolean isStatic) { + public DiscoveredFlow(String className, String methodName, String workflowName, String[] params, boolean isStatic) { this.className = className; this.methodName = methodName; this.isStatic = isStatic; this.workflowName = workflowName; + this.params = params; } } diff --git a/core/runtime/src/main/java/io/quarkiverse/flow/recorders/FlowRecorder.java b/core/runtime/src/main/java/io/quarkiverse/flow/recorders/FlowRecorder.java index 7da9a78..4fef3ac 100644 --- a/core/runtime/src/main/java/io/quarkiverse/flow/recorders/FlowRecorder.java +++ b/core/runtime/src/main/java/io/quarkiverse/flow/recorders/FlowRecorder.java @@ -12,11 +12,7 @@ import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; -import io.serverlessworkflow.impl.events.EventConsumer; -import io.serverlessworkflow.impl.events.EventPublisher; -import io.serverlessworkflow.impl.executors.TaskExecutorFactory; import io.serverlessworkflow.impl.expressions.ExpressionFactory; -import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; // TODO: produce definitions from workflows in the YAML format within the current classpath @@ -34,12 +30,7 @@ public Supplier workflowAppSupplier(ShutdownContext shutdow return () -> { final ArcContainer container = Arc.container(); final WorkflowApplication.Builder builder = WorkflowApplication.builder(); - builder.withEventConsumer(container.instance(EventConsumer.class).get()) - .withExpressionFactory(container.instance(ExpressionFactory.class).get()) - .withSchemaValidatorFactory(container.instance(SchemaValidatorFactory.class).get()) - .withTaskExecutorFactory(container.instance(TaskExecutorFactory.class).get()); - for (var p : container.select(EventPublisher.class)) - builder.withEventPublisher(p); + builder.withExpressionFactory(container.instance(ExpressionFactory.class).get()); final WorkflowApplication app = builder.build(); shutdownContext.addShutdownTask(app::close); @@ -57,7 +48,7 @@ public Supplier workflowDefinitionSupplier(DiscoveredFlow d) final Object target = d.isStatic ? null : Arc.container().instance(owner).get(); - final MethodHandles.Lookup lookup = MethodHandles.lookup(); + final MethodHandles.Lookup lookup = MethodHandles.publicLookup(); final MethodType mt = MethodType.methodType(Workflow.class); final MethodHandle mh = d.isStatic ? lookup.findStatic(owner, d.methodName, mt) : lookup.findVirtual(owner, d.methodName, mt); diff --git a/pom.xml b/pom.xml index d8b5892..7bc9e4f 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ UTF-8 3.28.1 7.2.0.Final - 2.4.0 + 2.4.0 @@ -63,6 +63,16 @@ serverlessworkflow-impl-jackson ${io.serverlessworkflow.version} + + io.quarkiverse.jackson-jq + quarkus-jackson-jq + ${io.quarkiverse.jackson-jq.version} + + + io.quarkiverse.jackson-jq + quarkus-jackson-jq-deployment + ${io.quarkiverse.jackson-jq.version} +