Copilot commented on code in PR #130:
URL: https://github.com/apache/maven-doap-plugin/pull/130#discussion_r2573067900


##########
src/main/java/org/apache/maven/plugin/doap/DoapUtil.java:
##########
@@ -498,32 +504,61 @@ public static void fetchURL(Settings settings, URL url) 
throws IOException {
 
             if (StringUtils.isNotEmpty(activeProxy.getHost())
                     && !ProxyUtils.validateNonProxyHosts(proxyInfo, 
url.getHost())) {
-                
httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), 
activeProxy.getPort());
 
-                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
-                    Credentials credentials =
-                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword());
+                // Set the proxy on the configuration
+                HttpHost proxy = new HttpHost(activeProxy.getHost(), 
activeProxy.getPort());
+                requestConfigBuilder.setProxy(proxy);
 
-                    httpClient.getState().setProxyCredentials(AuthScope.ANY, 
credentials);
+                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
+                    credentialsProvider.setCredentials(
+                            new AuthScope(proxy), // Scope to the specific 
proxy
+                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword()));
                 }
             }
         }
 
-        GetMethod getMethod = new GetMethod(url.toString());
+        // Ideally, the ConnectionManager and Client should be singletons 
reused across the application,
+        // not created per request.
+        CloseableHttpClient httpClient = HttpClients.custom()
+                .setConnectionManager(new PoolingHttpClientConnectionManager())
+                .setDefaultCredentialsProvider(credentialsProvider)
+                .setDefaultRequestConfig(requestConfigBuilder.build())
+                .setUserAgent("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 
5.0)")
+                .build();

Review Comment:
   Creating a new `PoolingHttpClientConnectionManager` and 
`CloseableHttpClient` for each request is inefficient and defeats the purpose 
of connection pooling. As the comment acknowledges, these should be created 
once and reused across all requests. Consider refactoring to use a singleton 
pattern or static instance for better performance and resource management.



##########
src/main/java/org/apache/maven/plugin/doap/DoapUtil.java:
##########
@@ -498,32 +504,61 @@ public static void fetchURL(Settings settings, URL url) 
throws IOException {
 
             if (StringUtils.isNotEmpty(activeProxy.getHost())
                     && !ProxyUtils.validateNonProxyHosts(proxyInfo, 
url.getHost())) {
-                
httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), 
activeProxy.getPort());
 
-                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
-                    Credentials credentials =
-                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword());
+                // Set the proxy on the configuration
+                HttpHost proxy = new HttpHost(activeProxy.getHost(), 
activeProxy.getPort());
+                requestConfigBuilder.setProxy(proxy);
 
-                    httpClient.getState().setProxyCredentials(AuthScope.ANY, 
credentials);
+                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
+                    credentialsProvider.setCredentials(
+                            new AuthScope(proxy), // Scope to the specific 
proxy
+                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword()));
                 }
             }
         }
 
-        GetMethod getMethod = new GetMethod(url.toString());
+        // Ideally, the ConnectionManager and Client should be singletons 
reused across the application,
+        // not created per request.
+        CloseableHttpClient httpClient = HttpClients.custom()
+                .setConnectionManager(new PoolingHttpClientConnectionManager())
+                .setDefaultCredentialsProvider(credentialsProvider)
+                .setDefaultRequestConfig(requestConfigBuilder.build())
+                .setUserAgent("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 
5.0)")
+                .build();

Review Comment:
   The `CloseableHttpClient` is never closed, causing a resource leak. The 
client should be closed in the `finally` block to properly release the 
connection manager and its resources. Consider using a try-with-resources 
statement or explicitly closing the client in the finally block after closing 
the response.



##########
src/main/java/org/apache/maven/plugin/doap/DoapUtil.java:
##########
@@ -498,32 +504,61 @@ public static void fetchURL(Settings settings, URL url) 
throws IOException {
 
             if (StringUtils.isNotEmpty(activeProxy.getHost())
                     && !ProxyUtils.validateNonProxyHosts(proxyInfo, 
url.getHost())) {
-                
httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), 
activeProxy.getPort());
 
-                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
-                    Credentials credentials =
-                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword());
+                // Set the proxy on the configuration
+                HttpHost proxy = new HttpHost(activeProxy.getHost(), 
activeProxy.getPort());
+                requestConfigBuilder.setProxy(proxy);
 
