Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apache Http Client 5.x Use Timeout and TimeValue classes #399

Merged
merged 13 commits into from
Jul 31, 2023
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ bin/
.project
.classpath
.settings/
.DS_Store
.moderne/
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite.java.apache.httpclient5;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.tree.J;

import java.util.Arrays;
import java.util.List;

public class UseTimeValue extends Recipe {
@Override
public String getDisplayName() {
return "Use `TimeValue` class to define time values (duration)";
}

@Override
public String getDescription() {
return "Use `TimeValue` class to define time values (duration).";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {


List<MethodMatcher> methodMatchers = Arrays.asList(
timtebeek marked this conversation as resolved.
Show resolved Hide resolved
new MethodMatcher("org.apache.hc.core5.http.io.SocketConfig.Builder setSoLinger(int)")
);

JavaTemplate template = JavaTemplate.builder("TimeValue.ofMilliseconds(#{})")
.contextSensitive()
.javaParser(JavaParser.fromJavaVersion().classpath(
"httpclient5", "httpcore5"
))
.imports("org.apache.hc.core5.util.TimeValue")
.build();
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(method, ctx);

if (methodMatches(m)) {
m = template.apply(updateCursor(m), m.getCoordinates().replaceArguments(), m.getArguments().get(0));
maybeAddImport("org.apache.hc.core5.util.TimeValue");
}
return m;
}

private boolean methodMatches(J.MethodInvocation method) {
return methodMatchers.stream()
.anyMatch(matcher -> matcher.matches(method));
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite.java.apache.httpclient5;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.tree.J;

import java.util.Arrays;
import java.util.List;


public class UseTimeout extends Recipe {
@Override
public String getDisplayName() {
return "Use `Timeout` class to define timeouts";
}

@Override
public String getDescription() {
return "Use Timeout class to define timeouts.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
joanvr marked this conversation as resolved.
Show resolved Hide resolved


final List<MethodMatcher> methodMatchers = Arrays.asList(
new MethodMatcher("org.apache.hc.client5.http.config.RequestConfig.Builder setConnectionRequestTimeout(int)"),
new MethodMatcher("org.apache.hc.client5.http.config.RequestConfig.Builder setConnectTimeout(int)"),
new MethodMatcher("org.apache.hc.client5.http.config.RequestConfig.Builder setResponseTimeout(int)"),
new MethodMatcher("org.apache.hc.core5.http.io.SocketConfig.Builder setSoTimeout(int)")
Comment on lines +49 to +52
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity: Do these methods actually exist? Or only when we're halfway through a migration with change package having partially updated the classes such that we use the new package here, but the methods taking an int as an argument do not actually exist?

I'm wondering if the 4.x deprecated methods already had replacements taking a Timeout, which we could fully migrate in the 4.x line instead, before we even start a migration to 5.x. That could make the recipes more broadly applicable, and applied in isolation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, in 4.x all timeouts and tiemvalues are expressed with integers in milliseconds, and on 5.x there are two options, using Timeout and TimeValue classes, or long + TimeUnit... So, those methods we are matching here does not actually exists in the classes in the namespace, but are "half-way" partially updated mapped classes to 5.x

);

final JavaTemplate template = JavaTemplate.builder("Timeout.ofMilliseconds(#{})")
.contextSensitive()
.javaParser(JavaParser.fromJavaVersion().classpath(
"httpclient5", "httpcore5"
))
.imports("org.apache.hc.core5.util.Timeout")
.build();
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(method, ctx);

if (methodMatches(m)) {
m = template.apply(updateCursor(m), m.getCoordinates().replaceArguments(), m.getArguments().get(0));
maybeAddImport("org.apache.hc.core5.util.Timeout");
}
return m;
}

private boolean methodMatches(J.MethodInvocation method) {
return methodMatchers.stream()
.anyMatch(matcher -> matcher.matches(method));
joanvr marked this conversation as resolved.
Show resolved Hide resolved
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*/
@NonNullApi @NonNullFields
package org.openrewrite.java.apache.httpclient5;

import org.openrewrite.internal.lang.NonNullApi;
import org.openrewrite.internal.lang.NonNullFields;
18 changes: 17 additions & 1 deletion src/main/resources/META-INF/rewrite/apache-httpclient-5.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ recipeList:
newVersion: 5.1.x
overrideManagedVersion: true
- org.openrewrite.java.apache.httpclient5.UpgradeApacheHttpClient_5_ClassMapping
- org.openrewrite.java.apache.httpclient5.UpgradeApacheHttpClient_5_DeprecatedMethods
- org.openrewrite.java.apache.httpclient5.UseTimeout
- org.openrewrite.java.apache.httpclient5.UseTimeValue

---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.apache.httpclient5.UpgradeApacheHttpClient_5_ClassMapping
Expand Down Expand Up @@ -251,8 +255,11 @@ recipeList:
oldPackageName: org.apache.http.config
newPackageName: org.apache.hc.core5.http.config
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: org.apache.http.config.SocketConfig
oldFullyQualifiedTypeName: org.apache.hc.core5.http.config.SocketConfig
newFullyQualifiedTypeName: org.apache.hc.core5.http.io.SocketConfig
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: org.apache.hc.core5.http.config.SocketConfig.Builder
newFullyQualifiedTypeName: org.apache.hc.core5.http.io.SocketConfig.Builder

- org.openrewrite.java.ChangePackage:
oldPackageName: org.apache.http.impl
Expand Down Expand Up @@ -396,3 +403,12 @@ recipeList:
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: org.apache.hc.core5.http.HttpConnectionFactory
newFullyQualifiedTypeName: org.apache.hc.core5.http.io.HttpConnectionFactory
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.apache.httpclient5.UpgradeApacheHttpClient_5_DeprecatedMethods
displayName: Migrate to ApacheHttpClient 5.x deprecated methods from 4.x
description: Migrates deprecated methods to their equivalent ones in 5.x
recipeList:
- org.openrewrite.java.ChangeMethodName:
methodPattern: org.apache.hc.client5.http.config.RequestConfig.Builder setSocketTimeout(int)
newMethodName: setResponseTimeout
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,70 @@ void method(HttpEntity entity, String urlStr) throws Exception {
""")
);
}

@Test
void useTimeoutClass() {
rewriteRun(
//language=java
java("""
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.SocketConfig;

class A {
void method() {
RequestConfig.custom()
.setConnectionRequestTimeout(300)
.setConnectTimeout(500)
.setSocketTimeout(1500);

SocketConfig.custom()
.setSoTimeout(1000);
}
}
""", """
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.util.Timeout;

class A {
void method() {
RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.ofMilliseconds(300))
.setConnectTimeout(Timeout.ofMilliseconds(500))
.setResponseTimeout(Timeout.ofMilliseconds(1500));

SocketConfig.custom()
.setSoTimeout(Timeout.ofMilliseconds(1000));
}
}
""")
);
}

@Test
void useTimeValueClass() {
rewriteRun(
//language=java
java("""
import org.apache.http.config.SocketConfig;

class A {
void method() {
SocketConfig.custom()
.setSoLinger(500);
}
}
""", """
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.util.TimeValue;

class A {
void method() {
SocketConfig.custom()
.setSoLinger(TimeValue.ofMilliseconds(500));
}
}
""")
);
}
}