Skip to content

Commit 7e53332

Browse files
JohannisKgithub-actions[bot]Jenson3210timtebeek
authored
Migrate to Process#waitFor(Duration) as part of Java 25 upgrade (#840)
* Added initial recipe * Fix * polish * polish * Update src/main/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDuration.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDurationTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDurationTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDurationTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * polish * polish * Add java 25 * Update src/main/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDuration.java Co-authored-by: Jente Sondervorst <jentesondervorst@gmail.com> * Update src/main/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDuration.java Co-authored-by: Jente Sondervorst <jentesondervorst@gmail.com> * Update src/main/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDuration.java Co-authored-by: Jente Sondervorst <jentesondervorst@gmail.com> * Update src/main/java/org/openrewrite/java/migrate/lang/MigrateProcessWaitForDuration.java Co-authored-by: Jente Sondervorst <jentesondervorst@gmail.com> * Add `rewrite-java-25` runtime dependency * Override toolchain to use Java 25 for tests that need it * Also override `java_version` for receive-pr.yml * Do not add recipe twice, as it will run twice * Apply formatter to tests * Remove imports before adding new ones, return from template.apply * Use markers to set Java versions in tests instead * Add test that no changes are made when Duration already present --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Jente Sondervorst <jentesondervorst@gmail.com> Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent 28c66ec commit 7e53332

File tree

4 files changed

+446
-0
lines changed

4 files changed

+446
-0
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ dependencies {
4646
runtimeOnly("org.openrewrite:rewrite-java-11")
4747
runtimeOnly("org.openrewrite:rewrite-java-17")
4848
runtimeOnly("org.openrewrite:rewrite-java-21")
49+
runtimeOnly("org.openrewrite:rewrite-java-25")
4950

5051
runtimeOnly("tech.picnic.error-prone-support:error-prone-contrib:latest.release:recipes")
5152

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lang;
17+
18+
import org.jspecify.annotations.Nullable;
19+
import org.openrewrite.ExecutionContext;
20+
import org.openrewrite.Preconditions;
21+
import org.openrewrite.Recipe;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.java.JavaTemplate;
24+
import org.openrewrite.java.JavaVisitor;
25+
import org.openrewrite.java.MethodMatcher;
26+
import org.openrewrite.java.search.UsesJavaVersion;
27+
import org.openrewrite.java.tree.Expression;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.staticanalysis.SimplifyDurationCreationUnits;
30+
31+
public class MigrateProcessWaitForDuration extends Recipe {
32+
33+
private static final MethodMatcher PROCESS_WAIT_FOR_MATCHER = new MethodMatcher("java.lang.Process waitFor(long, java.util.concurrent.TimeUnit)");
34+
35+
@Override
36+
public String getDisplayName() {
37+
return "Use `Process#waitFor(Duration)`";
38+
}
39+
40+
@Override
41+
public String getDescription() {
42+
return "Use `Process#waitFor(Duration)` instead of `Process#waitFor(long, TimeUnit)` in Java 25 or higher.";
43+
}
44+
45+
@Override
46+
public TreeVisitor<?, ExecutionContext> getVisitor() {
47+
return Preconditions.check(new UsesJavaVersion<>(25), new JavaVisitor<ExecutionContext>() {
48+
@Override
49+
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
50+
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
51+
52+
if (PROCESS_WAIT_FOR_MATCHER.matches(mi)) {
53+
Expression valueArg = mi.getArguments().get(0);
54+
Expression unitArg = mi.getArguments().get(1);
55+
String timeUnitName = getTimeUnitName(unitArg);
56+
String durationMethod = getDurationMethod(timeUnitName);
57+
58+
boolean isSimpleValue = valueArg instanceof J.Literal || valueArg instanceof J.Identifier;
59+
60+
maybeRemoveImport("java.util.concurrent.TimeUnit");
61+
maybeRemoveImport("java.util.concurrent.TimeUnit." + timeUnitName);
62+
maybeAddImport("java.time.Duration");
63+
maybeAddImport("java.time.temporal.ChronoUnit");
64+
65+
doAfterVisit(new SimplifyDurationCreationUnits().getVisitor());
66+
67+
if (isSimpleValue && "MICROSECONDS".equals(timeUnitName)) {
68+
return JavaTemplate.builder("Duration.of(#{any(long)}, ChronoUnit.MICROS)")
69+
.imports("java.time.Duration", "java.time.temporal.ChronoUnit")
70+
.build()
71+
.apply(getCursor(), mi.getCoordinates().replaceArguments(), valueArg);
72+
}
73+
if (isSimpleValue && durationMethod != null) {
74+
return JavaTemplate.builder("Duration." + durationMethod + "(#{any(long)})")
75+
.imports("java.time.Duration")
76+
.build()
77+
.apply(getCursor(), mi.getCoordinates().replaceArguments(), valueArg);
78+
}
79+
return JavaTemplate.builder("Duration.of(#{any(long)}, #{any(java.util.concurrent.TimeUnit)}.toChronoUnit())")
80+
.imports("java.time.Duration")
81+
.build()
82+
.apply(getCursor(), mi.getCoordinates().replaceArguments(), valueArg, unitArg);
83+
}
84+
return mi;
85+
}
86+
87+
private @Nullable String getTimeUnitName(Expression timeUnitArg) {
88+
if (timeUnitArg instanceof J.FieldAccess) {
89+
J.FieldAccess fa = (J.FieldAccess) timeUnitArg;
90+
return fa.getSimpleName();
91+
}
92+
if (timeUnitArg instanceof J.Identifier) {
93+
J.Identifier id = (J.Identifier) timeUnitArg;
94+
return id.getSimpleName();
95+
}
96+
return null;
97+
}
98+
99+
private @Nullable String getDurationMethod(@Nullable String timeUnitName) {
100+
if (timeUnitName == null) {
101+
return null;
102+
}
103+
switch (timeUnitName) {
104+
case "NANOSECONDS":
105+
return "ofNanos";
106+
case "MILLISECONDS":
107+
return "ofMillis";
108+
case "SECONDS":
109+
return "ofSeconds";
110+
case "MINUTES":
111+
return "ofMinutes";
112+
case "HOURS":
113+
return "ofHours";
114+
case "DAYS":
115+
return "ofDays";
116+
default:
117+
return null;
118+
}
119+
}
120+
});
121+
}
122+
}

src/main/resources/META-INF/rewrite/java-version-25.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ tags:
2727
recipeList:
2828
- org.openrewrite.java.migrate.UpgradeToJava21
2929
- org.openrewrite.java.migrate.UpgradeBuildToJava25
30+
- org.openrewrite.java.migrate.lang.MigrateProcessWaitForDuration
3031
- org.openrewrite.java.migrate.AccessController
3132
- org.openrewrite.java.migrate.RemoveSecurityPolicy
3233
- org.openrewrite.java.migrate.RemoveSecurityManager

0 commit comments

Comments
 (0)