-                    httpClient.getState().setProxyCredentials(AuthScope.ANY, 
credentials);
+                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
+                    credentialsProvider.setCredentials(
+                            new AuthScope(proxy), // Scope to the specific 
proxy
+                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword()));
                 }
             }
         }
 
-        GetMethod getMethod = new GetMethod(url.toString());
+        // Ideally, the ConnectionManager and Client should be singletons 
reused across the application,
+        // not created per request.
+        CloseableHttpClient httpClient = HttpClients.custom()
+                .setConnectionManager(new PoolingHttpClientConnectionManager())
+                .setDefaultCredentialsProvider(credentialsProvider)
+                .setDefaultRequestConfig(requestConfigBuilder.build())
+                .setUserAgent("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 
5.0)")
+                .build();
+
+        HttpGet httpGet = new HttpGet(url.toString());
+        CloseableHttpResponse response = null;
+
         try {
             int status;
             try {
-                status = httpClient.executeMethod(getMethod);
+                response = httpClient.execute(httpGet);
+                status = response.getStatusLine().getStatusCode();
             } catch (SocketTimeoutException e) {
-                // could be a sporadic failure, one more retry before we give 
up
-                status = httpClient.executeMethod(getMethod);
+                // Retry logic: Close previous response if it exists (unlikely 
on timeout) and retry once
+                if (response != null) {
+                    response.close();
+                }
+                response = httpClient.execute(httpGet);

Review Comment:
   On `SocketTimeoutException`, the retry logic attempts to reuse the same 
`HttpGet` object. However, after a failed request attempt, the request object 
may be in an inconsistent state. It's safer to create a new `HttpGet` instance 
for the retry attempt.
   ```suggestion
                   // Create a new HttpGet instance for the retry attempt
                   HttpGet retryHttpGet = new HttpGet(url.toString());
                   response = httpClient.execute(retryHttpGet);
   ```



##########
src/main/java/org/apache/maven/plugin/doap/DoapUtil.java:
##########
@@ -498,32 +504,61 @@ public static void fetchURL(Settings settings, URL url) 
throws IOException {
 
             if (StringUtils.isNotEmpty(activeProxy.getHost())
                     && !ProxyUtils.validateNonProxyHosts(proxyInfo, 
url.getHost())) {
-                
httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), 
activeProxy.getPort());
 
-                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
-                    Credentials credentials =
-                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword());
+                // Set the proxy on the configuration
+                HttpHost proxy = new HttpHost(activeProxy.getHost(), 
activeProxy.getPort());
+                requestConfigBuilder.setProxy(proxy);
 
-                    httpClient.getState().setProxyCredentials(AuthScope.ANY, 
credentials);
+                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
+                    credentialsProvider.setCredentials(
+                            new AuthScope(proxy), // Scope to the specific 
proxy
+                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword()));
                 }
             }
         }
 
