This is an automated email from the ASF dual-hosted git repository.
gitgabrio pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-runtimes.git
The following commit(s) were added to refs/heads/main by this push:
new 070876ffdf [incubator-kie-issues#1473] Introduce Transactional
annotation, conditionally deleted (#3671)
070876ffdf is described below
commit 070876ffdf3be21c4f158925fc6c16f20b0476ab
Author: Gabriele Cardosi <[email protected]>
AuthorDate: Thu Oct 3 12:04:33 2024 +0200
[incubator-kie-issues#1473] Introduce Transactional annotation,
conditionally deleted (#3671)
* [incubator-kie-issues#1473] WIP - Implemented transactional imports and
annotations with optional removal. Add unit tests. Small refactoring to allow
unit testing/ TDD
* [incubator-kie-issues#1473] Simplified following suggestions.
Now the annotation is conditionally add during code generation.
* [incubator-kie-issues#1473] Implemented
KogitoContextTestUtils#restContextBuilders for rest-specific tests.
Enforced exclusion of "Java" context from processes' rest generation.
* [incubator-kie-issues#1473] Set log level to debug inside
manageTransactional
* [incubator-kie-issues#1473] Add spring-tx dependency to
jbpm-spring-boot-starter to be always available for springboot projects
* [incubator-kie-issues#1473] Add quarkus-narayana-jta dependency to
kogito-quarkus-workflow-common to be always available for quarkus projects
* [incubator-kie-issues#1473] Add quarkus-narayana-jta-deployment dependency
* [incubator-kie-issues#1473] Introduce nasty workaround (delay) to avoid
ARJUNA issues with nested threads
* [incubator-kie-issues#1473] Disabling transaction for
sonataflow-quarkus-integration-test
* [incubator-kie-issues#1473] Conditionally add the thread sleep. Fix
kogito.processes.transactionEnabled variable
* [incubator-kie-issues#1473] Importing new wait strategy.
* [incubator-kie-issues#1473] WIP - Experimenting Thread.join inside
transaction (instead of Thread.sleep)
* [incubator-kie-issues#1473] WIP - fix formatting
* [incubator-kie-issues#1473] WIP - commented out conditionally excluded
serverless transaction. Disable transaction workaround.
* [incubator-kie-issues#1473] WIP - commented out conditionally excluded
serverless transaction. Disable transaction workaround.
* [incubator-kie-issues#1473] Disabling transaction for serverless
* [incubator-kie-issues#1473] Fix formatting
* [incubator-kie-issues#1473] Fix test
* [incubator-kie-issues#1473] Fixed as per PR suggestion. Using
context.hasRest() instead of name.equals("Java") to check for rest endpoint
creation. Implemented MockQuarkusKogitoBuildContext and
MockSpringBootKogitoBuildContext to override the hasRest method during tests.
---------
Co-authored-by: Gabriele-Cardosi <[email protected]>
---
.../impl/MockQuarkusKogitoBuildContext.java | 51 ++++++
.../impl/MockSpringBootKogitoBuildContext.java | 50 ++++++
.../codegen/api/utils/KogitoContextTestUtils.java | 12 ++
.../kie/kogito/codegen/process/ProcessCodegen.java | 21 ++-
.../codegen/process/ProcessResourceGenerator.java | 199 +++++++++++++++------
.../RestResourceSpringTemplate.java | 3 +-
.../process/ProcessResourceGeneratorTest.java | 116 ++++++++++--
.../events/CodegenMessageStartEventTest.java | 4 +-
.../process/events/CodegenUserTaskTest.java | 2 +-
.../kogito-quarkus-processes-deployment/pom.xml | 4 +
.../pom.xml | 4 +
.../kogito-quarkus-workflow-common/pom.xml | 4 +
.../kogito-processes-spring-boot-starter/pom.xml | 4 +
13 files changed, 397 insertions(+), 77 deletions(-)
diff --git
a/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/context/impl/MockQuarkusKogitoBuildContext.java
b/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/context/impl/MockQuarkusKogitoBuildContext.java
new file mode 100644
index 0000000000..9972c2d7f0
--- /dev/null
+++
b/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/context/impl/MockQuarkusKogitoBuildContext.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.kie.kogito.codegen.api.context.impl;
+
+/**
+ * Mocked <code>QuarkusKogitoBuildContext</code> to provide <code>hasRest() =
true</code>
+ * during test
+ */
+public class MockQuarkusKogitoBuildContext extends QuarkusKogitoBuildContext {
+
+ protected
MockQuarkusKogitoBuildContext(MockQuarkusKogitoBuildContextBuilder builder) {
+ super(builder);
+ }
+
+ public static Builder builder() {
+ return new MockQuarkusKogitoBuildContextBuilder();
+ }
+
+ @Override
+ public boolean hasRest() {
+ return true;
+ }
+
+ protected static class MockQuarkusKogitoBuildContextBuilder extends
QuarkusKogitoBuildContextBuilder {
+
+ protected MockQuarkusKogitoBuildContextBuilder() {
+ }
+
+ @Override
+ public MockQuarkusKogitoBuildContext build() {
+ return new MockQuarkusKogitoBuildContext(this);
+ }
+
+ }
+}
\ No newline at end of file
diff --git
a/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/context/impl/MockSpringBootKogitoBuildContext.java
b/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/context/impl/MockSpringBootKogitoBuildContext.java
new file mode 100644
index 0000000000..32a865ebd1
--- /dev/null
+++
b/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/context/impl/MockSpringBootKogitoBuildContext.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.kie.kogito.codegen.api.context.impl;
+
+/**
+ * Mocked <code>SpringBootKogitoBuildContext</code> to provide <code>hasRest()
= true</code>
+ * during test
+ */
+public class MockSpringBootKogitoBuildContext extends
SpringBootKogitoBuildContext {
+
+ protected
MockSpringBootKogitoBuildContext(MockSpringBootKogitoBuildContextBuilder
builder) {
+ super(builder);
+ }
+
+ public static Builder builder() {
+ return new MockSpringBootKogitoBuildContextBuilder();
+ }
+
+ @Override
+ public boolean hasRest() {
+ return true;
+ }
+
+ protected static class MockSpringBootKogitoBuildContextBuilder extends
SpringBootKogitoBuildContextBuilder {
+
+ protected MockSpringBootKogitoBuildContextBuilder() {
+ }
+
+ @Override
+ public MockSpringBootKogitoBuildContext build() {
+ return new MockSpringBootKogitoBuildContext(this);
+ }
+ }
+}
\ No newline at end of file
diff --git
a/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/utils/KogitoContextTestUtils.java
b/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/utils/KogitoContextTestUtils.java
index e96c2c7a34..3c7a273ea1 100644
---
a/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/utils/KogitoContextTestUtils.java
+++
b/kogito-codegen-modules/kogito-codegen-api/src/test/java/org/kie/kogito/codegen/api/utils/KogitoContextTestUtils.java
@@ -25,6 +25,8 @@ import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;
import org.kie.kogito.codegen.api.context.KogitoBuildContext;
import org.kie.kogito.codegen.api.context.impl.JavaKogitoBuildContext;
+import org.kie.kogito.codegen.api.context.impl.MockQuarkusKogitoBuildContext;
+import
org.kie.kogito.codegen.api.context.impl.MockSpringBootKogitoBuildContext;
import org.kie.kogito.codegen.api.context.impl.QuarkusKogitoBuildContext;
import org.kie.kogito.codegen.api.context.impl.SpringBootKogitoBuildContext;
@@ -44,6 +46,16 @@ public class KogitoContextTestUtils {
Arguments.of(SpringBootKogitoBuildContext.builder()));
}
+ /**
+ *
+ * @return <b>Mocked</b> <code>QuarkusKogitoBuildContext</code> and
<code>SpringBootKogitoBuildContext</code> providing <code>hasRest() =
true</code>
+ */
+ public static Stream<Arguments> restContextBuilders() {
+ return Stream.of(
+ Arguments.of(MockQuarkusKogitoBuildContext.builder()),
+ Arguments.of(MockSpringBootKogitoBuildContext.builder()));
+ }
+
public static Predicate<String>
mockClassAvailabilityResolver(Collection<String> includedClasses,
Collection<String> excludedClasses) {
return mockClassAvailabilityResolver(includedClasses, excludedClasses,
KogitoContextTestUtils.class.getClassLoader());
}
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
index 6f5ba05f7a..c360ec5e8c 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
@@ -76,6 +76,7 @@ import
com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
+import static
org.kie.kogito.codegen.process.ProcessResourceGenerator.TRANSACTION_ENABLED;
import static
org.kie.kogito.grafana.GrafanaConfigurationWriter.buildDashboardName;
import static
org.kie.kogito.grafana.GrafanaConfigurationWriter.generateOperationalDashboard;
import static org.kie.kogito.internal.utils.ConversionUtils.sanitizeClassName;
@@ -352,20 +353,23 @@ public class ProcessCodegen extends AbstractGenerator {
ProcessMetaData metaData =
processIdToMetadata.get(workFlowProcess.getId());
- //Creating and adding the ResourceGenerator
- ProcessResourceGenerator processResourceGenerator = new
ProcessResourceGenerator(
+ //Creating and adding the ResourceGenerator for REST generation
+ if (context().hasRest()) {
+ ProcessResourceGenerator processResourceGenerator = new
ProcessResourceGenerator(
context(),
workFlowProcess,
modelClassGenerator.className(),
execModelGen.className(),
applicationCanonicalName());
- processResourceGenerator
+ processResourceGenerator
.withUserTasks(processIdToUserTaskModel.get(workFlowProcess.getId()))
.withSignals(metaData.getSignals())
- .withTriggers(metaData.isStartable(),
metaData.isDynamic(), metaData.getTriggers());
+ .withTriggers(metaData.isStartable(),
metaData.isDynamic(), metaData.getTriggers())
+ .withTransaction(isTransactionEnabled());
- rgs.add(processResourceGenerator);
+ rgs.add(processResourceGenerator);
+ }
if (metaData.getTriggers() != null) {
@@ -468,7 +472,7 @@ public class ProcessCodegen extends AbstractGenerator {
svgs.keySet().stream().forEach(key ->
storeFile(GeneratedFileType.INTERNAL_RESOURCE, "META-INF/processSVG/" + key +
".svg", svgs.get(key)));
}
- if (context().hasRESTForGenerator(this)) {
+ if (context().hasRest() && context().hasRESTForGenerator(this)) {
final ProcessCloudEventMetaFactoryGenerator topicsGenerator =
new ProcessCloudEventMetaFactoryGenerator(context(),
processExecutableModelGenerators);
storeFile(REST_TYPE, topicsGenerator.generatedFilePath(),
topicsGenerator.generate());
@@ -504,6 +508,11 @@ public class ProcessCodegen extends AbstractGenerator {
return generatedFiles;
}
+ protected boolean isTransactionEnabled() {
+ String processTransactionProperty = String.format("kogito.%s.%s",
GENERATOR_NAME, TRANSACTION_ENABLED);
+ return
"true".equalsIgnoreCase(context().getApplicationProperty(processTransactionProperty).orElse("true"));
+ }
+
private void storeFile(GeneratedFileType type, String path, String source)
{
if (generatedFiles.stream().anyMatch(f ->
path.equals(f.relativePath()))) {
LOGGER.warn("There's already a generated file named {} to be
compiled. Ignoring.", path);
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java
index 90ab0277b0..165477103f 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java
@@ -29,6 +29,8 @@ import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
+import org.drools.codegen.common.di.DependencyInjectionAnnotator;
+import org.drools.codegen.common.rest.RestAnnotator;
import org.drools.util.StringUtils;
import org.jbpm.compiler.canonical.ProcessToExecModelGenerator;
import org.jbpm.compiler.canonical.TriggerMetaData;
@@ -44,6 +46,8 @@ import org.kie.kogito.codegen.core.CodegenUtils;
import org.kie.kogito.codegen.core.GeneratorConfig;
import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess;
import org.kie.kogito.internal.utils.ConversionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier.Keyword;
@@ -73,6 +77,15 @@ import static
org.kie.kogito.internal.utils.ConversionUtils.sanitizeJavaName;
*/
public class ProcessResourceGenerator {
+ /**
+ * Flag used to configure transaction enablement. Default to
<code>true</code>
+ */
+ public static final String TRANSACTION_ENABLED = "transactionEnabled";
+
+ static final String INVALID_CONTEXT_TEMPLATE = "ProcessResourceGenerator
can't be used for context without Rest %s";
+
+ private static final Logger LOG =
LoggerFactory.getLogger(ProcessResourceGenerator.class);
+
private static final String REST_TEMPLATE_NAME = "RestResource";
private static final String REACTIVE_REST_TEMPLATE_NAME =
"ReactiveRestResource";
private static final String REST_USER_TASK_TEMPLATE_NAME =
"RestResourceUserTask";
@@ -93,6 +106,7 @@ public class ProcessResourceGenerator {
private boolean startable;
private boolean dynamic;
+ private boolean transactionEnabled;
private List<TriggerMetaData> triggers;
private List<UserTaskModelMetaData> userTasks;
@@ -106,6 +120,9 @@ public class ProcessResourceGenerator {
String modelfqcn,
String processfqcn,
String appCanonicalName) {
+ if (!context.hasRest()) {
+ throw new
IllegalArgumentException(String.format(INVALID_CONTEXT_TEMPLATE,
context.name()));
+ }
this.context = context;
this.process = process;
this.processId = process.getId();
@@ -134,6 +151,11 @@ public class ProcessResourceGenerator {
return this;
}
+ public ProcessResourceGenerator withTransaction(boolean
transactionEnabled) {
+ this.transactionEnabled = transactionEnabled;
+ return this;
+ }
+
public String getTaskModelFactory() {
return taskModelFactoryUnit.toString();
}
@@ -149,27 +171,104 @@ public class ProcessResourceGenerator {
protected String getRestTemplateName() {
boolean isReactiveGenerator =
"reactive".equals(context.getApplicationProperty(GeneratorConfig.KOGITO_REST_RESOURCE_TYPE_PROP)
.orElse(""));
- boolean isQuarkus =
context.name().equals(QuarkusKogitoBuildContext.CONTEXT_NAME);
+ return isQuarkus() && isReactiveGenerator ?
REACTIVE_REST_TEMPLATE_NAME : REST_TEMPLATE_NAME;
+ }
- return isQuarkus && isReactiveGenerator ? REACTIVE_REST_TEMPLATE_NAME
: REST_TEMPLATE_NAME;
+ protected boolean isQuarkus() {
+ return context.name().equals(QuarkusKogitoBuildContext.CONTEXT_NAME);
}
public String generate() {
- TemplatedGenerator.Builder templateBuilder =
TemplatedGenerator.builder()
- .withFallbackContext(QuarkusKogitoBuildContext.CONTEXT_NAME);
- CompilationUnit clazz = templateBuilder.build(context,
getRestTemplateName())
- .compilationUnitOrThrow();
- clazz.setPackageDeclaration(process.getPackageName());
- clazz.addImport(modelfqcn);
- clazz.addImport(modelfqcn + "Output");
- clazz.addImport(modelfqcn + "Input");
- ClassOrInterfaceDeclaration template = clazz
+ return getCompilationUnit().toString();
+ }
+
+ protected CompilationUnit getCompilationUnit() {
+ TemplatedGenerator.Builder templateBuilder =
createTemplatedGeneratorBuilder();
+ CompilationUnit toReturn = createCompilationUnit(templateBuilder);
+ addPackageAndImports(toReturn);
+ ClassOrInterfaceDeclaration template = toReturn
.findFirst(ClassOrInterfaceDeclaration.class)
.orElseThrow(() -> new NoSuchElementException("Compilation
unit doesn't contain a class or interface declaration!"));
template.setName(resourceClazzName);
AtomicInteger index = new AtomicInteger(0);
//Generate signals endpoints
+ generateSignalsEndpoints(templateBuilder, template, index);
+
+ // security must be applied before user tasks are added to make sure
that user task
+ // endpoints are not security annotated as they should restrict access
based on user assignments
+ securityAnnotated(template);
+
+ Map<String, String> typeInterpolations = new HashMap<>();
+ taskModelFactoryUnit =
parse(this.getClass().getResourceAsStream("/class-templates/TaskModelFactoryTemplate"
+
+ ".java"));
+ String taskModelFactorySimpleClassName =
+
sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_"
+ "TaskModelFactory");
+ taskModelFactoryUnit.setPackageDeclaration(process.getPackageName());
+ taskModelFactoryClassName = process.getPackageName() + "." +
taskModelFactorySimpleClassName;
+ ClassOrInterfaceDeclaration taskModelFactoryClass =
+
taskModelFactoryUnit.findFirst(ClassOrInterfaceDeclaration.class).orElseThrow(IllegalStateException::new);
+ taskModelFactoryClass.setName(taskModelFactorySimpleClassName);
+ typeInterpolations.put("$TaskModelFactory$",
taskModelFactoryClassName);
+
+ manageUserTasks(templateBuilder, template, taskModelFactoryClass,
index);
+
+ typeInterpolations.put("$Clazz$", resourceClazzName);
+ typeInterpolations.put("$Type$", dataClazzName);
+
template.findAll(StringLiteralExpr.class).forEach(this::interpolateStrings);
+ template.findAll(ClassOrInterfaceType.class).forEach(cls ->
interpolateTypes(cls, typeInterpolations));
+
+ TagResourceGenerator.addTags(toReturn, process, context);
+
+
template.findAll(MethodDeclaration.class).forEach(this::interpolateMethods);
+
+ if (context.hasDI()) {
+ template.findAll(FieldDeclaration.class,
+ CodegenUtils::isProcessField).forEach(fd ->
context.getDependencyInjectionAnnotator().withNamedInjection(fd, processId));
+ } else {
+ template.findAll(FieldDeclaration.class,
+
CodegenUtils::isProcessField).forEach(this::initializeProcessField);
+ }
+
+ // if triggers are not empty remove createResource method as there is
another trigger to start process instances
+ if ((!startable && !dynamic) || !isPublic()) {
+ Optional<MethodDeclaration> createResourceMethod =
+ template.findFirst(MethodDeclaration.class).filter(md ->
md.getNameAsString().equals(
+ "createResource_" + processName));
+ createResourceMethod.ifPresent(template::remove);
+ }
+
+ if (context.hasDI()) {
+
context.getDependencyInjectionAnnotator().withApplicationComponent(template);
+ }
+
+ enableValidation(template);
+
+ manageTransactional(toReturn);
+
+ template.getMembers().sort(new BodyDeclarationComparator());
+ return toReturn;
+ }
+
+ protected TemplatedGenerator.Builder createTemplatedGeneratorBuilder() {
+ return TemplatedGenerator.builder()
+ .withFallbackContext(QuarkusKogitoBuildContext.CONTEXT_NAME);
+ }
+
+ protected CompilationUnit createCompilationUnit(TemplatedGenerator.Builder
templateBuilder) {
+ return templateBuilder.build(context, getRestTemplateName())
+ .compilationUnitOrThrow();
+ }
+
+ protected void addPackageAndImports(CompilationUnit compilationUnit) {
+ compilationUnit.setPackageDeclaration(process.getPackageName());
+ compilationUnit.addImport(modelfqcn);
+ compilationUnit.addImport(modelfqcn + "Output");
+ compilationUnit.addImport(modelfqcn + "Input");
+ }
+
+ protected void generateSignalsEndpoints(TemplatedGenerator.Builder
templateBuilder,
+ ClassOrInterfaceDeclaration template, AtomicInteger index) {
Optional.ofNullable(signals)
.ifPresent(signalsMap -> {
//using template class to the endpoints generation
@@ -265,20 +364,10 @@ public class ProcessResourceGenerator {
});
});
});
+ }
- // security must be applied before user tasks are added to make sure
that user task
- // endpoints are not security annotated as they should restrict access
based on user assignments
- securityAnnotated(template);
-
- Map<String, String> typeInterpolations = new HashMap<>();
- taskModelFactoryUnit =
parse(this.getClass().getResourceAsStream("/class-templates/TaskModelFactoryTemplate.java"));
- String taskModelFactorySimpleClassName =
sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_"
+ "TaskModelFactory");
- taskModelFactoryUnit.setPackageDeclaration(process.getPackageName());
- taskModelFactoryClassName = process.getPackageName() + "." +
taskModelFactorySimpleClassName;
- ClassOrInterfaceDeclaration taskModelFactoryClass =
taskModelFactoryUnit.findFirst(ClassOrInterfaceDeclaration.class).orElseThrow(IllegalStateException::new);
- taskModelFactoryClass.setName(taskModelFactorySimpleClassName);
- typeInterpolations.put("$TaskModelFactory$",
taskModelFactoryClassName);
-
+ protected void manageUserTasks(TemplatedGenerator.Builder templateBuilder,
ClassOrInterfaceDeclaration template,
+ ClassOrInterfaceDeclaration taskModelFactoryClass, AtomicInteger
index) {
if (userTasks != null && !userTasks.isEmpty()) {
CompilationUnit userTaskClazz = templateBuilder.build(context,
REST_USER_TASK_TEMPLATE_NAME).compilationUnitOrThrow();
@@ -310,44 +399,42 @@ public class ProcessResourceGenerator {
template.findAll(MethodDeclaration.class)
.stream()
.filter(md ->
md.getNameAsString().equals(SIGNAL_METHOD_PREFFIX + methodSuffix))
-
.collect(Collectors.toList()).forEach(template::remove);
+ .forEach(template::remove);
}
switchExpr.getEntries().add(0, userTask.getModelSwitchEntry());
}
}
+ }
- typeInterpolations.put("$Clazz$", resourceClazzName);
- typeInterpolations.put("$Type$", dataClazzName);
-
template.findAll(StringLiteralExpr.class).forEach(this::interpolateStrings);
- template.findAll(ClassOrInterfaceType.class).forEach(cls ->
interpolateTypes(cls, typeInterpolations));
-
- TagResourceGenerator.addTags(clazz, process, context);
-
-
template.findAll(MethodDeclaration.class).forEach(this::interpolateMethods);
-
- if (context.hasDI()) {
- template.findAll(FieldDeclaration.class,
- CodegenUtils::isProcessField).forEach(fd ->
context.getDependencyInjectionAnnotator().withNamedInjection(fd, processId));
- } else {
- template.findAll(FieldDeclaration.class,
-
CodegenUtils::isProcessField).forEach(this::initializeProcessField);
- }
-
- // if triggers are not empty remove createResource method as there is
another trigger to start process instances
- if ((!startable && !dynamic) || !isPublic()) {
- Optional<MethodDeclaration> createResourceMethod =
template.findFirst(MethodDeclaration.class).filter(md ->
md.getNameAsString().equals("createResource_" + processName));
- createResourceMethod.ifPresent(template::remove);
- }
-
- if (context.hasDI()) {
-
context.getDependencyInjectionAnnotator().withApplicationComponent(template);
+ /**
+ * Conditionally add the <code>Transactional</code> annotation
+ *
+ * @param compilationUnit
+ *
+ */
+ protected void manageTransactional(CompilationUnit compilationUnit) {
+ if (transactionEnabled && context.hasDI() && !isServerless()) { //
disabling transaction for serverless
+ LOG.debug("Transaction is enabled, adding annotations...");
+ DependencyInjectionAnnotator dependencyInjectionAnnotator =
context.getDependencyInjectionAnnotator();
+ getRestMethods(compilationUnit)
+ .forEach(dependencyInjectionAnnotator::withTransactional);
}
+ }
- enableValidation(template);
-
- template.getMembers().sort(new BodyDeclarationComparator());
- return clazz.toString();
+ /**
+ * Retrieves all the <b>Rest endpoint</b> <code>MethodDeclaration</code>s
from the given
+ * <code>CompilationUnit</code>
+ *
+ * @param compilationUnit
+ * @return
+ */
+ protected Collection<MethodDeclaration> getRestMethods(CompilationUnit
compilationUnit) {
+ RestAnnotator restAnnotator = context.getRestAnnotator();
+ return compilationUnit.findAll(MethodDeclaration.class)
+ .stream()
+ .filter(restAnnotator::isRestAnnotated)
+ .toList();
}
private void securityAnnotated(ClassOrInterfaceDeclaration template) {
@@ -468,4 +555,8 @@ public class ProcessResourceGenerator {
protected boolean isPublic() {
return
KogitoWorkflowProcess.PUBLIC_VISIBILITY.equalsIgnoreCase(process.getVisibility());
}
+
+ protected boolean isServerless() {
+ return
KogitoWorkflowProcess.SW_TYPE.equalsIgnoreCase(process.getType());
+ }
}
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceSpringTemplate.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceSpringTemplate.java
index 9c15e01b47..190a38af3b 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceSpringTemplate.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceSpringTemplate.java
@@ -43,6 +43,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -55,8 +56,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
-import org.springframework.http.ResponseEntity;
-import org.springframework.http.HttpStatus;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java
index 57b4fa9a49..52d1665166 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java
@@ -24,14 +24,17 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
+import org.assertj.core.api.ListAssert;
import org.drools.io.FileSystemResource;
import org.jbpm.compiler.canonical.ProcessMetaData;
import org.jbpm.compiler.canonical.ProcessToExecModelGenerator;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.kie.api.definition.process.Process;
import org.kie.kogito.codegen.api.AddonsConfig;
import org.kie.kogito.codegen.api.context.KogitoBuildContext;
+import org.kie.kogito.codegen.api.context.impl.JavaKogitoBuildContext;
import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess;
import com.github.javaparser.StaticJavaParser;
@@ -45,14 +48,29 @@ import com.github.javaparser.ast.expr.NormalAnnotationExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static
org.kie.kogito.codegen.process.ProcessResourceGenerator.INVALID_CONTEXT_TEMPLATE;
class ProcessResourceGeneratorTest {
+
private static final String SIGNAL_METHOD = "signal_";
private static final List<String> JAVA_AND_QUARKUS_REST_ANNOTATIONS =
List.of("DELETE", "GET", "POST");
private static final List<String> SPRING_BOOT_REST_ANNOTATIONS =
List.of("DeleteMapping", "GetMapping", "PostMapping");
+ @Test
+ void testProcessResourceGeneratorForJava() {
+ KogitoBuildContext.Builder contextBuilder =
JavaKogitoBuildContext.builder();
+ String fileName =
"src/test/resources/startsignal/StartSignalEventNoPayload.bpmn2"; // not
relevant
+ boolean transactionEnabled = true; // not relevant
+ String expectedMessage = String.format(INVALID_CONTEXT_TEMPLATE,
JavaKogitoBuildContext.CONTEXT_NAME);
+ assertThatThrownBy(() -> getProcessResourceGenerator(contextBuilder,
fileName,
+ transactionEnabled))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining(expectedMessage);
+ }
+
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
void testGenerateProcessWithDocumentation(KogitoBuildContext.Builder
contextBuilder) {
String fileName = "src/test/resources/ProcessWithDocumentation.bpmn";
String expectedSummary = "This is the documentation";
@@ -62,7 +80,7 @@ class ProcessResourceGeneratorTest {
}
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
void testGenerateProcessWithoutDocumentation(KogitoBuildContext.Builder
contextBuilder) {
String fileName =
"src/test/resources/ProcessWithoutDocumentation.bpmn";
String expectedSummary = "ProcessWithoutDocumentation";
@@ -72,7 +90,7 @@ class ProcessResourceGeneratorTest {
}
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
void testGenerateBoundarySignalEventOnTask(KogitoBuildContext.Builder
contextBuilder) {
String fileName =
"src/test/resources/signalevent/BoundarySignalEventOnTask.bpmn2";
@@ -88,7 +106,7 @@ class ProcessResourceGeneratorTest {
}
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
void testGenerateStartSignalEventStringPayload(KogitoBuildContext.Builder
contextBuilder) {
String fileName =
"src/test/resources/startsignal/StartSignalEventStringPayload.bpmn2";
String signalName = "start";
@@ -121,7 +139,7 @@ class ProcessResourceGeneratorTest {
}
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
void testGenerateStartSignalEventNoPayload(KogitoBuildContext.Builder
contextBuilder) {
String fileName =
"src/test/resources/startsignal/StartSignalEventNoPayload.bpmn2";
String signalName = "start";
@@ -152,6 +170,68 @@ class ProcessResourceGeneratorTest {
.forEach(method -> assertMethodOutputModelType(method,
outputType));
}
+ @ParameterizedTest
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
+ void testManageTransactionalEnabled(KogitoBuildContext.Builder
contextBuilder) {
+ String fileName =
"src/test/resources/startsignal/StartSignalEventNoPayload.bpmn2";
+
+ boolean transactionEnabled = true;
+ ProcessResourceGenerator processResourceGenerator =
getProcessResourceGenerator(contextBuilder, fileName,
+ transactionEnabled);
+ CompilationUnit compilationUnit =
+
processResourceGenerator.createCompilationUnit(processResourceGenerator.createTemplatedGeneratorBuilder());
+ assertThat(compilationUnit).isNotNull();
+ KogitoBuildContext kogitoBuildContext = contextBuilder.build();
+ Collection<MethodDeclaration> restEndpoints =
processResourceGenerator.getRestMethods(compilationUnit);
+ // before processResourceGenerator.manageTransactional, the annotation
is not there
+ testTransaction(restEndpoints, kogitoBuildContext, false);
+ processResourceGenerator.manageTransactional(compilationUnit);
+ // the annotation is (conditionally) add after
processResourceGenerator.manageTransactional
+ testTransaction(restEndpoints, kogitoBuildContext, transactionEnabled);
+ }
+
+ @ParameterizedTest
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
+ void testManageTransactionalDisabled(KogitoBuildContext.Builder
contextBuilder) {
+ String fileName =
"src/test/resources/startsignal/StartSignalEventNoPayload.bpmn2";
+ boolean transactionEnabled = false;
+ ProcessResourceGenerator processResourceGenerator =
getProcessResourceGenerator(contextBuilder, fileName,
+ transactionEnabled);
+ CompilationUnit compilationUnit =
+
processResourceGenerator.createCompilationUnit(processResourceGenerator.createTemplatedGeneratorBuilder());
+ assertThat(compilationUnit).isNotNull();
+ KogitoBuildContext kogitoBuildContext = contextBuilder.build();
+ Collection<MethodDeclaration> restEndpoints =
processResourceGenerator.getRestMethods(compilationUnit);
+ // before processResourceGenerator.manageTransactional, the annotation
is not there
+ testTransaction(restEndpoints, kogitoBuildContext, false);
+ processResourceGenerator.manageTransactional(compilationUnit);
+ // the annotation is (conditionally) add after
processResourceGenerator.manageTransactional
+ testTransaction(restEndpoints, kogitoBuildContext, transactionEnabled);
+ }
+
+ void testTransaction(Collection<MethodDeclaration> restEndpoints,
+ KogitoBuildContext kogitoBuildContext,
+ boolean enabled) {
+ String transactionalAnnotation =
+
kogitoBuildContext.getDependencyInjectionAnnotator().getTransactionalAnnotation();
+ restEndpoints.forEach(methodDeclaration -> {
+ ListAssert<?> transactionAnnotationAssert = assertThat(
+
methodDeclaration.getAnnotations().stream().filter(annotationExpr ->
annotationExpr.getNameAsString().equals(transactionalAnnotation)));
+ if (enabled) {
+ transactionAnnotationAssert.hasSize(1);
+ } else {
+ transactionAnnotationAssert.isEmpty();
+ }
+ if
(methodDeclaration.getName().toString().startsWith("createResource_")) {
+ ListAssert<?> stmtsAsserts = assertThat(
+ methodDeclaration.getBody()
+ .get()
+ .getStatements());
+ stmtsAsserts.hasSize(2);
+ }
+ });
+ }
+
void testOpenApiDocumentation(KogitoBuildContext.Builder contextBuilder,
String fileName, String expectedSummary, String expectedDescription) {
ClassOrInterfaceDeclaration classDeclaration =
getResourceClassDeclaration(contextBuilder, fileName);
@@ -168,15 +248,27 @@ class ProcessResourceGeneratorTest {
return classDeclaration.orElseThrow();
}
- private CompilationUnit getCompilationUnit(KogitoBuildContext.Builder
contextBuilder, KogitoWorkflowProcess process) {
+ private CompilationUnit getCompilationUnit(KogitoBuildContext.Builder
contextBuilder,
+ KogitoWorkflowProcess process) {
+ ProcessResourceGenerator processResourceGenerator =
getProcessResourceGenerator(contextBuilder, process, true);
+ return StaticJavaParser.parse(processResourceGenerator.generate());
+ }
+
+ private ProcessResourceGenerator
getProcessResourceGenerator(KogitoBuildContext.Builder contextBuilder,
+ String fileName, boolean withTransaction) {
+ return getProcessResourceGenerator(contextBuilder,
parseProcess(fileName), withTransaction);
+ }
+
+ private ProcessResourceGenerator
getProcessResourceGenerator(KogitoBuildContext.Builder contextBuilder,
+ KogitoWorkflowProcess process,
+ boolean withTransaction) {
KogitoBuildContext context = createContext(contextBuilder);
ProcessExecutableModelGenerator execModelGen =
new ProcessExecutableModelGenerator(process, new
ProcessToExecModelGenerator(context.getClassLoader()));
KogitoWorkflowProcess workFlowProcess = execModelGen.process();
-
- ProcessResourceGenerator processResourceGenerator = new
ProcessResourceGenerator(
+ ProcessResourceGenerator toReturn = new ProcessResourceGenerator(
context,
workFlowProcess,
new ModelClassGenerator(context, workFlowProcess).className(),
@@ -185,11 +277,11 @@ class ProcessResourceGeneratorTest {
ProcessMetaData metaData = execModelGen.generate();
- processResourceGenerator
+ toReturn
.withSignals(metaData.getSignals())
- .withTriggers(metaData.isStartable(), metaData.isDynamic(),
metaData.getTriggers());
-
- return StaticJavaParser.parse(processResourceGenerator.generate());
+ .withTriggers(metaData.isStartable(), metaData.isDynamic(),
metaData.getTriggers())
+ .withTransaction(withTransaction);
+ return toReturn;
}
private void assertThatMethodHasOpenApiDocumentation(MethodDeclaration
method, String summary, String description) {
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenMessageStartEventTest.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenMessageStartEventTest.java
index 6badf99806..f8c540a242 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenMessageStartEventTest.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenMessageStartEventTest.java
@@ -49,7 +49,7 @@ public class CodegenMessageStartEventTest {
private static final Path MESSAGE_START_END_EVENT_SOURCE_FULL_SOURCE =
BASE_PATH.resolve(MESSAGE_START_END_EVENT_SOURCE);
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
public void testRESTApiForMessageStartEvent(KogitoBuildContext.Builder
contextBuilder) {
KogitoBuildContext context = contextBuilder.build();
@@ -80,7 +80,7 @@ public class CodegenMessageStartEventTest {
}
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
public void testRESTApiForMessageEndEvent(KogitoBuildContext.Builder
contextBuilder) {
KogitoBuildContext context = contextBuilder.build();
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenUserTaskTest.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenUserTaskTest.java
index 271f9c897f..24f875c2a0 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenUserTaskTest.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/events/CodegenUserTaskTest.java
@@ -43,7 +43,7 @@ public class CodegenUserTaskTest {
private static final Path MESSAGE_USERTASK_SOURCE_FULL_SOURCE =
BASE_PATH.resolve(MESSAGE_USERTASK_SOURCE);
@ParameterizedTest
-
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders")
public void testRESTApiForMessageStartEvent(KogitoBuildContext.Builder
contextBuilder) {
KogitoBuildContext context = contextBuilder.build();
diff --git
a/quarkus/extensions/kogito-quarkus-processes-extension/kogito-quarkus-processes-deployment/pom.xml
b/quarkus/extensions/kogito-quarkus-processes-extension/kogito-quarkus-processes-deployment/pom.xml
index 9acc49370f..6053372826 100644
---
a/quarkus/extensions/kogito-quarkus-processes-extension/kogito-quarkus-processes-deployment/pom.xml
+++
b/quarkus/extensions/kogito-quarkus-processes-extension/kogito-quarkus-processes-deployment/pom.xml
@@ -45,6 +45,10 @@
<groupId>org.jbpm</groupId>
<artifactId>jbpm-quarkus</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-narayana-jta-deployment</artifactId>
+ </dependency>
<!-- test -->
<dependency>
<groupId>io.quarkus</groupId>
diff --git
a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/pom.xml
b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/pom.xml
index e74b1c102b..5caf3a85a6 100644
---
a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/pom.xml
+++
b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/pom.xml
@@ -68,6 +68,10 @@
<groupId>io.quarkiverse.jackson-jq</groupId>
<artifactId>quarkus-jackson-jq-deployment</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-narayana-jta-deployment</artifactId>
+ </dependency>
<!-- Quarkus Deployment Extensions Dependencies -->
<dependency>
diff --git
a/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/pom.xml
b/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/pom.xml
index 6911874239..736a415858 100644
---
a/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/pom.xml
+++
b/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/pom.xml
@@ -45,6 +45,10 @@
<groupId>io.smallrye.reactive</groupId>
<artifactId>smallrye-mutiny-vertx-web-client</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-narayana-jta</artifactId>
+ </dependency>
<dependency>
<groupId>org.kie.kogito</groupId>
<artifactId>jbpm-deps-group-engine</artifactId>
diff --git a/springboot/starters/kogito-processes-spring-boot-starter/pom.xml
b/springboot/starters/kogito-processes-spring-boot-starter/pom.xml
index c29c25ebe1..cd9817e03e 100644
--- a/springboot/starters/kogito-processes-spring-boot-starter/pom.xml
+++ b/springboot/starters/kogito-processes-spring-boot-starter/pom.xml
@@ -56,6 +56,10 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]