fix(code-conversion): AbstractMigrationRecipe NPE on Null Return Type#1032
fix(code-conversion): AbstractMigrationRecipe NPE on Null Return Type#1032
Conversation
Fix NPE in AbstractMigrationRecipe When Return Type Cannot Be ResolvedProblemThe Root CauseThe
The lookup returns Solution
Test Cases1. Task Query with Method AccessTask task = taskService.createTaskQuery().taskId(taskId).singleResult();
return task.getName(); // <-- NPE when visiting task.getName()Why it fails:
After fix: Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
return //TODO: Manual migration required - could not resolve return type for: task
task.getName();2. Task Query Result Used in Another Queryfinal Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
List<ProcessInstance> processInstances = runtimeService
.createProcessInstanceQuery()
.processInstanceId(task.getProcessInstanceId()) // <-- NPE here
.list();Why it fails:
After fix: final Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
List<ProcessInstance> processInstances = runtimeService
.createProcessInstanceQuery()
.processInstanceId(//TODO: Manual migration required - could not resolve return type for: task
task.getProcessInstanceId())
.list();3. TaskService from ProcessEngine ParameterTaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
taskService.complete(task.getId()); // <-- NPE when visiting task.getId()Why it fails:
After fix: TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
camundaClient
.newCompleteUserTaskCommand(Long.valueOf(//TODO: Manual migration required - could not resolve return type for: task
task.getId()))
.send()
.join();Follow-Up: Handling More Edge CasesThis fix prevents crashes but leaves code for manual migration. The following improvements could reduce the number of skipped transformations. Add
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a NullPointerException in the AbstractMigrationRecipe class that occurred when the return type of a variable could not be resolved during code migration. The fix adds null checks and gracefully handles unresolvable types by inserting TODO comments to flag code requiring manual migration. Additionally, it corrects the Camunda 8 API method name from newUserTaskCompleteCommand to newCompleteUserTaskCommand.
Changes:
- Added null-safe handling in AbstractMigrationRecipe when
returnTypeFqnis null, preventing NPE and adding TODO comments for manual migration - Created MigrationMessages utility class to centralize migration-related comment templates
- Fixed API method name to use correct
newCompleteUserTaskCommandinstead ofnewUserTaskCompleteCommand - Added comprehensive regression tests to verify NPE fix works correctly in multiple scenarios
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| AbstractMigrationRecipeNullReturnTypeTest.java | New regression test file with three test cases verifying that the recipe handles null return types gracefully without crashing |
| MigrationMessages.java | New utility class providing centralized message templates for migration comments, following the EntityConversionServiceLogs pattern |
| AbstractMigrationRecipe.java | Added null checks for getTypeAsFullyQualified() and returnTypeFqn, and logic to add TODO comments when return types cannot be resolved |
| MigrateUserTaskMethodsRecipe.java | Corrected Camunda 8 API method name from newUserTaskCompleteCommand to newCompleteUserTaskCommand |
| JavaType.FullyQualified originalType = declarations.getTypeAsFullyQualified(); | ||
| if (originalType != null) { | ||
| maybeRemoveImport( | ||
| RecipeUtils.getGenericLongName(originalType.toString())); | ||
| } |
There was a problem hiding this comment.
Good defensive null check added to prevent NPE when getTypeAsFullyQualified returns null. This pattern should be consistently applied to all similar calls to getTypeAsFullyQualified in the codebase. Note that ReplaceTypedValueAPIRecipe.java has two similar calls on lines 294 and 461 that may benefit from the same null check pattern for consistency and robustness.
| @@ -82,7 +82,7 @@ protected List<ReplacementUtils.SimpleReplacementSpec> simpleMethodInvocations() | |||
| RecipeUtils.createSimpleJavaTemplate( | |||
| """ | |||
| #{camundaClient:any(io.camunda.client.CamundaClient)} | |||
| .newUserTaskCompleteCommand(Long.valueOf(#{taskId:any(java.lang.String)})) | |||
| .newCompleteUserTaskCommand(Long.valueOf(#{taskId:any(java.lang.String)})) | |||
There was a problem hiding this comment.
This file contains a fix to the Camunda 8 API method name (changing from newUserTaskCompleteCommand to newCompleteUserTaskCommand), which is not mentioned in the PR title or description. While this is a valid and necessary correction, it should be documented in the PR description to provide complete context for reviewers. The PR description currently only mentions the NPE fix.
Pull Request Template
related to: #537
Description
Type of Change
Testing Checklist
Black-Box Testing Requirements
DbClient,IdKeyMapper,..impl..packages except logging constants)ArchitectureTestvalidates these rules)Test Coverage
Architecture Compliance
Run architecture tests to ensure compliance:
mvn test -Dtest=ArchitectureTestIf architecture tests fail, refactor your tests to use:
LogCapturerfor log assertionscamundaClient.new*SearchRequest()for C8 queriesDocumentation
Checklist
Related Issues