-        GetMethod getMethod = new GetMethod(url.toString());
+        // Ideally, the ConnectionManager and Client should be singletons 
reused across the application,
+        // not created per request.
+        CloseableHttpClient httpClient = HttpClients.custom()
+                .setConnectionManager(new PoolingHttpClientConnectionManager())
+                .setDefaultCredentialsProvider(credentialsProvider)
+                .setDefaultRequestConfig(requestConfigBuilder.build())
+                .setUserAgent("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 
5.0)")
+                .build();
+
+        HttpGet httpGet = new HttpGet(url.toString());
+        CloseableHttpResponse response = null;
+
         try {
             int status;
             try {
-                status = httpClient.executeMethod(getMethod);
+                response = httpClient.execute(httpGet);
+                status = response.getStatusLine().getStatusCode();
             } catch (SocketTimeoutException e) {
-                // could be a sporadic failure, one more retry before we give 
up
-                status = httpClient.executeMethod(getMethod);
+                // Retry logic: Close previous response if it exists (unlikely 
on timeout) and retry once
+                if (response != null) {
+                    response.close();
+                }
+                response = httpClient.execute(httpGet);
+                status = response.getStatusLine().getStatusCode();
             }
 
             if (status != HttpStatus.SC_OK) {
+                // Ensure entity is consumed before throwing exception to 
release connection
+                if (response.getEntity() != null) {
+                    EntityUtils.consume(response.getEntity());
+                }
                 throw new FileNotFoundException(url.toString());
             }
         } finally {
-            getMethod.releaseConnection();
+            // In 4.5, closing the response releases the connection back to 
the pool
+            if (response != null) {
+                try {
+                    response.close();
+                } catch (IOException e) {
+                    // Log or ignore

Review Comment:
   Silently ignoring the IOException when closing the response makes debugging 
difficult. Consider logging the exception or at minimum adding a comment 
explaining why it's safe to ignore. If a logger is not available in this 
context, document why ignoring this exception is acceptable.
   ```suggestion
                       // Log the exception to aid debugging
                       System.err.println("Warning: IOException occurred while 
closing HTTP response: " + e.getMessage());
                       e.printStackTrace(System.err);
   ```



##########
src/main/java/org/apache/maven/plugin/doap/DoapUtil.java:
##########
@@ -498,32 +504,61 @@ public static void fetchURL(Settings settings, URL url) 
throws IOException {
 
             if (StringUtils.isNotEmpty(activeProxy.getHost())
                     && !ProxyUtils.validateNonProxyHosts(proxyInfo, 
url.getHost())) {
-                
httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), 
activeProxy.getPort());
 
-                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
-                    Credentials credentials =
-                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword());
+                // Set the proxy on the configuration
+                HttpHost proxy = new HttpHost(activeProxy.getHost(), 
activeProxy.getPort());
+                requestConfigBuilder.setProxy(proxy);
 
-                    httpClient.getState().setProxyCredentials(AuthScope.ANY, 
credentials);
+                if (StringUtils.isNotEmpty(activeProxy.getUsername()) && 
activeProxy.getPassword() != null) {
+                    credentialsProvider.setCredentials(
+                            new AuthScope(proxy), // Scope to the specific 
proxy
+                            new 
UsernamePasswordCredentials(activeProxy.getUsername(), 
activeProxy.getPassword()));
                 }
             }
         }
 
-        GetMethod getMethod = new GetMethod(url.toString());
+        // Ideally, the ConnectionManager and Client should be singletons 
reused across the application,
+        // not created per request.
+        CloseableHttpClient httpClient = HttpClients.custom()
+                .setConnectionManager(new PoolingHttpClientConnectionManager())
+                .setDefaultCredentialsProvider(credentialsProvider)
+                .setDefaultRequestConfig(requestConfigBuilder.build())
+                .setUserAgent("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 
5.0)")
+                .build();
+
+        HttpGet httpGet = new HttpGet(url.toString());
+        CloseableHttpResponse response = null;
+
         try {
             int status;
             try {
-                status = httpClient.executeMethod(getMethod);
+                response = httpClient.execute(httpGet);
+                status = response.getStatusLine().getStatusCode();
             } catch (SocketTimeoutException e) {
-                // could be a sporadic failure, one more retry before we give 
up
-                status = httpClient.executeMethod(getMethod);
+                // Retry logic: Close previous response if it exists (unlikely 
on timeout) and retry once
+                if (response != null) {
+                    response.close();
+                }
+                response = httpClient.execute(httpGet);
+                status = response.getStatusLine().getStatusCode();
             }
 
             if (status != HttpStatus.SC_OK) {
+                // Ensure entity is consumed before throwing exception to 
release connection
+                if (response.getEntity() != null) {
+                    EntityUtils.consume(response.getEntity());
+                }
                 throw new FileNotFoundException(url.toString());

Review Comment:
   When the status is `SC_OK` (success case), the response entity is never 
consumed. While closing the response should release the connection, it's best 
practice to consume the entity explicitly using 
`EntityUtils.consume(response.getEntity())` to ensure proper connection reuse 
and avoid potential connection pool starvation. Add entity consumption for the 
success case, similar to the error case at line 549.
   ```suggestion
                   throw new FileNotFoundException(url.toString());
               } else {
                   // Ensure entity is consumed in the success case to release 
connection for reuse
                   if (response.getEntity() != null) {
                       EntityUtils.consume(response.getEntity());
                   }
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to