Skip to content

Commit f203bfc

Browse files
fix(services): no job workers are generated for ExecutionListener (#762)
Related to: #540 --------- Co-authored-by: gergely.juhasz@camunda.com <gergely.juhasz@camunda.com>
1 parent 91ef95e commit f203bfc

File tree

8 files changed

+721
-2
lines changed

8 files changed

+721
-2
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
3+
* one or more contributor license agreements. See the NOTICE file distributed
4+
* with this work for additional information regarding copyright ownership.
5+
* Licensed under the Camunda License 1.0. You may not use this file
6+
* except in compliance with the Camunda License 1.0.
7+
*/
8+
package io.camunda.migration.code.recipes.delegate;
9+
10+
import java.util.Collections;
11+
import java.util.List;
12+
import java.util.stream.Collectors;
13+
import org.openrewrite.*;
14+
import org.openrewrite.java.*;
15+
import org.openrewrite.java.search.UsesType;
16+
import org.openrewrite.java.tree.*;
17+
import org.openrewrite.jgit.annotations.NonNull;
18+
19+
public class CleanupExecutionListenerRecipe extends Recipe {
20+
21+
/** Instantiates a new instance. */
22+
public CleanupExecutionListenerRecipe() {}
23+
24+
@Override
25+
public String getDisplayName() {
26+
return "Removes ExecutionListener-related code";
27+
}
28+
29+
@Override
30+
public String getDescription() {
31+
return "Removes ExecutionListener-related code.";
32+
}
33+
34+
@Override
35+
public TreeVisitor<?, ExecutionContext> getVisitor() {
36+
37+
// define preconditions
38+
TreeVisitor<?, ExecutionContext> check =
39+
new UsesType<>("org.camunda.bpm.engine.delegate.ExecutionListener", true);
40+
41+
return Preconditions.check(
42+
check,
43+
new JavaIsoVisitor<>() {
44+
45+
@Override
46+
@NonNull
47+
public J.ClassDeclaration visitClassDeclaration(
48+
@NonNull J.ClassDeclaration classDecl, ExecutionContext ctx) {
49+
50+
// Skip interfaces
51+
if (classDecl.getKind() != J.ClassDeclaration.Kind.Type.Class) {
52+
return classDecl;
53+
}
54+
55+
// Filter out the ExecutionListener interface
56+
List<TypeTree> updatedImplements = classDecl.getImplements() == null ? Collections.emptyList() :
57+
classDecl.getImplements().stream()
58+
.filter(
59+
id ->
60+
!TypeUtils.isOfClassType(
61+
id.getType(), "org.camunda.bpm.engine.delegate.ExecutionListener"))
62+
.collect(Collectors.toList());
63+
64+
// Filter out the notify method
65+
List<Statement> filteredStatements =
66+
classDecl.getBody().getStatements().stream()
67+
.filter(
68+
(statement ->
69+
!(statement instanceof J.MethodDeclaration methDecl
70+
&& methDecl.getSimpleName().equals("notify"))))
71+
.toList();
72+
73+
maybeRemoveImport("org.camunda.bpm.engine.delegate.ExecutionListener");
74+
maybeRemoveImport("org.camunda.bpm.engine.delegate.DelegateExecution");
75+
76+
return classDecl
77+
.withBody(classDecl.getBody().withStatements(filteredStatements))
78+
.withImplements(updatedImplements.isEmpty() ? null : updatedImplements);
79+
}
80+
});
81+
}
82+
}
83+

code-conversion/recipes/src/main/java/io/camunda/migration/code/recipes/delegate/MigrateExecutionRecipe.java

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public String getDescription() {
3939
public List<Recipe> getRecipeList() {
4040
return List.of(
4141
new CopyDelegateToJobWorkerRecipe(),
42+
new CopyExecutionListenerToJobWorkerRecipe(),
4243
new MigrateDelegateExecutionMethodsInJobWorker(),
4344
new MigrateDelegateBPMNErrorAndExceptionInJobWorker());
4445
}
@@ -129,6 +130,83 @@ public J.ClassDeclaration visitClassDeclaration(
129130
}
130131
}
131132

133+
private static class CopyExecutionListenerToJobWorkerRecipe extends Recipe {
134+
135+
public CopyExecutionListenerToJobWorkerRecipe() {}
136+
137+
@Override
138+
public String getDisplayName() {
139+
return "Copy ExecutionListener code to job worker recipe";
140+
}
141+
142+
@Override
143+
public String getDescription() {
144+
return "Copies ExecutionListener notify() logic into the generated job worker method.";
145+
}
146+
147+
@Override
148+
public TreeVisitor<?, ExecutionContext> getVisitor() {
149+
150+
TreeVisitor<?, ExecutionContext> precondition =
151+
Preconditions.and(
152+
new UsesType<>("io.camunda.client.annotation.JobWorker", true),
153+
new UsesType<>("org.camunda.bpm.engine.delegate.ExecutionListener", true));
154+
155+
return Preconditions.check(
156+
precondition,
157+
new JavaIsoVisitor<ExecutionContext>() {
158+
@Override
159+
public J.ClassDeclaration visitClassDeclaration(
160+
J.ClassDeclaration classDecl, ExecutionContext ctx) {
161+
162+
if (classDecl.getKind() != J.ClassDeclaration.Kind.Type.Class) {
163+
return super.visitClassDeclaration(classDecl, ctx);
164+
}
165+
166+
List<Statement> current = classDecl.getBody().getStatements();
167+
List<Statement> updated = new ArrayList<>();
168+
169+
// 1) find notify(...) body
170+
J.Block notifyBody = null;
171+
for (Statement stmt : current) {
172+
if (stmt instanceof J.MethodDeclaration m
173+
&& "notify".equals(m.getSimpleName())) {
174+
notifyBody = m.getBody();
175+
}
176+
}
177+
178+
if (notifyBody != null) {
179+
for (Statement stmt : current) {
180+
if (stmt instanceof J.MethodDeclaration m
181+
&& "executeJob".equals(m.getSimpleName())) {
182+
183+
J.Block jobBody = m.getBody();
184+
List<Statement> jobStmts = jobBody.getStatements();
185+
List<Statement> listenerStmts =
186+
new ArrayList<>(notifyBody.getStatements());
187+
188+
// keep resultMap init + return, wrap listener logic in between
189+
listenerStmts.add(0, jobStmts.get(0));
190+
listenerStmts.add(jobStmts.get(jobStmts.size() - 1));
191+
192+
updated.add(
193+
m.withBody(jobBody.withStatements(listenerStmts))
194+
.withName(m.getName().withSimpleName("executeJobMigrated"))
195+
.withMethodType(
196+
m.getMethodType().withName("executeJobMigrated")));
197+
} else {
198+
updated.add(stmt);
199+
}
200+
}
201+
return classDecl.withBody(classDecl.getBody().withStatements(updated));
202+
}
203+
204+
return super.visitClassDeclaration(classDecl, ctx);
205+
}
206+
});
207+
}
208+
}
209+
132210
private static class MigrateDelegateExecutionMethodsInJobWorker extends AbstractMigrationRecipe {
133211

134212
@Override
@@ -145,14 +223,18 @@ public String getDescription() {
145223
protected TreeVisitor<?, ExecutionContext> preconditions() {
146224
return Preconditions.and(
147225
new UsesType<>("io.camunda.client.annotation.JobWorker", true),
148-
new UsesType<>("org.camunda.bpm.engine.delegate.JavaDelegate", true));
226+
Preconditions.or(
227+
new UsesType<>("org.camunda.bpm.engine.delegate.JavaDelegate", true),
228+
new UsesType<>("org.camunda.bpm.engine.delegate.ExecutionListener", true)));
149229
}
150230

151231
@Override
152232
protected Predicate<Cursor> visitorSkipCondition() {
153233
return cursor -> {
154234
J.MethodDeclaration m = cursor.firstEnclosing(J.MethodDeclaration.class);
155-
return m != null && "execute".equals(m.getSimpleName());
235+
return m != null
236+
&& ("execute".equals(m.getSimpleName())
237+
|| "notify".equals(m.getSimpleName()));
156238
};
157239
}
158240

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
3+
* one or more contributor license agreements. See the NOTICE file distributed
4+
* with this work for additional information regarding copyright ownership.
5+
* Licensed under the Camunda License 1.0. You may not use this file
6+
* except in compliance with the Camunda License 1.0.
7+
*/
8+
package io.camunda.migration.code.recipes.delegate;
9+
10+
import java.util.List;
11+
import io.camunda.migration.code.recipes.utils.RecipeUtils;
12+
import org.openrewrite.*;
13+
import org.openrewrite.java.*;
14+
import org.openrewrite.java.search.UsesType;
15+
import org.openrewrite.java.tree.*;
16+
17+
public class PrepareJobWorkerBeneathExecutionListenerRecipe extends Recipe {
18+
19+
public PrepareJobWorkerBeneathExecutionListenerRecipe() {}
20+
21+
@Override
22+
public String getDisplayName() {
23+
return "Injects a job worker prototype for ExecutionListener";
24+
}
25+
26+
@Override
27+
public String getDescription() {
28+
return "Injects a job worker prototype beneath ExecutionListener implementations.";
29+
}
30+
31+
@Override
32+
public TreeVisitor<?, ExecutionContext> getVisitor() {
33+
TreeVisitor<?, ExecutionContext> precondition =
34+
Preconditions.and(
35+
Preconditions.not(new UsesType<>("io.camunda.client.api.response.ActivatedJob", true)),
36+
new UsesType<>("org.camunda.bpm.engine.delegate.ExecutionListener", true));
37+
38+
return Preconditions.check(
39+
precondition,
40+
new JavaIsoVisitor<ExecutionContext>() {
41+
@Override
42+
public J.ClassDeclaration visitClassDeclaration(
43+
J.ClassDeclaration classDecl, ExecutionContext ctx) {
44+
45+
if (classDecl.getKind() != J.ClassDeclaration.Kind.Type.Class) {
46+
return classDecl;
47+
}
48+
49+
List<Statement> statements = classDecl.getBody().getStatements();
50+
51+
for (Statement stmt : statements) {
52+
if (stmt instanceof J.MethodDeclaration m
53+
&& "notify".equals(m.getSimpleName())) {
54+
55+
String workerName =
56+
Character.toLowerCase(classDecl.getSimpleName().charAt(0))
57+
+ classDecl.getSimpleName().substring(1);
58+
59+
maybeAddImport("io.camunda.client.annotation.JobWorker");
60+
maybeAddImport("io.camunda.client.api.response.ActivatedJob");
61+
maybeAddImport("java.util.Map");
62+
maybeAddImport("java.util.HashMap");
63+
64+
return RecipeUtils.createSimpleJavaTemplate(
65+
"""
66+
@JobWorker(type = \"#{}\", autoComplete = true)
67+
public Map<String, Object> executeJob(ActivatedJob job) throws Exception {
68+
Map<String, Object> resultMap = new HashMap<>();
69+
return resultMap;
70+
}
71+
""",
72+
"io.camunda.client.annotation.JobWorker",
73+
"io.camunda.client.api.response.ActivatedJob",
74+
"java.util.Map",
75+
"java.util.HashMap")
76+
.apply(
77+
updateCursor(classDecl),
78+
classDecl.getBody().getCoordinates().lastStatement(),
79+
workerName);
80+
}
81+
}
82+
83+
return super.visitClassDeclaration(classDecl, ctx);
84+
}
85+
});
86+
}
87+
}

code-conversion/recipes/src/main/resources/META-INF/rewrite/delegateRecipes.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ recipeList:
99
version: ${version.camunda-8}
1010
- io.camunda.migration.code.recipes.sharedRecipes.ReplaceTypedValueAPIRecipe
1111
- io.camunda.migration.code.recipes.delegate.PrepareJobWorkerBeneathDelegateRecipe
12+
- io.camunda.migration.code.recipes.delegate.PrepareJobWorkerBeneathExecutionListenerRecipe
1213
---
1314
type: specs.openrewrite.org/v1beta/recipe
1415
name: io.camunda.migration.code.recipes.AllDelegateMigrateRecipes
@@ -23,6 +24,7 @@ displayName: Runs all delegate cleanup recipes
2324
description: Removes delegate code and unused imports.
2425
recipeList:
2526
- io.camunda.migration.code.recipes.delegate.CleanupDelegateRecipe
27+
- io.camunda.migration.code.recipes.delegate.CleanupExecutionListenerRecipe
2628
- org.openrewrite.java.RemoveUnusedImports
2729
---
2830
type: specs.openrewrite.org/v1beta/recipe

0 commit comments

Comments
 (0)