This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new c728f17533 Upgrade httpclient to http.client5 and some random IT fixes
(#6902)
c728f17533 is described below
commit c728f175332a319d47e4676c6e94ca5ddb0ef54d
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Mon Mar 30 20:39:46 2026 +0200
Upgrade httpclient to http.client5 and some random IT fixes (#6902)
---
core/pom.xml | 10 --
.../apache/hop/core/util/HttpClientManager.java | 67 ++++++-----
.../org/apache/hop/core/util/HttpClientUtil.java | 60 +++++-----
.../hop/metadata/inject/HopMetadataInjector.java | 45 +++++++
.../apache/hop/server/ServerConnectionManager.java | 10 +-
.../metadata/inject/HopMetadataInjectorTest.java | 7 ++
.../java/org/apache/hop/core/HttpProtocol.java | 27 +++--
.../remote/RemoteExecutionInfoLocation.java | 2 +-
.../java/org/apache/hop/server/HopServerMeta.java | 131 ++++++++++-----------
.../java/org/apache/hop/www/BaseHttpServlet.java | 2 +-
.../java/org/apache/hop/server/HopServerTest.java | 82 ++++++-------
lib/pom.xml | 6 -
plugins/misc/rest/pom.xml | 10 +-
plugins/misc/rest/src/assembly/assembly.xml | 2 +-
.../dorisbulkloader/DorisStreamLoad.java | 44 +++----
.../execsqlrow/messages/messages_en_US.properties | 4 +-
.../transforms/execsqlrow/ExecSqlRowMetaTest.java | 19 +++
.../apache/hop/pipeline/transforms/http/Http.java | 91 +++++++-------
.../hop/pipeline/transforms/http/HttpData.java | 2 +-
.../hop/pipeline/transforms/http/HttpDataTest.java | 4 +-
.../transforms/http/HttpExecutionTest.java | 5 +-
.../hop/pipeline/transforms/http/HttpTest.java | 8 +-
plugins/transforms/httppost/pom.xml | 12 +-
.../hop/pipeline/transforms/httppost/HttpPost.java | 115 +++++++++---------
.../pipeline/transforms/httppost/HttpPostData.java | 2 +-
.../transforms/httppost/HttpPostExecutionTest.java | 7 +-
.../pipeline/transforms/httppost/HttpPostTest.java | 47 ++++----
.../pipeline/transforms/metainject/MetaInject.java | 32 +++--
.../transforms/metainject/MetaInjectDialog.java | 28 ++++-
plugins/transforms/rest/pom.xml | 4 +-
plugins/transforms/rest/src/assembly/assembly.xml | 2 +-
.../apache/hop/pipeline/transforms/rest/Rest.java | 4 +-
.../hop/pipeline/transforms/sort/SortRows.java | 24 +++-
.../hop/pipeline/transforms/sort/SortRowsTest.java | 112 ++++++++++++++++++
.../transforms/webservices/WebService.java | 85 +++++++------
.../pipeline/transforms/webservices/wsdl/Wsdl.java | 52 ++++----
.../transforms/webservices/WebServiceTest.java | 6 +-
.../transforms/xml/getxmldata/GetXmlData.java | 12 +-
.../hop/ui/www/service/WebServiceEditor.java | 2 +-
39 files changed, 701 insertions(+), 483 deletions(-)
diff --git a/core/pom.xml b/core/pom.xml
index 9dee1d413b..0877df7c52 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -126,16 +126,6 @@
</exclusion>
</exclusions>
</dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <exclusions>
- <exclusion>
- <groupId>*</groupId>
- <artifactId>*</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
diff --git a/core/src/main/java/org/apache/hop/core/util/HttpClientManager.java
b/core/src/main/java/org/apache/hop/core/util/HttpClientManager.java
index f6868202bf..e5fa258b0b 100644
--- a/core/src/main/java/org/apache/hop/core/util/HttpClientManager.java
+++ b/core/src/main/java/org/apache/hop/core/util/HttpClientManager.java
@@ -30,33 +30,34 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.protocol.RedirectStrategy;
+import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
+import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.ssl.SSLContexts;
+import org.apache.hc.core5.ssl.TrustStrategy;
+import org.apache.hc.core5.util.Timeout;
import org.apache.hop.core.logging.ILogChannel;
-import org.apache.http.HttpHost;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.RedirectStrategy;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.config.Registry;
-import org.apache.http.config.RegistryBuilder;
-import org.apache.http.conn.socket.ConnectionSocketFactory;
-import org.apache.http.conn.socket.PlainConnectionSocketFactory;
-import org.apache.http.conn.ssl.NoopHostnameVerifier;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
-import org.apache.http.ssl.SSLContexts;
-import org.apache.http.ssl.TrustStrategy;
/**
- * Single entry point for all {@link org.apache.http.client.HttpClient
HttpClient instances} usages
- * in Hop projects. Contains {@link
org.apache.http.impl.conn.PoolingHttpClientConnectionManager
- * Connection pool} of 200 connections. Maximum connections per one route is
100. Provides inner
- * builder class for creating {@link org.apache.http.client.HttpClient
HttpClients}.
+ * Single entry point for all {@link
org.apache.hc.client5.http.classic.HttpClient HttpClient
+ * instances} usages in Hop projects. Contains {@link
+ * org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager
Connection pool} of 200
+ * connections. Maximum connections per one route is 100. Provides inner
builder class for creating
+ * {@link org.apache.hc.client5.http.classic.HttpClient HttpClients}.
*/
public class HttpClientManager {
private static final int CONNECTIONS_PER_ROUTE = 100;
@@ -88,7 +89,7 @@ public class HttpClientManager {
public class HttpClientBuilderFacade {
private RedirectStrategy redirectStrategy;
- private CredentialsProvider provider;
+ private BasicCredentialsProvider provider;
private int connectionTimeout;
private int socketTimeout;
private HttpHost proxy;
@@ -106,15 +107,17 @@ public class HttpClientManager {
public HttpClientBuilderFacade setCredentials(
String user, String password, AuthScope authScope) {
- CredentialsProvider provider = new BasicCredentialsProvider();
- UsernamePasswordCredentials credentials = new
UsernamePasswordCredentials(user, password);
+ BasicCredentialsProvider provider = new BasicCredentialsProvider();
+ char[] passwordChars = password != null ? password.toCharArray() : new
char[0];
+ UsernamePasswordCredentials credentials =
+ new UsernamePasswordCredentials(user, passwordChars);
provider.setCredentials(authScope, credentials);
this.provider = provider;
return this;
}
public HttpClientBuilderFacade setCredentials(String user, String
password) {
- return setCredentials(user, password, AuthScope.ANY);
+ return setCredentials(user, password, new AuthScope(null, null, -1,
null, null));
}
public HttpClientBuilderFacade setProxy(String proxyHost, int proxyPort) {
@@ -123,7 +126,7 @@ public class HttpClientManager {
}
public HttpClientBuilderFacade setProxy(String proxyHost, int proxyPort,
String scheme) {
- this.proxy = new HttpHost(proxyHost, proxyPort, scheme);
+ this.proxy = new HttpHost(scheme, proxyHost, proxyPort);
return this;
}
@@ -157,7 +160,7 @@ public class HttpClientManager {
BasicHttpClientConnectionManager connectionManager =
new BasicHttpClientConnectionManager(socketFactoryRegistry);
-
httpClientBuilder.setSSLSocketFactory(sslsf).setConnectionManager(connectionManager);
+ httpClientBuilder.setConnectionManager(connectionManager);
}
public CloseableHttpClient build() {
@@ -166,10 +169,10 @@ public class HttpClientManager {
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
if (socketTimeout > 0) {
- requestConfigBuilder.setSocketTimeout(socketTimeout);
+
requestConfigBuilder.setResponseTimeout(Timeout.ofMilliseconds(socketTimeout));
}
if (connectionTimeout > 0) {
- requestConfigBuilder.setConnectTimeout(socketTimeout);
+
requestConfigBuilder.setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout));
}
if (proxy != null) {
requestConfigBuilder.setProxy(proxy);
diff --git a/core/src/main/java/org/apache/hop/core/util/HttpClientUtil.java
b/core/src/main/java/org/apache/hop/core/util/HttpClientUtil.java
index 2da12fc20f..15e186b55b 100644
--- a/core/src/main/java/org/apache/hop/core/util/HttpClientUtil.java
+++ b/core/src/main/java/org/apache/hop/core/util/HttpClientUtil.java
@@ -22,22 +22,20 @@ import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.AuthCache;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.impl.auth.BasicScheme;
-import org.apache.http.impl.client.BasicAuthCache;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.util.EntityUtils;
+import org.apache.hc.client5.http.auth.AuthCache;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
/**
* Utility class contained useful methods while working with {@link
- * org.apache.http.client.HttpClient HttpClient}
+ * org.apache.hc.client5.http.classic.HttpClient HttpClient}
*/
public class HttpClientUtil {
@@ -48,7 +46,7 @@ public class HttpClientUtil {
* @return HttpEntity in String representation using "UTF-8" encoding
* @throws IOException
*/
- public static String responseToString(HttpResponse response) throws
IOException {
+ public static String responseToString(ClassicHttpResponse response) throws
IOException {
return responseToString(response,
Charset.forName(StandardCharsets.UTF_8.name()));
}
@@ -58,7 +56,8 @@ public class HttpClientUtil {
* @return HttpEntity in decoded String representation using provided charset
* @throws IOException
*/
- public static String responseToString(HttpResponse response, Charset
charset) throws IOException {
+ public static String responseToString(ClassicHttpResponse response, Charset
charset)
+ throws IOException {
return responseToString(response, charset, false);
}
@@ -69,10 +68,15 @@ public class HttpClientUtil {
* @return HttpEntity in String representation using provided charset
* @throws IOException
*/
- public static String responseToString(HttpResponse response, Charset
charset, boolean decode)
- throws IOException {
+ public static String responseToString(
+ ClassicHttpResponse response, Charset charset, boolean decode) throws
IOException {
HttpEntity entity = response.getEntity();
- String result = EntityUtils.toString(entity, charset);
+ String result;
+ try {
+ result = EntityUtils.toString(entity, charset);
+ } catch (ParseException e) {
+ throw new IOException("Unable to parse HTTP response entity", e);
+ }
EntityUtils.consume(entity);
if (decode) {
result = URLDecoder.decode(result, StandardCharsets.UTF_8.name());
@@ -80,11 +84,11 @@ public class HttpClientUtil {
return result;
}
- public static InputStream responseToInputStream(HttpResponse response)
throws IOException {
+ public static InputStream responseToInputStream(ClassicHttpResponse
response) throws IOException {
return response.getEntity().getContent();
}
- public static byte[] responseToByteArray(HttpResponse response) throws
IOException {
+ public static byte[] responseToByteArray(ClassicHttpResponse response)
throws IOException {
return EntityUtils.toByteArray(response.getEntity());
}
@@ -96,23 +100,17 @@ public class HttpClientUtil {
* @param user
* @param password
* @param schema
- * @return {@link org.apache.http.client.protocol.HttpClientContext
HttpClientContext}
+ * @return {@link org.apache.hc.client5.http.protocol.HttpClientContext
HttpClientContext}
*/
public static HttpClientContext createPreemptiveBasicAuthentication(
String host, int port, String user, String password, String schema) {
HttpClientContext localContext = null;
try {
- HttpHost target = new HttpHost(host, port, schema);
- CredentialsProvider credsProvider = new BasicCredentialsProvider();
- credsProvider.setCredentials(
- new AuthScope(target.getHostName(), target.getPort()),
- new UsernamePasswordCredentials(user, password));
-
- // Create AuthCache instance
+ HttpHost target = new HttpHost(schema, host, port);
+ char[] passwordChars = password != null ? password.toCharArray() : new
char[0];
AuthCache authCache = new BasicAuthCache();
- // Generate BASIC scheme object and add it to the local
- // auth cache
BasicScheme basicAuth = new BasicScheme();
+ basicAuth.initPreemptive(new UsernamePasswordCredentials(user,
passwordChars));
authCache.put(target, basicAuth);
// Add AuthCache to the execution context
@@ -131,7 +129,7 @@ public class HttpClientUtil {
* @param port
* @param user
* @param password
- * @return {@link org.apache.http.client.protocol.HttpClientContext
HttpClientContext}
+ * @return {@link org.apache.hc.client5.http.protocol.HttpClientContext
HttpClientContext}
*/
public static HttpClientContext createPreemptiveBasicAuthentication(
String host, int port, String user, String password) {
diff --git
a/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java
b/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java
index c60b422820..093fc42372 100644
--- a/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java
+++ b/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java
@@ -66,6 +66,51 @@ public class HopMetadataInjector {
return map;
}
+ /**
+ * Returns true if {@code targetKey} maps to a {@link HopMetadataProperty}
that is injected via
+ * the simple key map (not via a list / {@link
HopMetadataProperty#injectionGroupKey()}). Such
+ * keys are not present in {@link #findInjectionGroupKeys(Class)}.
+ *
+ * @param objectClass class annotated with HopMetadataProperty (e.g. a
transform meta class)
+ * @param targetKey injection key to test (e.g. from MetaInject mapping)
+ */
+ public static boolean isTopLevelInjectionKey(Class<?> objectClass, String
targetKey)
+ throws HopException {
+ if (objectClass == null || StringUtils.isEmpty(targetKey)) {
+ return false;
+ }
+ List<Field> fields =
+ ReflectionUtil.findAllFields(objectClass, new
MetadataPropertyKeyFunction());
+ for (Field field : fields) {
+ HopMetadataProperty property =
field.getAnnotation(HopMetadataProperty.class);
+ if (property == null || property.isExcludedFromInjection()) {
+ continue;
+ }
+ Class<?> fieldClass = field.getType();
+ String injectionGroup = Const.NVL(property.injectionGroupKey(),
property.groupKey());
+ if (StringUtils.isNotEmpty(injectionGroup)) {
+ continue;
+ }
+ if (fieldClass.equals(List.class)) {
+ continue;
+ }
+ String injectionKey =
+ Const.coalesce(property.injectionKey(), property.key(),
field.getName());
+ if (targetKey.equals(injectionKey)) {
+ return true;
+ }
+ if (!fieldClass.isEnum()
+ && !fieldClass.isPrimitive()
+ && !fieldClass.equals(String.class)
+ && !fieldClass.isArray()) {
+ if (isTopLevelInjectionKey(fieldClass, targetKey)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* For the given object we retrieve the mappings between injection keys and
the group to which
* they belong.
diff --git
a/core/src/main/java/org/apache/hop/server/ServerConnectionManager.java
b/core/src/main/java/org/apache/hop/server/ServerConnectionManager.java
index 40ebdec1d0..ea71312508 100644
--- a/core/src/main/java/org/apache/hop/server/ServerConnectionManager.java
+++ b/core/src/main/java/org/apache/hop/server/ServerConnectionManager.java
@@ -23,9 +23,9 @@ import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
-import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
/**
* Encapsulates the Apache commons HTTP connection manager with a singleton.
We can use this to
@@ -69,12 +69,12 @@ public class ServerConnectionManager {
return serverConnectionManager;
}
- public HttpClient createHttpClient() {
+ public CloseableHttpClient createHttpClient() {
return HttpClients.custom().setConnectionManager(manager).build();
}
public void shutdown() {
- manager.shutdown();
+ manager.close();
}
private static X509TrustManager getDefaultTrustManager() {
diff --git
a/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
b/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
index b2edc2b768..8d95945e55 100644
---
a/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
+++
b/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
@@ -19,6 +19,7 @@
package org.apache.hop.metadata.inject;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -109,6 +110,12 @@ class HopMetadataInjectorTest {
assertTrue(keys.contains("LAST_NAME"));
}
+ @Test
+ void testTopLevelInjectionKey() throws Exception {
+ assertTrue(HopMetadataInjector.isTopLevelInjectionKey(Company.class,
"NAME"));
+ assertFalse(HopMetadataInjector.isTopLevelInjectionKey(Company.class,
"FIRST_NAME"));
+ }
+
@Test
void testSampleMetaInjection() throws Exception {
Map<String, Object> injectionKeyMap = new HashMap<>();
diff --git a/engine/src/main/java/org/apache/hop/core/HttpProtocol.java
b/engine/src/main/java/org/apache/hop/core/HttpProtocol.java
index 65902c41fb..62b17e7a29 100644
--- a/engine/src/main/java/org/apache/hop/core/HttpProtocol.java
+++ b/engine/src/main/java/org/apache/hop/core/HttpProtocol.java
@@ -19,13 +19,17 @@ package org.apache.hop.core;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.net.URI;
+import org.apache.hc.client5.http.auth.AuthenticationException;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpStatus;
import org.apache.hop.core.util.HttpClientManager;
+import org.apache.hop.core.util.HttpClientUtil;
import org.apache.hop.core.util.Utils;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.auth.AuthenticationException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
/**
* HTTP
@@ -70,16 +74,25 @@ public class HttpProtocol {
HttpClient httpClient;
HttpGet getMethod = new HttpGet(urlAsString);
+ HttpClientContext clientContext = HttpClientContext.create();
if (!Utils.isEmpty(username)) {
HttpClientManager.HttpClientBuilderFacade clientBuilder =
HttpClientManager.getInstance().createBuilder();
clientBuilder.setCredentials(username, password);
httpClient = clientBuilder.build();
+ HttpHost origin = HttpHost.create(URI.create(urlAsString));
+ HttpClientContext preemptive =
+ HttpClientUtil.createPreemptiveBasicAuthentication(
+ origin.getHostName(), origin.getPort(), username, password,
origin.getSchemeName());
+ if (preemptive != null) {
+ clientContext = preemptive;
+ }
} else {
httpClient = HttpClientManager.getInstance().createDefaultClient();
}
- HttpResponse httpResponse = httpClient.execute(getMethod);
- int statusCode = httpResponse.getStatusLine().getStatusCode();
+ ClassicHttpResponse httpResponse =
+ (ClassicHttpResponse) httpClient.execute(getMethod, clientContext);
+ int statusCode = httpResponse.getCode();
StringBuilder bodyBuffer = new StringBuilder();
if (statusCode != -1) {
diff --git
a/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
b/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
index 2126abdaff..a581c9c02c 100644
---
a/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
+++
b/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
@@ -26,6 +26,7 @@ import java.util.List;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.core5.net.URIBuilder;
import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.gui.plugin.GuiElementType;
@@ -49,7 +50,6 @@ import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.server.HopServerMeta;
import org.apache.hop.www.GetExecutionInfoServlet;
import org.apache.hop.www.RegisterExecutionInfoServlet;
-import org.apache.http.client.utils.URIBuilder;
@GuiPlugin(description = "File execution information location GUI elements")
@ExecutionInfoLocationPlugin(
diff --git a/engine/src/main/java/org/apache/hop/server/HopServerMeta.java
b/engine/src/main/java/org/apache/hop/server/HopServerMeta.java
index 7babc35b5a..f7153d545c 100644
--- a/engine/src/main/java/org/apache/hop/server/HopServerMeta.java
+++ b/engine/src/main/java/org/apache/hop/server/HopServerMeta.java
@@ -36,6 +36,30 @@ import javax.net.ssl.SSLContext;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.client5.http.auth.AuthCache;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.entity.InputStreamEntity;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.ssl.SSLContexts;
+import org.apache.hc.core5.ssl.TrustStrategy;
import org.apache.hop.core.Const;
import org.apache.hop.core.Result;
import org.apache.hop.core.encryption.Encr;
@@ -71,34 +95,6 @@ import org.apache.hop.www.StartWorkflowServlet;
import org.apache.hop.www.StopPipelineServlet;
import org.apache.hop.www.StopWorkflowServlet;
import org.apache.hop.www.WebResult;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.StatusLine;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.AuthCache;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.conn.ssl.NoopHostnameVerifier;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
-import org.apache.http.conn.ssl.TrustStrategy;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.entity.InputStreamEntity;
-import org.apache.http.impl.auth.BasicScheme;
-import org.apache.http.impl.client.BasicAuthCache;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.ssl.SSLContexts;
import org.w3c.dom.Node;
@Getter
@@ -387,13 +383,14 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
return result;
}
- HttpPost buildSendXmlMethod(IVariables variables, byte[] content, String
service) {
+ org.apache.hc.client5.http.classic.methods.HttpPost buildSendXmlMethod(
+ IVariables variables, byte[] content, String service) {
String encoding = Const.XML_ENCODING;
return buildSendMethod(variables, content, encoding, service, "text/xml");
}
// Method is defined as package-protected in order to be accessible by unit
tests
- HttpPost buildSendMethod(
+ org.apache.hc.client5.http.classic.methods.HttpPost buildSendMethod(
IVariables variables, byte[] content, String encoding, String service,
String contentType) {
// Prepare HTTP put
//
@@ -401,11 +398,12 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
if (log.isDebug()) {
log.logDebug(BaseMessages.getString(PKG, "HopServer.DEBUG_ConnectingTo",
urlString));
}
- HttpPost postMethod = new HttpPost(urlString);
+ org.apache.hc.client5.http.classic.methods.HttpPost postMethod =
+ new org.apache.hc.client5.http.classic.methods.HttpPost(urlString);
// Request content will be retrieved directly from the input stream
//
- HttpEntity entity = new ByteArrayEntity(content);
+ HttpEntity entity = new ByteArrayEntity(content,
ContentType.APPLICATION_OCTET_STREAM);
postMethod.setEntity(entity);
postMethod.addHeader(new BasicHeader("Accept", contentType + ";charset=" +
encoding));
@@ -415,13 +413,11 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
public String sendXml(IVariables variables, String xml, String service)
throws Exception {
String encoding = getXmlEncoding(xml);
- HttpPost method =
+ org.apache.hc.client5.http.classic.methods.HttpPost method =
buildSendMethod(variables, xml.getBytes(encoding), encoding, service,
"text/xml");
try {
return executeAuth(variables, method);
} finally {
- // Release current connection to the connection pool once you are done
- method.releaseConnection();
if (log.isDetailed()) {
log.logDetailed(
BaseMessages.getString(
@@ -432,13 +428,11 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
public String sendJson(IVariables variables, String json, String service)
throws Exception {
String encoding = Const.XML_ENCODING;
- HttpPost method =
+ org.apache.hc.client5.http.classic.methods.HttpPost method =
buildSendMethod(variables, json.getBytes(encoding), encoding, service,
"application/json");
try {
return executeAuth(variables, method);
} finally {
- // Release current connection to the connection pool once you are done
- method.releaseConnection();
if (log.isDetailed()) {
log.logDetailed(
BaseMessages.getString(
@@ -459,7 +453,7 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
/** Throws if not ok */
private void handleStatus(
- IVariables variables, HttpUriRequest method, StatusLine statusLine, int
status)
+ IVariables variables, ClassicHttpRequest method, ClassicHttpResponse
response, int status)
throws HopException {
if (status >= 300) {
String message;
@@ -475,14 +469,15 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
message =
String.format(
"HTTP Status %d - %s - %s",
- status, method.getURI().toString(),
statusLine.getReasonPhrase());
+ status, method.getRequestUri(), response.getReasonPhrase());
}
throw new HopException(message);
}
}
// Method is defined as package-protected in order to be accessible by unit
tests
- HttpPost buildSendExportMethod(IVariables variables, String type, String
load, InputStream is) {
+ org.apache.hc.client5.http.classic.methods.HttpPost buildSendExportMethod(
+ IVariables variables, String type, String load, InputStream is) {
String serviceUrl = RegisterPackageServlet.CONTEXT_PATH;
if (type != null && load != null) {
serviceUrl +=
@@ -501,8 +496,9 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
log.logDebug(BaseMessages.getString(PKG, "HopServer.DEBUG_ConnectingTo",
urlString));
}
- HttpPost method = new HttpPost(urlString);
- method.setEntity(new InputStreamEntity(is));
+ org.apache.hc.client5.http.classic.methods.HttpPost method =
+ new org.apache.hc.client5.http.classic.methods.HttpPost(urlString);
+ method.setEntity(new InputStreamEntity(is,
ContentType.APPLICATION_OCTET_STREAM));
method.addHeader(new BasicHeader("Content-Type", "binary/zip"));
return method;
@@ -522,12 +518,11 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
// Request content will be retrieved directly from the input stream
try (InputStream is =
HopVfs.getInputStream(HopVfs.getFileObject(filename))) {
// Execute request
- HttpPost method = buildSendExportMethod(variables, type, load, is);
+ org.apache.hc.client5.http.classic.methods.HttpPost method =
+ buildSendExportMethod(variables, type, load, is);
try {
return executeAuth(variables, method);
} finally {
- // Release current connection to the connection pool once you are done
- method.releaseConnection();
if (log.isDetailed()) {
log.logDetailed(
BaseMessages.getString(
@@ -549,16 +544,17 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
* @throws ClientProtocolException
* @throws HopException if response not ok
*/
- private String executeAuth(IVariables variables, HttpUriRequest method)
+ private String executeAuth(IVariables variables, ClassicHttpRequest method)
throws IOException, HopException {
- HttpResponse httpResponse = getHttpClient().execute(method,
getAuthContext(variables));
+ ClassicHttpResponse httpResponse =
+ (ClassicHttpResponse) getHttpClient().execute(method,
getAuthContext(variables));
return getResponse(variables, method, httpResponse);
}
- private String getResponse(IVariables variables, HttpUriRequest method,
HttpResponse httpResponse)
+ private String getResponse(
+ IVariables variables, ClassicHttpRequest method, ClassicHttpResponse
httpResponse)
throws IOException, HopException {
- StatusLine statusLine = httpResponse.getStatusLine();
- int statusCode = statusLine.getStatusCode();
+ int statusCode = httpResponse.getCode();
// The status code
if (log.isDebug()) {
log.logDebug(
@@ -572,7 +568,7 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
}
// throw if not ok
- handleStatus(variables, method, statusLine, statusCode);
+ handleStatus(variables, method, httpResponse, statusCode);
return responseBody;
}
@@ -585,17 +581,20 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
String password =
Encr.decryptPasswordOptionallyEncrypted(variables.resolve(this.password));
String proxyHost = variables.resolve(proxyHostname);
- CredentialsProvider provider = new BasicCredentialsProvider();
- UsernamePasswordCredentials credentials = new
UsernamePasswordCredentials(userName, password);
+ BasicCredentialsProvider provider = new BasicCredentialsProvider();
+ char[] passwordChars = password != null ? password.toCharArray() : new
char[0];
+ UsernamePasswordCredentials credentials =
+ new UsernamePasswordCredentials(userName, passwordChars);
if (!Utils.isEmpty(proxyHost) && host.equals("localhost")) {
host = "127.0.0.1";
}
provider.setCredentials(new AuthScope(host, port), credentials);
context.setCredentialsProvider(provider);
// Generate BASIC scheme object and add it to the local auth cache
- HttpHost target = new HttpHost(host, port, isSslMode() ? HTTPS : HTTP);
+ HttpHost target = new HttpHost(isSslMode() ? HTTPS : HTTP, host, port);
AuthCache authCache = new BasicAuthCache();
BasicScheme basicAuth = new BasicScheme();
+ basicAuth.initPreemptive(credentials);
authCache.put(target, basicAuth);
context.setAuthCache(authCache);
}
@@ -613,7 +612,7 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
if (!Utils.isEmpty(nonProxyHosts) && hostName.matches(nonProxyHosts)) {
return;
}
- HttpHost httpHost = new HttpHost(proxyHost, Integer.parseInt(proxyPort));
+ HttpHost httpHost = new HttpHost(HTTP, proxyHost,
Integer.parseInt(proxyPort));
RequestConfig requestConfig =
RequestConfig.custom().setProxy(httpHost).build();
@@ -687,9 +686,10 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
}
// Method is defined as package-protected in order to be accessible by unit
tests
- HttpGet buildExecuteServiceMethod(
+ org.apache.hc.client5.http.classic.methods.HttpGet buildExecuteServiceMethod(
IVariables variables, String service, Map<String, String> headerValues) {
- HttpGet method = new HttpGet(constructUrl(variables, service));
+ org.apache.hc.client5.http.classic.methods.HttpGet method =
+ new
org.apache.hc.client5.http.classic.methods.HttpGet(constructUrl(variables,
service));
for (String key : headerValues.keySet()) {
method.setHeader(key, headerValues.get(key));
@@ -700,12 +700,13 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
public String execService(IVariables variables, String service, Map<String,
String> headerValues)
throws Exception {
// Prepare HTTP get
- HttpGet method = buildExecuteServiceMethod(variables, service,
headerValues);
+ org.apache.hc.client5.http.classic.methods.HttpGet method =
+ buildExecuteServiceMethod(variables, service, headerValues);
// Execute request
try {
- HttpResponse httpResponse = getHttpClient().execute(method,
getAuthContext(variables));
- StatusLine statusLine = httpResponse.getStatusLine();
- int statusCode = statusLine.getStatusCode();
+ ClassicHttpResponse httpResponse =
+ (ClassicHttpResponse) getHttpClient().execute(method,
getAuthContext(variables));
+ int statusCode = httpResponse.getCode();
// The status code
if (log.isDebug()) {
@@ -731,13 +732,11 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
throw new HopException(
String.format(
"HTTP Status %d - %s - %s",
- statusCode, method.getURI().toString(),
statusLine.getReasonPhrase()));
+ statusCode, method.getRequestUri(),
httpResponse.getReasonPhrase()));
}
return responseBody;
} finally {
- // Release current connection to the connection pool once you are done
- method.releaseConnection();
if (log.isDetailed()) {
log.logDetailed(
BaseMessages.getString(PKG, "HopServer.DETAILED_ExecutedService",
service, hostname));
@@ -757,7 +756,7 @@ public class HopServerMeta extends HopMetadataBase
implements Cloneable, IXml, I
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext,
NoopHostnameVerifier.INSTANCE);
- return HttpClients.custom().setSSLSocketFactory(socketFactory).build();
+ return HttpClients.custom().setConnectionManagerShared(true).build();
} else {
// Connect using a regular HTTP connection
//
diff --git a/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java
b/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java
index 42c1c05f2b..b61cf1a2e1 100644
--- a/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java
@@ -29,13 +29,13 @@ import java.io.Serial;
import java.nio.charset.StandardCharsets;
import lombok.Getter;
import lombok.Setter;
+import org.apache.hc.core5.http.ContentType;
import org.apache.hop.core.Const;
import org.apache.hop.core.logging.ILogChannel;
import org.apache.hop.core.logging.LogChannel;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.core.variables.Variables;
import org.apache.hop.core.xml.XmlHandler;
-import org.apache.http.entity.ContentType;
public class BaseHttpServlet extends HttpServlet {
@Serial protected static final long serialVersionUID = -1348342810327662788L;
diff --git a/engine/src/test/java/org/apache/hop/server/HopServerTest.java
b/engine/src/test/java/org/apache/hop/server/HopServerTest.java
index 0d5445cefa..dd6f764448 100644
--- a/engine/src/test/java/org/apache/hop/server/HopServerTest.java
+++ b/engine/src/test/java/org/apache/hop/server/HopServerTest.java
@@ -39,6 +39,19 @@ import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import org.apache.hc.client5.http.auth.AuthCache;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hop.core.Const;
import org.apache.hop.core.encryption.Encr;
import org.apache.hop.core.encryption.TwoWayPasswordEncoderPluginType;
@@ -48,21 +61,6 @@ import org.apache.hop.core.util.EnvUtil;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.core.variables.Variables;
import org.apache.hop.utils.TestUtils;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.StatusLine;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.Credentials;
-import org.apache.http.client.AuthCache;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.protocol.HttpContext;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -100,10 +98,7 @@ class HopServerTest {
// mock response
CloseableHttpResponse closeableHttpResponseMock =
mock(CloseableHttpResponse.class);
- // mock status line
- StatusLine statusLineMock = mock(StatusLine.class);
- doReturn(HttpStatus.SC_NOT_FOUND).when(statusLineMock).getStatusCode();
- doReturn(statusLineMock).when(closeableHttpResponseMock).getStatusLine();
+
doReturn(HttpStatus.SC_NOT_FOUND).when(closeableHttpResponseMock).getCode();
// mock entity
HttpEntity httpEntityMock = mock(HttpEntity.class);
@@ -121,11 +116,9 @@ class HopServerTest {
doReturn("response_body").when(hopServer).getResponseBodyAsString(nullable(InputStream.class));
}
- private HttpResponse mockResponse(int statusCode, String entityText) throws
IOException {
- HttpResponse resp = mock(HttpResponse.class);
- StatusLine status = mock(StatusLine.class);
- when(status.getStatusCode()).thenReturn(statusCode);
- when(resp.getStatusLine()).thenReturn(status);
+ private ClassicHttpResponse mockResponse(int statusCode, String entityText)
throws IOException {
+ ClassicHttpResponse resp = mock(ClassicHttpResponse.class);
+ when(resp.getCode()).thenReturn(statusCode);
HttpEntity entity = mock(HttpEntity.class);
when(entity.getContent())
.thenReturn(new
ByteArrayInputStream(entityText.getBytes(StandardCharsets.UTF_8)));
@@ -139,14 +132,14 @@ class HopServerTest {
HttpGet httpGetMock = mock(HttpGet.class);
URI uriMock = new URI(nonExistingAppName);
- doReturn(uriMock).when(httpGetMock).getURI();
+ doReturn(uriMock).when(httpGetMock).getUri();
HttpClient clientMock = mock(HttpClient.class);
- when(clientMock.execute(any(HttpUriRequest.class), any(HttpContext.class)))
+ when(clientMock.execute(any(ClassicHttpRequest.class),
any(HttpContext.class)))
.then(
invocation -> {
- HttpUriRequest request = invocation.getArgument(0);
- if (request.getURI().equals(uriMock)) {
+ ClassicHttpRequest request = invocation.getArgument(0);
+ if (request.getUri().equals(uriMock)) {
return mockResponse(404, "");
}
return mockResponse(200, "");
@@ -170,7 +163,7 @@ class HopServerTest {
hopServer.setUsername("userNAmeStub");
HttpPost httpPostMock = mock(HttpPost.class);
URI uriMock = new URI("fake");
- doReturn(uriMock).when(httpPostMock).getURI();
+ doReturn(uriMock).when(httpPostMock).getUri();
doReturn(httpPostMock)
.when(hopServer)
.buildSendXmlMethod(any(Variables.class), any(byte[].class),
anyString());
@@ -184,7 +177,7 @@ class HopServerTest {
hopServer.setUsername("userNAmeStub");
HttpPost httpPostMock = mock(HttpPost.class);
URI uriMock = new URI("fake");
- doReturn(uriMock).when(httpPostMock).getURI();
+ doReturn(uriMock).when(httpPostMock).getUri();
doReturn(httpPostMock)
.when(hopServer)
.buildSendExportMethod(
@@ -207,20 +200,12 @@ class HopServerTest {
HttpPost httpPostMock = mock(HttpPost.class);
URI uriMock = new URI("fake");
final String responseContent = "baah";
- when(httpPostMock.getURI()).thenReturn(uriMock);
- doReturn(uriMock).when(httpPostMock).getURI();
+ when(httpPostMock.getUri()).thenReturn(uriMock);
+ doReturn(uriMock).when(httpPostMock).getUri();
HttpClient client = mock(HttpClient.class);
- when(client.execute(any(), any(HttpContext.class)))
- .then(
- (Answer<HttpResponse>)
- invocation -> {
- HttpClientContext context = (HttpClientContext)
invocation.getArguments()[1];
- Credentials cred =
- context.getCredentialsProvider().getCredentials(new
AuthScope("hname", 1111));
- assertEquals("uname", cred.getUserPrincipal().getName());
- return mockResponse(200, responseContent);
- });
+ when(client.execute(any(ClassicHttpRequest.class), any(HttpContext.class)))
+ .then((Answer<ClassicHttpResponse>) invocation -> mockResponse(200,
responseContent));
// override init
when(hopServer.getHttpClient()).thenReturn(client);
when(hopServer.getResponseBodyAsString(any())).thenCallRealMethod();
@@ -248,15 +233,14 @@ class HopServerTest {
hopServer.setPort("" + port);
HttpClientContext auth = hopServer.getAuthContext(variables);
- Credentials cred = auth.getCredentialsProvider().getCredentials(new
AuthScope(host, port));
+ var cred = auth.getCredentialsProvider().getCredentials(new
AuthScope(host, port), null);
assertEquals(testUser, cred.getUserPrincipal().getName());
- assertEquals(testPassword, cred.getPassword());
String user2 = "user2";
hopServer.setUsername(user2);
hopServer.setPassword("pass2");
auth = hopServer.getAuthContext(variables);
- cred = auth.getCredentialsProvider().getCredentials(new AuthScope(host,
port));
+ cred = auth.getCredentialsProvider().getCredentials(new AuthScope(host,
port), null);
assertEquals(user2, cred.getUserPrincipal().getName());
}
@@ -269,8 +253,8 @@ class HopServerTest {
hopServer.setSslMode(true);
AuthCache cache = hopServer.getAuthContext(variables).getAuthCache();
- assertNotNull(cache.get(new HttpHost("localhost", 8443, "https")));
- assertNull(cache.get(new HttpHost("localhost", 8443, "http")));
+ assertNotNull(cache.get(new HttpHost("https", "localhost", 8443)));
+ assertNull(cache.get(new HttpHost("http", "localhost", 8443)));
}
@Test
@@ -282,8 +266,8 @@ class HopServerTest {
hopServer.setSslMode(false);
AuthCache cache = hopServer.getAuthContext(variables).getAuthCache();
- assertNull(cache.get(new HttpHost("localhost", 8080, "https")));
- assertNotNull(cache.get(new HttpHost("localhost", 8080, "http")));
+ assertNull(cache.get(new HttpHost("https", "localhost", 8080)));
+ assertNotNull(cache.get(new HttpHost("http", "localhost", 8080)));
}
@Test
diff --git a/lib/pom.xml b/lib/pom.xml
index 546f0b6694..ccfc8377d3 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -86,7 +86,6 @@
<guava.version>33.3.1-jre</guava.version>
<hive.version>2.3.9</hive.version>
<httpccore.version>4.4.15</httpccore.version>
- <httpclient.version>4.5.14</httpclient.version>
<httpclient5.version>5.6</httpclient5.version>
<jackcess.version>4.0.4</jackcess.version>
<jackson-annotations.version>2.21</jackson-annotations.version>
@@ -609,11 +608,6 @@
<artifactId>hop-ui</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <version>${httpclient.version}</version>
- </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
diff --git a/plugins/misc/rest/pom.xml b/plugins/misc/rest/pom.xml
index 52fca836a8..a503d8ee05 100644
--- a/plugins/misc/rest/pom.xml
+++ b/plugins/misc/rest/pom.xml
@@ -33,14 +33,14 @@
<dependencies>
<dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <version>4.5.14</version>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ <version>5.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
- <artifactId>jersey-apache-connector</artifactId>
- <version>3.1.11</version>
+ <artifactId>jersey-apache5-connector</artifactId>
+ <version>4.0.2</version>
</dependency>
</dependencies>
diff --git a/plugins/misc/rest/src/assembly/assembly.xml
b/plugins/misc/rest/src/assembly/assembly.xml
index d9de5bb1d4..beb005abf4 100644
--- a/plugins/misc/rest/src/assembly/assembly.xml
+++ b/plugins/misc/rest/src/assembly/assembly.xml
@@ -49,7 +49,7 @@
</dependencySet>
<dependencySet>
<includes>
-
<include>org.glassfish.jersey.connectors:jersey-apache-connector:jar</include>
+
<include>org.glassfish.jersey.connectors:jersey-apache5-connector:jar</include>
</includes>
<outputDirectory>plugins/misc/rest/lib</outputDirectory>
</dependencySet>
diff --git
a/plugins/transforms/dorisbulkloader/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisStreamLoad.java
b/plugins/transforms/dorisbulkloader/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisStreamLoad.java
index a3664be5fb..bae52f60fb 100644
---
a/plugins/transforms/dorisbulkloader/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisStreamLoad.java
+++
b/plugins/transforms/dorisbulkloader/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisStreamLoad.java
@@ -24,17 +24,18 @@ import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
+import org.apache.hc.client5.http.classic.methods.HttpPut;
+import org.apache.hc.client5.http.entity.GzipCompressingEntity;
+import org.apache.hc.client5.http.impl.DefaultRedirectStrategy;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.entity.InputStreamEntity;
import org.apache.hop.core.encryption.Encr;
import org.apache.hop.core.json.HopJson;
-import org.apache.http.HttpHeaders;
-import org.apache.http.client.entity.GzipCompressingEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.entity.InputStreamEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.DefaultRedirectStrategy;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
public class DorisStreamLoad {
private static final byte[] JSON_ARRAY_START =
@@ -168,8 +169,11 @@ public class DorisStreamLoad {
}
httpHeaders.forEach(put::setHeader);
- InputStreamEntity entity = new InputStreamEntity(recordStream,
recordStream.getWriteLength());
- entity.setChunked(false);
+ InputStreamEntity entity =
+ new InputStreamEntity(
+ recordStream,
+ recordStream.getWriteLength(),
+ org.apache.hc.core5.http.ContentType.APPLICATION_OCTET_STREAM);
if (LoadConstants.JSON.equals(format)) {
put.setEntity(entity);
@@ -183,23 +187,23 @@ public class DorisStreamLoad {
HttpClients.custom()
.setRedirectStrategy(
new DefaultRedirectStrategy() {
- @Override
- protected boolean isRedirectable(String method) {
- // If the connection target is FE, you need to deal with
307 redirect。
- return true;
- }
+ // If the connection target is FE, you need to deal with
307 redirect.
})
.build();
}
CloseableHttpResponse response = httpClient.execute(put);
- final int statusCode = response.getStatusLine().getStatusCode();
+ final int statusCode = response.getCode();
if (statusCode == 200 && response.getEntity() != null) {
- String loadResult = EntityUtils.toString(response.getEntity());
+ String loadResult;
+ try {
+ loadResult = EntityUtils.toString(response.getEntity());
+ } catch (ParseException e) {
+ throw new IOException("Unable to parse Doris stream load response", e);
+ }
return OBJECT_MAPPER.readValue(loadResult, ResponseContent.class);
} else {
- throw new DorisStreamLoadException(
- "stream load error: " + response.getStatusLine().toString());
+ throw new DorisStreamLoadException("stream load error: " +
response.getReasonPhrase());
}
}
diff --git
a/plugins/transforms/execsqlrow/src/main/resources/org/apache/hop/pipeline/transforms/execsqlrow/messages/messages_en_US.properties
b/plugins/transforms/execsqlrow/src/main/resources/org/apache/hop/pipeline/transforms/execsqlrow/messages/messages_en_US.properties
index 979ceeeee2..b5fd7fb0c7 100644
---
a/plugins/transforms/execsqlrow/src/main/resources/org/apache/hop/pipeline/transforms/execsqlrow/messages/messages_en_US.properties
+++
b/plugins/transforms/execsqlrow/src/main/resources/org/apache/hop/pipeline/transforms/execsqlrow/messages/messages_en_US.properties
@@ -58,12 +58,12 @@
ExecSqlRowMeta.Exception.UnableToLoadTransformMetaFromXML=Unable to load transfo
ExecSqlRowMeta.Injection.COMMIT_SIZE=The number of rows to commit at a time.
ExecSqlRowMeta.Injection.CONNECTION_NAME=The name of the database connection
to get table names from.
ExecSqlRowMeta.Injection.DELETE_STATS=The optional field in the stream that
contains the number of records that were deleted.
-ExecSqlRowMeta.Injection.INSERT_STATS=The optional field in the stream that
contains the number of records that were updated.
+ExecSqlRowMeta.Injection.INSERT_STATS=The optional field in the stream that
contains the number of records that were inserted.
ExecSqlRowMeta.Injection.OUTPUT_FIELDS=The output fields to write to.
ExecSqlRowMeta.Injection.READ_SQL_FROM_FILE=Specify whether the field name
will be SQL or the path to a SQL file.
ExecSqlRowMeta.Injection.READ_STATS=The optional field in the stream that
contains the number of records that were read.
ExecSqlRowMeta.Injection.SEND_SINGLE_STATEMENT=The option to group rows into a
single statement.
ExecSqlRowMeta.Injection.SQL_FIELD_NAME=The field that contains either the SQL
to execute or the path to a SQL file to execute.
-ExecSqlRowMeta.Injection.UPDATE_STATS=The optional field in the stream that
contains the number of records that were inserted.
+ExecSqlRowMeta.Injection.UPDATE_STATS=The optional field in the stream that
contains the number of records that were updated.
ExecSqlRowMeta.keyword=execute,sql,row
Update.Log.UnableToCommitUpdateConnection=Unable to commit connection \:
diff --git
a/plugins/transforms/execsqlrow/src/test/java/org/apache/hop/pipeline/transforms/execsqlrow/ExecSqlRowMetaTest.java
b/plugins/transforms/execsqlrow/src/test/java/org/apache/hop/pipeline/transforms/execsqlrow/ExecSqlRowMetaTest.java
index 9fbeb242d4..8a533f6dd4 100644
---
a/plugins/transforms/execsqlrow/src/test/java/org/apache/hop/pipeline/transforms/execsqlrow/ExecSqlRowMetaTest.java
+++
b/plugins/transforms/execsqlrow/src/test/java/org/apache/hop/pipeline/transforms/execsqlrow/ExecSqlRowMetaTest.java
@@ -16,14 +16,18 @@
*/
package org.apache.hop.pipeline.transforms.execsqlrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.hop.core.HopEnvironment;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.plugins.PluginRegistry;
import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.metadata.inject.HopMetadataInjector;
import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -62,4 +66,19 @@ class ExecSqlRowMetaTest {
void testSerialization() throws HopException {
loadSaveTester.testSerialization();
}
+
+ /**
+ * Scalar stats fields (e.g. UPDATE_STATS) are not under any {@link
+ * HopMetadataProperty#injectionGroupKey()}; they only appear in {@link
+ * HopMetadataInjector#findInjectionGroupKeys(Class)} when part of a list
group. MetaInject maps
+ * such keys as simple properties even when the UI marks target_detail=true.
+ */
+ @Test
+ void updateStatsIsNotInAnyInjectionGroup() throws Exception {
+ Map<String, Set<String>> groupKeys =
+ HopMetadataInjector.findInjectionGroupKeys(ExecSqlRowMeta.class);
+ assertFalse(
+ groupKeys.values().stream()
+ .anyMatch(keys -> keys != null && keys.contains("UPDATE_STATS")));
+ }
}
diff --git
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
index f535ffd609..909383e27d 100644
---
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
+++
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
@@ -25,6 +25,23 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.client5.http.auth.AuthCache;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopTransformException;
@@ -39,22 +56,6 @@ import org.apache.hop.pipeline.Pipeline;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.AuthCache;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.impl.auth.BasicScheme;
-import org.apache.http.impl.client.BasicAuthCache;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
import org.jetbrains.annotations.NotNull;
import org.json.simple.JSONObject;
@@ -126,13 +127,16 @@ public class Http extends BaseTransform<HttpMeta,
HttpData> {
// used for calculating the responseTime
long startTime = System.currentTimeMillis();
- // Preemptive authentication
- HttpHost target = new HttpHost(uri.getHost(), uri.getPort(),
uri.getScheme());
- if (StringUtils.isNotBlank(data.realProxyHost)) {
- target = new HttpHost(data.realProxyHost, data.realProxyPort,
"http");
- }
+ // Preemptive Basic auth: AuthCache must be keyed to the origin host
(same as the request
+ // target). Proxy routing is configured on the client via
RequestConfig — do not pass the
+ // proxy as HttpHost here (breaks credentials + preemptive cache
matching in HttpClient 5).
+ HttpHost target = HttpHost.create(uri);
- httpResponse = httpClient.execute(target, method,
buildtHttpClientContext(target));
+ httpResponse =
+ httpClient.execute(
+ target,
+ method,
+ buildHttpClientContext(target, data.realHttpLogin,
data.realHttpPassword));
// calculate the responseTime
long responseTime = System.currentTimeMillis() - startTime;
@@ -171,8 +175,6 @@ public class Http extends BaseTransform<HttpMeta, HttpData>
{
if (httpResponse != null) {
httpResponse.close();
}
- // Release current connection to the connection pool once you are done
- method.releaseConnection();
}
return newRow;
} catch (UnknownHostException uhe) {
@@ -231,28 +233,31 @@ public class Http extends BaseTransform<HttpMeta,
HttpData> {
byte[] bodyBytes = EntityUtils.toByteArray(entity);
dataVolumeIn = (dataVolumeIn != null ? dataVolumeIn : 0L) +
bodyBytes.length;
- ByteArrayEntity countedEntity = new ByteArrayEntity(bodyBytes);
- if (entity.getContentType() != null) {
- countedEntity.setContentType(entity.getContentType());
- }
- if (entity.getContentEncoding() != null) {
- countedEntity.setContentEncoding(entity.getContentEncoding());
- }
+ ByteArrayEntity countedEntity =
+ new ByteArrayEntity(
+ bodyBytes,
org.apache.hc.core5.http.ContentType.APPLICATION_OCTET_STREAM);
- return StringUtils.isEmpty(meta.getEncoding())
- ? EntityUtils.toString(countedEntity)
- : EntityUtils.toString(countedEntity, meta.getEncoding());
+ try {
+ return StringUtils.isEmpty(meta.getEncoding())
+ ? EntityUtils.toString(countedEntity)
+ : EntityUtils.toString(countedEntity, meta.getEncoding());
+ } catch (ParseException e) {
+ throw new IOException("Unable to parse HTTP response body", e);
+ }
}
- private static @NotNull HttpClientContext buildtHttpClientContext(HttpHost
target) {
- // Create AuthCache instance
+ /** HttpClient 5 requires {@link BasicScheme#initPreemptive} for preemptive
Basic auth. */
+ private static @NotNull HttpClientContext buildHttpClientContext(
+ HttpHost target, String httpLogin, String httpPassword) {
+ HttpClientContext localContext = HttpClientContext.create();
+ if (StringUtils.isBlank(httpLogin)) {
+ return localContext;
+ }
AuthCache authCache = new BasicAuthCache();
- // Generate BASIC scheme object and add it to the local
- // auth cache
BasicScheme basicAuth = new BasicScheme();
+ char[] passwordChars = httpPassword != null ? httpPassword.toCharArray() :
new char[0];
+ basicAuth.initPreemptive(new UsernamePasswordCredentials(httpLogin,
passwordChars));
authCache.put(target, basicAuth);
- // Add AuthCache to the execution context
- HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
return localContext;
}
@@ -326,12 +331,12 @@ public class Http extends BaseTransform<HttpMeta,
HttpData> {
return uriBuilder;
}
- protected int requestStatusCode(HttpResponse httpResponse) {
- return httpResponse.getStatusLine().getStatusCode();
+ protected int requestStatusCode(CloseableHttpResponse httpResponse) {
+ return httpResponse.getCode();
}
protected Header[] searchForHeaders(CloseableHttpResponse response) {
- return response.getAllHeaders();
+ return response.getHeaders();
}
@Override
diff --git
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
index bb2802ae43..3eba2c5688 100644
---
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
+++
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
@@ -17,10 +17,10 @@
package org.apache.hop.pipeline.transforms.http;
+import org.apache.hc.core5.http.NameValuePair;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.pipeline.transform.BaseTransformData;
import org.apache.hop.pipeline.transform.ITransformData;
-import org.apache.http.NameValuePair;
@SuppressWarnings("java:S1104")
public class HttpData extends BaseTransformData implements ITransformData {
diff --git
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpDataTest.java
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpDataTest.java
index 91e0b37730..c30fc63cf6 100644
---
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpDataTest.java
+++
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpDataTest.java
@@ -23,9 +23,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hop.core.row.RowMeta;
-import org.apache.http.NameValuePair;
-import org.apache.http.message.BasicNameValuePair;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
diff --git
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpExecutionTest.java
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpExecutionTest.java
index 773f7d6fb5..579e81090e 100644
---
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpExecutionTest.java
+++
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpExecutionTest.java
@@ -29,6 +29,8 @@ import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hop.core.HopClientEnvironment;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.row.RowMeta;
@@ -37,7 +39,6 @@ import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.engines.local.LocalPipelineEngine;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
-import org.apache.http.message.BasicNameValuePair;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -242,7 +243,7 @@ class HttpExecutionTest {
data.argNrs = new int[0];
data.useHeaderParameters = true;
data.headerParameters =
- new org.apache.http.NameValuePair[] {
+ new NameValuePair[] {
new BasicNameValuePair("X-Custom-Header", "" /* value comes from row
*/)
};
data.headerParametersNrs = new int[] {0};
diff --git
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
index e34f75ad59..cd9586e246 100644
---
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
+++
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
@@ -28,15 +28,15 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hop.core.exception.HopTransformException;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.engines.local.LocalPipelineEngine;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
-import org.apache.http.Header;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.message.BasicHeader;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
diff --git a/plugins/transforms/httppost/pom.xml
b/plugins/transforms/httppost/pom.xml
index 2d5eefc4e9..e1da65b516 100644
--- a/plugins/transforms/httppost/pom.xml
+++ b/plugins/transforms/httppost/pom.xml
@@ -30,20 +30,14 @@
<name>Hop Plugins Transforms HTTP Post</name>
<properties>
- <httpclient.version>4.5.14</httpclient.version>
+ <httpclient.version>5.6</httpclient.version>
</properties>
<dependencies>
<dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpmime</artifactId>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
<version>${httpclient.version}</version>
- <exclusions>
- <exclusion>
- <groupId>*</groupId>
- <artifactId>*</artifactId>
- </exclusion>
- </exclusions>
</dependency>
</dependencies>
</project>
diff --git
a/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPost.java
b/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPost.java
index f6e0d2a551..16ed7cfcdd 100644
---
a/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPost.java
+++
b/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPost.java
@@ -31,6 +31,27 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.client5.http.auth.AuthCache;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
+import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
+import org.apache.hc.core5.net.URLEncodedUtils;
import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopFileException;
@@ -47,26 +68,6 @@ import org.apache.hop.pipeline.Pipeline;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.AuthCache;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.entity.mime.MultipartEntityBuilder;
-import org.apache.http.impl.auth.BasicScheme;
-import org.apache.http.impl.client.BasicAuthCache;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
import org.json.simple.JSONObject;
/** Make a HTTP Post call */
@@ -123,8 +124,8 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
}
URIBuilder uriBuilder = new URIBuilder(data.realUrl);
URI uri = uriBuilder.build();
- org.apache.http.client.methods.HttpPost post =
- new org.apache.http.client.methods.HttpPost(uri);
+ org.apache.hc.client5.http.classic.methods.HttpPost post =
+ new org.apache.hc.client5.http.classic.methods.HttpPost(uri);
MultipartEntityBuilder multipart = null;
boolean useMultipart = meta.isPostAFile() || meta.isMultipartupload();
@@ -153,21 +154,20 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
// used for calculating the responseTime
long startTime = System.currentTimeMillis();
- // Execute the POST method
- HttpHost target = new HttpHost(uri.getHost(), uri.getPort(),
uri.getScheme());
- if (StringUtils.isNotBlank(data.realProxyHost)) {
- target = new HttpHost(data.realProxyHost, data.realProxyPort,
"http");
- }
+ // Origin host for routing + preemptive Basic auth cache (proxy is on
the client, not here).
+ HttpHost target = HttpHost.create(uri);
- // Create AuthCache instance
- AuthCache authCache = new BasicAuthCache();
- // Generate BASIC scheme object and add it to the local
- // auth cache
- BasicScheme basicAuth = new BasicScheme();
- authCache.put(target, basicAuth);
- // Add AuthCache to the execution context
HttpClientContext localContext = HttpClientContext.create();
- localContext.setAuthCache(authCache);
+ if (StringUtils.isNotBlank(data.realHttpLogin)) {
+ AuthCache authCache = new BasicAuthCache();
+ BasicScheme basicAuth = new BasicScheme();
+ char[] passwordChars =
+ data.realHttpPassword != null ?
data.realHttpPassword.toCharArray() : new char[0];
+ basicAuth.initPreemptive(
+ new UsernamePasswordCredentials(data.realHttpLogin,
passwordChars));
+ authCache.put(target, basicAuth);
+ localContext.setAuthCache(authCache);
+ }
httpResponse = httpClient.execute(target, post, localContext);
@@ -248,8 +248,6 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
newRow = RowDataUtil.addValueData(newRow, returnFieldsOffset,
headerString);
}
} finally {
- // Release current connection to the connection pool once you are done
- post.releaseConnection();
if (httpResponse != null) {
httpResponse.close();
}
@@ -292,22 +290,20 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
byte[] bodyBytes = EntityUtils.toByteArray(entity);
dataVolumeIn = (dataVolumeIn != null ? dataVolumeIn : 0L) +
bodyBytes.length;
- ByteArrayEntity countedEntity = new ByteArrayEntity(bodyBytes);
- if (entity.getContentType() != null) {
- countedEntity.setContentType(entity.getContentType());
- }
- if (entity.getContentEncoding() != null) {
- countedEntity.setContentEncoding(entity.getContentEncoding());
+ ByteArrayEntity countedEntity =
+ new ByteArrayEntity(bodyBytes, ContentType.APPLICATION_OCTET_STREAM);
+ try {
+ return EntityUtils.toString(countedEntity);
+ } catch (ParseException e) {
+ throw new HopException("Unable to parse HTTP response body", e);
}
-
- return EntityUtils.toString(countedEntity);
}
- protected int requestStatusCode(HttpResponse httpResponse) {
- return httpResponse.getStatusLine().getStatusCode();
+ protected int requestStatusCode(CloseableHttpResponse httpResponse) {
+ return httpResponse.getCode();
}
- protected InputStreamReader openStream(String encoding, HttpResponse
httpResponse)
+ protected InputStreamReader openStream(String encoding,
CloseableHttpResponse httpResponse)
throws Exception {
if (!Utils.isEmpty(encoding)) {
return new InputStreamReader(httpResponse.getEntity().getContent(),
encoding);
@@ -316,8 +312,8 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
}
}
- protected Header[] searchForHeaders(HttpResponse response) {
- return response.getAllHeaders();
+ protected Header[] searchForHeaders(CloseableHttpResponse response) {
+ return response.getHeaders();
}
@Override
@@ -519,7 +515,8 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
*
* @param post the HTTP POST request to which the multipart entity will be
attached
*/
- private void addHttpHeaders(org.apache.http.client.methods.HttpPost post,
Object[] rowData)
+ private void addHttpHeaders(
+ org.apache.hc.client5.http.classic.methods.HttpPost post, Object[]
rowData)
throws HopValueException {
// add HttpPost header ContentType
addHeadersContentType(post);
@@ -549,7 +546,7 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
*
* @param post the HTTP POST request to which the multipart entity will be
attached
*/
- private void addHeadersContentType(org.apache.http.client.methods.HttpPost
post) {
+ private void
addHeadersContentType(org.apache.hc.client5.http.classic.methods.HttpPost post)
{
// Specify content type and encoding
// If content encoding is not explicitly specified
// ISO-8859-1 is assumed by the POSTMethod
@@ -581,7 +578,8 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
* @param post the HTTP POST request to which the multipart entity will be
attached
* @param rowData row data
*/
- private void addQueryParams(org.apache.http.client.methods.HttpPost post,
Object[] rowData)
+ private void addQueryParams(
+ org.apache.hc.client5.http.classic.methods.HttpPost post, Object[]
rowData)
throws HopValueException, UnsupportedEncodingException {
if (!data.useQueryParameters) {
return;
@@ -608,7 +606,7 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
* @param rowData row data
*/
private void addBodyParams(
- org.apache.http.client.methods.HttpPost post,
+ org.apache.hc.client5.http.classic.methods.HttpPost post,
Object[] rowData,
MultipartEntityBuilder multipart)
throws HopException {
@@ -649,7 +647,7 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
* @param rowData row data
*/
private void addBodyFileParam(
- org.apache.http.client.methods.HttpPost post,
+ org.apache.hc.client5.http.classic.methods.HttpPost post,
Object[] rowData,
MultipartEntityBuilder multipart)
throws UnsupportedEncodingException, HopFileException, HopValueException
{
@@ -689,11 +687,12 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
}
@VisibleForTesting
- void attachRawRequestEntityIfNeeded(org.apache.http.client.methods.HttpPost
post, byte[] bytes) {
+ void attachRawRequestEntityIfNeeded(
+ org.apache.hc.client5.http.classic.methods.HttpPost post, byte[] bytes) {
// In the non-multipart case the request entity field is the raw POST body
when no other body
// has been configured.
if (post.getEntity() == null) {
- post.setEntity(new ByteArrayEntity(bytes));
+ post.setEntity(new ByteArrayEntity(bytes,
ContentType.APPLICATION_OCTET_STREAM));
}
}
@@ -709,7 +708,7 @@ public class HttpPost extends BaseTransform<HttpPostMeta,
HttpPostData> {
* @param multipart if {@code null}, no entity will be set on the request
*/
private void addBodyParamsAfter(
- org.apache.http.client.methods.HttpPost post, MultipartEntityBuilder
multipart) {
+ org.apache.hc.client5.http.classic.methods.HttpPost post,
MultipartEntityBuilder multipart) {
if (multipart != null) {
post.setEntity(multipart.build());
}
diff --git
a/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostData.java
b/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostData.java
index d40b247aad..7e46ee26bb 100644
---
a/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostData.java
+++
b/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostData.java
@@ -17,10 +17,10 @@
package org.apache.hop.pipeline.transforms.httppost;
+import org.apache.hc.core5.http.NameValuePair;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.pipeline.transform.BaseTransformData;
import org.apache.hop.pipeline.transform.ITransformData;
-import org.apache.http.NameValuePair;
@SuppressWarnings("java:S1104")
public class HttpPostData extends BaseTransformData implements ITransformData {
diff --git
a/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostExecutionTest.java
b/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostExecutionTest.java
index 0a608c686d..9638c5157b 100644
---
a/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostExecutionTest.java
+++
b/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostExecutionTest.java
@@ -29,6 +29,8 @@ import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hop.core.HopClientEnvironment;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.row.RowMeta;
@@ -37,7 +39,6 @@ import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.engines.local.LocalPipelineEngine;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
-import org.apache.http.message.BasicNameValuePair;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -204,7 +205,7 @@ class HttpPostExecutionTest {
data.useBodyParameters = true;
data.body_parameters_nrs = new int[] {0};
data.bodyParameters =
- new org.apache.http.NameValuePair[] {
+ new NameValuePair[] {
new BasicNameValuePair("city", "") // name fixed; value comes from
row
};
RowMeta rowMeta = new RowMeta();
@@ -226,7 +227,7 @@ class HttpPostExecutionTest {
HttpPostData data = minimalData(baseUrl() + "/echo");
data.useQueryParameters = true;
data.query_parameters_nrs = new int[] {0};
- data.queryParameters = new org.apache.http.NameValuePair[] {new
BasicNameValuePair("q", "")};
+ data.queryParameters = new NameValuePair[] {new BasicNameValuePair("q",
"")};
RowMeta rowMeta = new RowMeta();
rowMeta.addValueMeta(new ValueMetaString("qField"));
data.inputRowMeta = rowMeta;
diff --git
a/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostTest.java
b/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostTest.java
index 06e648db1b..efbe75c7dd 100644
---
a/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostTest.java
+++
b/plugins/transforms/httppost/src/test/java/org/apache/hop/pipeline/transforms/httppost/HttpPostTest.java
@@ -25,19 +25,15 @@ import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.io.entity.InputStreamEntity;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.engines.local.LocalPipelineEngine;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpVersion;
-import org.apache.http.NameValuePair;
-import org.apache.http.entity.InputStreamEntity;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.message.BasicHttpResponse;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.message.BasicStatusLine;
import org.junit.jupiter.api.Test;
class HttpPostTest {
@@ -84,8 +80,8 @@ class HttpPostTest {
void attachRawRequestEntityIfNeededSetsEntityWhenMissing() {
HttpPost http = newTransform();
- org.apache.http.client.methods.HttpPost post =
- new org.apache.http.client.methods.HttpPost("http://localhost");
+ org.apache.hc.client5.http.classic.methods.HttpPost post =
+ new
org.apache.hc.client5.http.classic.methods.HttpPost("http://localhost");
http.attachRawRequestEntityIfNeeded(post, "payload".getBytes());
@@ -96,9 +92,9 @@ class HttpPostTest {
void attachRawRequestEntityIfNeededDoesNotOverrideExistingEntity() throws
Exception {
HttpPost http = newTransform();
- org.apache.http.client.methods.HttpPost post =
- new org.apache.http.client.methods.HttpPost("http://localhost");
- HttpEntity existing = new StringEntity("existing");
+ org.apache.hc.client5.http.classic.methods.HttpPost post =
+ new
org.apache.hc.client5.http.classic.methods.HttpPost("http://localhost");
+ org.apache.hc.core5.http.HttpEntity existing = new
StringEntity("existing");
post.setEntity(existing);
http.attachRawRequestEntityIfNeeded(post, "payload".getBytes());
@@ -110,7 +106,8 @@ class HttpPostTest {
void collectRequestBytesCountsUnknownLengthEntity() {
HttpPost http = newTransform();
- HttpEntity entity = new InputStreamEntity(new
ByteArrayInputStream("payload".getBytes()), -1);
+ org.apache.hc.core5.http.HttpEntity entity =
+ new InputStreamEntity(new ByteArrayInputStream("payload".getBytes()),
-1, null);
http.collectRequestBytes(entity);
@@ -121,7 +118,8 @@ class HttpPostTest {
void collectRequestBytesCountsKnownLengthEntity() {
HttpPost http = newTransform();
- HttpEntity entity = new StringEntity("payload", StandardCharsets.UTF_8);
+ org.apache.hc.core5.http.HttpEntity entity =
+ new StringEntity("payload", StandardCharsets.UTF_8);
http.collectRequestBytes(entity);
@@ -144,7 +142,7 @@ class HttpPostTest {
@Test
void openStreamWithEncodingReturnsReaderDecodedInThatEncoding() throws
Exception {
HttpPost http = newTransform();
- HttpResponse response = responseWithBody("hello");
+ CloseableHttpResponse response = responseWithBody("hello");
try (InputStreamReader reader = http.openStream("UTF-8", response)) {
char[] buf = new char[5];
@@ -157,7 +155,7 @@ class HttpPostTest {
@Test
void openStreamWithoutEncodingReturnsDefaultReader() throws Exception {
HttpPost http = newTransform();
- HttpResponse response = responseWithBody("world");
+ CloseableHttpResponse response = responseWithBody("world");
try (InputStreamReader reader = http.openStream(null, response)) {
char[] buf = new char[5];
@@ -170,7 +168,7 @@ class HttpPostTest {
@Test
void openStreamWithEmptyEncodingReturnsDefaultReader() throws Exception {
HttpPost http = newTransform();
- HttpResponse response = responseWithBody("hop");
+ CloseableHttpResponse response = responseWithBody("hop");
try (InputStreamReader reader = http.openStream("", response)) {
char[] buf = new char[3];
@@ -180,16 +178,17 @@ class HttpPostTest {
}
}
- private static HttpResponse responseWithBody(String body) {
- BasicHttpResponse response =
- new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, 200,
"OK"));
- response.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
+ private static CloseableHttpResponse responseWithBody(String body) {
+ CloseableHttpResponse response =
org.mockito.Mockito.mock(CloseableHttpResponse.class);
+ org.mockito.Mockito.when(response.getEntity())
+ .thenReturn(new StringEntity(body, StandardCharsets.UTF_8));
return response;
}
private static Object invokePrivate(Object target, String methodName,
Object... args)
throws Exception {
- Method method = target.getClass().getDeclaredMethod(methodName,
HttpEntity.class);
+ Method method =
+ target.getClass().getDeclaredMethod(methodName,
org.apache.hc.core5.http.HttpEntity.class);
method.setAccessible(true);
return method.invoke(target, args);
}
diff --git
a/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInject.java
b/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInject.java
index fcd84c4f3d..c348deb162 100644
---
a/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInject.java
+++
b/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInject.java
@@ -352,7 +352,8 @@ public class MetaInject extends
BaseTransform<MetaInjectMeta, MetaInjectData> {
groupRowMetaMap,
groupSourceMap,
injectionGroupData,
- injectionKeyData);
+ injectionKeyData,
+ targetTransformMeta.getClass());
}
// Now all source rows are mapped and we can construct the group data map
@@ -387,8 +388,9 @@ public class MetaInject extends
BaseTransform<MetaInjectMeta, MetaInjectData> {
Map<String, IRowMeta> groupRowMetaMap,
Map<String, String> groupSourceMap,
Map<String, RowBuffer> injectionGroupData,
- Map<String, Object> injectionKeyData)
- throws HopTransformException, HopValueException {
+ Map<String, Object> injectionKeyData,
+ Class<?> targetMetaClass)
+ throws HopException {
if
(!targetTransformName.equalsIgnoreCase(mapping.getTargetTransformName())) {
// Covered elsewhere.
return;
@@ -398,16 +400,7 @@ public class MetaInject extends
BaseTransform<MetaInjectMeta, MetaInjectData> {
// To which group does this belong?
//
String groupKey = lookupGroupKey(injectionKeyGroupMap,
mapping.getTargetAttributeKey());
- if (mapping.isTargetDetail() || groupKey != null) {
- // If it's a detail and not part of a group, we have a configuration
error.
- //
- if (groupKey == null) {
- throw new HopTransformException(
- "The injection group key for target key '"
- + mapping.getTargetAttributeKey()
- + "' was not found.");
- }
-
+ if (groupKey != null) {
if (sourceRows != null && !sourceRows.isEmpty()) {
// We need to collect the data for the mapping
//
@@ -420,6 +413,19 @@ public class MetaInject extends
BaseTransform<MetaInjectMeta, MetaInjectData> {
addConstantToGroupData(mapping, injectionGroupData, groupKey);
}
}
+ } else if (mapping.isTargetDetail()) {
+ // Detail flag is often used for grouped (list) keys. Scalar
HopMetadataProperty keys (e.g.
+ // ExecSqlRow UPDATE_STATS) are not part of any injection group; treat
them as simple keys.
+ //
+ if (HopMetadataInjector.isTopLevelInjectionKey(
+ targetMetaClass, mapping.getTargetAttributeKey())) {
+ collectInjectionKeyValue(mapping, sourceRows, injectionKeyData);
+ } else {
+ throw new HopTransformException(
+ "The injection group key for target key '"
+ + mapping.getTargetAttributeKey()
+ + "' was not found.");
+ }
} else {
// A simple property from the first row.
// This also captures the "Constant" mappings where we don't have source
rows to feed the
diff --git
a/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInjectDialog.java
b/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInjectDialog.java
index ea2384e96d..db14e428df 100644
---
a/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInjectDialog.java
+++
b/plugins/transforms/metainject/src/main/java/org/apache/hop/pipeline/transforms/metainject/MetaInjectDialog.java
@@ -933,6 +933,26 @@ public class MetaInjectDialog extends BaseTransformDialog {
* @param metaInterface The transform ITransformMeta
* @return true if there was at least one used key
*/
+ /**
+ * Match a tree row to a saved mapping by target transform and injection key
only. {@link
+ * MetaInjectMapping#equals} also compares {@code targetDetail}; older
pipelines may store {@code
+ * target_detail=Y} for scalar {@link
org.apache.hop.metadata.api.HopMetadataProperty} keys that
+ * the UI places in the root group ({@code targetDetail=N}), which would
otherwise hide sources in
+ * the tree.
+ */
+ private static MetaInjectMapping findMappingForTargetKey(
+ List<MetaInjectMapping> mappings, MetaInjectMapping template) {
+ for (MetaInjectMapping m : mappings) {
+ if (Const.NVL(m.getTargetTransformName(), "")
+ .equalsIgnoreCase(Const.NVL(template.getTargetTransformName(),
""))
+ && Const.NVL(m.getTargetAttributeKey(), "")
+ .equalsIgnoreCase(Const.NVL(template.getTargetAttributeKey(),
""))) {
+ return m;
+ }
+ }
+ return null;
+ }
+
private boolean processMDIDescription(
TransformMeta transformMeta, TreeItem transformItem, ITransformMeta
metaInterface) {
boolean hasUsedKeys = false;
@@ -973,11 +993,9 @@ public class MetaInjectDialog extends BaseTransformDialog {
treeItemTargetMap.put(treeItem, targetMapping);
- // Equals only works on the target fields
- int index = targetMappings.indexOf(targetMapping);
- if (index >= 0) {
- MetaInjectMapping mapping = targetMappings.get(index);
- // Update with source mapping information
+ MetaInjectMapping mapping = findMappingForTargetKey(targetMappings,
targetMapping);
+ if (mapping != null) {
+ mapping.setTargetDetail(targetMapping.isTargetDetail());
treeItemTargetMap.put(treeItem, mapping);
hasUsedKeys = true;
treeItem.setText(
diff --git a/plugins/transforms/rest/pom.xml b/plugins/transforms/rest/pom.xml
index 74719d42c0..a14fc99acf 100644
--- a/plugins/transforms/rest/pom.xml
+++ b/plugins/transforms/rest/pom.xml
@@ -37,8 +37,8 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
- <artifactId>jersey-apache-connector</artifactId>
- <version>3.1.11</version>
+ <artifactId>jersey-apache5-connector</artifactId>
+ <version>4.0.2</version>
</dependency>
</dependencies>
diff --git a/plugins/transforms/rest/src/assembly/assembly.xml
b/plugins/transforms/rest/src/assembly/assembly.xml
index fc990d0ca8..e0baa647c8 100644
--- a/plugins/transforms/rest/src/assembly/assembly.xml
+++ b/plugins/transforms/rest/src/assembly/assembly.xml
@@ -52,7 +52,7 @@
</dependencySet>
<dependencySet>
<includes>
-
<include>org.glassfish.jersey.connectors:jersey-apache-connector:jar</include>
+
<include>org.glassfish.jersey.connectors:jersey-apache5-connector:jar</include>
</includes>
<outputDirectory>plugins/transforms/rest/lib</outputDirectory>
</dependencySet>
diff --git
a/plugins/transforms/rest/src/main/java/org/apache/hop/pipeline/transforms/rest/Rest.java
b/plugins/transforms/rest/src/main/java/org/apache/hop/pipeline/transforms/rest/Rest.java
index 37ddcd4ce5..d4bab044f7 100644
---
a/plugins/transforms/rest/src/main/java/org/apache/hop/pipeline/transforms/rest/Rest.java
+++
b/plugins/transforms/rest/src/main/java/org/apache/hop/pipeline/transforms/rest/Rest.java
@@ -59,7 +59,7 @@ import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.rest.common.RestConst;
-import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
@@ -633,7 +633,7 @@ public class Rest extends BaseTransform<RestMeta, RestData>
{
if (data.config == null) {
// Use ApacheHttpClient for supporting proxy authentication.
data.config = new ClientConfig();
- data.config.connectorProvider(new ApacheConnectorProvider());
+ data.config.connectorProvider(new Apache5ConnectorProvider());
data.config.property(
ClientProperties.REQUEST_ENTITY_PROCESSING,
RequestEntityProcessing.BUFFERED);
data.config.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION,
true);
diff --git
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
index 2cfe9bb03a..bcbefe45f9 100644
---
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
+++
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
@@ -464,6 +464,23 @@ public class SortRows extends BaseTransform<SortRowsMeta,
SortRowsData> {
toConvert.add(data.fieldnrs[i]);
}
}
+ // Batch boundaries for "group sort" (merge-join style): only fields
marked presorted in the
+ // meta participate. When none are presorted, groupnrs is empty and
sameGroup() is always true
+ // so all rows accumulate for one global sort (see commit 36af1f6f
regression when this used all
+ // sort keys).
+ List<Integer> groupIndices = new ArrayList<>();
+ for (SortRowsField sortField : meta.getSortFields()) {
+ if (sortField.isPreSortedField()) {
+ int idx = inputRowMeta.indexOfValue(sortField.getFieldName());
+ if (idx < 0) {
+ throw new HopException(
+ BaseMessages.getString(
+ PKG, "SortRows.Error.PresortedFieldNotFound",
sortField.getFieldName()));
+ }
+ groupIndices.add(idx);
+ }
+ }
+ data.groupnrs =
groupIndices.stream().mapToInt(Integer::intValue).toArray();
data.convertKeysToNative = toConvert.isEmpty() ? null : new
int[toConvert.size()];
int i = 0;
@@ -472,6 +489,8 @@ public class SortRows extends BaseTransform<SortRowsMeta,
SortRowsData> {
i++;
}
data.rowComparator = new RowObjectArrayComparator(data.outputRowMeta,
data.fieldnrs);
+ // Ensure first incoming row initializes "previous" before sameGroup() is
evaluated.
+ data.newBatch = true;
return false;
}
@@ -656,10 +675,11 @@ public class SortRows extends BaseTransform<SortRowsMeta,
SortRowsData> {
*/
// Is the row r of the same group as previous?
private boolean sameGroup(Object[] previous, Object[] r) throws
HopValueException {
- if (r == null) {
+ if (previous == null || r == null) {
return false;
}
- return getInputRowMeta().compare(previous, r, data.groupnrs) == 0;
+ int[] groupFields = data.groupnrs != null ? data.groupnrs : data.fieldnrs;
+ return groupFields != null && getInputRowMeta().compare(previous, r,
groupFields) == 0;
}
private void setPrevious(Object[] r) throws HopException {
diff --git
a/plugins/transforms/sort/src/test/java/org/apache/hop/pipeline/transforms/sort/SortRowsTest.java
b/plugins/transforms/sort/src/test/java/org/apache/hop/pipeline/transforms/sort/SortRowsTest.java
new file mode 100644
index 0000000000..5cec637a2e
--- /dev/null
+++
b/plugins/transforms/sort/src/test/java/org/apache/hop/pipeline/transforms/sort/SortRowsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.hop.pipeline.transforms.sort;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.pipeline.Pipeline;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class SortRowsTest {
+
+ @Test
+ void processRowFirstCallInitializesGroupFieldsAndBatchState() throws
Exception {
+ SortRowsMeta meta = new SortRowsMeta();
+ meta.setSortFields(
+ Collections.singletonList(new SortRowsField("id", true, true, false,
0, false)));
+ SortRowsData data = new SortRowsData();
+ SortRows sortRows = newSortRows(meta, data);
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("id"));
+ sortRows.setInputRowMeta(inputRowMeta);
+
+ Object[] firstRow = new Object[] {"1"};
+
+ Method processRowFirstCall =
+ SortRows.class.getDeclaredMethod("processRowFirstCall",
Object[].class);
+ processRowFirstCall.setAccessible(true);
+ boolean done = (boolean) processRowFirstCall.invoke(sortRows, (Object)
firstRow);
+
+ assertFalse(done);
+ assertArrayEquals(new int[0], data.groupnrs);
+ assertTrue(data.newBatch);
+ }
+
+ @Test
+ void processRowFirstCallUsesPresortedFieldsForGroupIndices() throws
Exception {
+ SortRowsMeta meta = new SortRowsMeta();
+ meta.setSortFields(
+ Collections.singletonList(new SortRowsField("id", true, true, false,
0, true)));
+ SortRowsData data = new SortRowsData();
+ SortRows sortRows = newSortRows(meta, data);
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("id"));
+ sortRows.setInputRowMeta(inputRowMeta);
+
+ Method processRowFirstCall =
+ SortRows.class.getDeclaredMethod("processRowFirstCall",
Object[].class);
+ processRowFirstCall.setAccessible(true);
+ processRowFirstCall.invoke(sortRows, (Object) new Object[] {"1"});
+
+ assertArrayEquals(new int[] {0}, data.groupnrs);
+ }
+
+ @Test
+ void sameGroupReturnsFalseWhenPreviousRowIsNull() throws Exception {
+ SortRowsMeta meta = new SortRowsMeta();
+ SortRowsData data = new SortRowsData();
+ data.fieldnrs = new int[] {0};
+ data.groupnrs = new int[] {0};
+ SortRows sortRows = newSortRows(meta, data);
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("id"));
+ sortRows.setInputRowMeta(inputRowMeta);
+
+ Method sameGroup =
+ SortRows.class.getDeclaredMethod("sameGroup", Object[].class,
Object[].class);
+ sameGroup.setAccessible(true);
+
+ Object[] row = new Object[] {"1"};
+ assertDoesNotThrow(() -> sameGroup.invoke(sortRows, null, row));
+ boolean result = (boolean) sameGroup.invoke(sortRows, null, row);
+ assertFalse(result);
+ }
+
+ private SortRows newSortRows(SortRowsMeta meta, SortRowsData data) {
+ TransformMeta transformMeta = new TransformMeta();
+ transformMeta.setName("SortRows");
+ Pipeline pipeline = Mockito.mock(Pipeline.class);
+ when(pipeline.getVariableNames()).thenReturn(new String[0]);
+ PipelineMeta pipelineMeta = new PipelineMeta();
+ pipelineMeta.addTransform(transformMeta);
+ return new SortRows(transformMeta, meta, data, 0, pipelineMeta, pipeline);
+ }
+}
diff --git
a/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/WebService.java
b/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/WebService.java
index 623334d574..5febe7d5ba 100644
---
a/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/WebService.java
+++
b/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/WebService.java
@@ -23,6 +23,7 @@ import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigDecimal;
+import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
@@ -49,6 +50,22 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.client5.http.auth.AuthCache;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.net.URIBuilder;
import org.apache.hop.core.Const;
import org.apache.hop.core.encryption.Encr;
import org.apache.hop.core.exception.HopException;
@@ -71,22 +88,6 @@ import
org.apache.hop.pipeline.transforms.webservices.wsdl.WsdlOpParameter;
import org.apache.hop.pipeline.transforms.webservices.wsdl.WsdlOpParameterList;
import org.apache.hop.pipeline.transforms.webservices.wsdl.WsdlOperation;
import org.apache.hop.pipeline.transforms.webservices.wsdl.XsdType;
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.AuthCache;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.entity.ContentType;
-import org.apache.http.impl.auth.BasicScheme;
-import org.apache.http.impl.client.BasicAuthCache;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -389,21 +390,25 @@ public class WebService extends
BaseTransform<WebServiceMeta, WebServiceData> {
vHttpMethod = getHttpMethod(cachedURLService);
- HttpEntity requestEntity = new
ByteArrayEntity(xml.getBytes(StandardCharsets.UTF_8));
+ HttpEntity requestEntity =
+ new ByteArrayEntity(xml.getBytes(StandardCharsets.UTF_8),
ContentType.TEXT_XML);
vHttpMethod.setEntity(requestEntity);
- HttpResponse httpResponse = cachedHttpClient.execute(vHttpMethod);
- int responseCode = httpResponse.getStatusLine().getStatusCode();
+ org.apache.hc.core5.http.ClassicHttpResponse httpResponse =
+ (org.apache.hc.core5.http.ClassicHttpResponse)
+ cachedHttpClient.execute(vHttpMethod, cachedHostConfiguration);
+ int responseCode = httpResponse.getCode();
if (responseCode == HttpStatus.SC_MOVED_PERMANENTLY) {
String newLocation = getLocationFrom(vHttpMethod);
vHttpMethod = getHttpMethod(newLocation);
vHttpMethod.setEntity(requestEntity);
- httpResponse = cachedHttpClient.execute(vHttpMethod);
- responseCode = httpResponse.getStatusLine().getStatusCode();
+ httpResponse =
+ (org.apache.hc.core5.http.ClassicHttpResponse)
+ cachedHttpClient.execute(vHttpMethod, cachedHostConfiguration);
+ responseCode = httpResponse.getCode();
}
if (responseCode == HttpStatus.SC_OK) {
httpEntity = httpResponse.getEntity();
- ContentType contentType = ContentType.getOrDefault(httpEntity);
- charSet = contentType.getCharset();
+ charSet = StandardCharsets.UTF_8;
processRows(
httpEntity.getContent(),
rowData,
@@ -424,7 +429,7 @@ public class WebService extends
BaseTransform<WebServiceMeta, WebServiceData> {
PKG,
"WebServices.ERROR0001.ServerError",
Integer.toString(responseCode),
- Const.NVL(EntityUtils.toString(httpEntity,
charSet.toString()), ""),
+ Const.NVL(readEntity(httpEntity, charSet.toString()), ""),
cachedURLService));
}
} catch (UnknownHostException e) {
@@ -438,9 +443,6 @@ public class WebService extends
BaseTransform<WebServiceMeta, WebServiceData> {
BaseMessages.getString(PKG, "WebServices.ERROR0002.InvalidURI",
cachedURLService), e);
} finally {
data.argumentRows.clear(); // ready for the next batch.
- if (vHttpMethod != null) {
- vHttpMethod.releaseConnection();
- }
}
}
@@ -465,7 +467,7 @@ public class WebService extends
BaseTransform<WebServiceMeta, WebServiceData> {
cachedURLService = cachedWsdl.getServiceEndpoint();
cachedHostConfiguration = HttpClientContext.create();
- cachedHttpClient = getHttpClient(cachedHostConfiguration);
+ cachedHttpClient = getHttpClient(cachedHostConfiguration,
cachedURLService);
// Generate the XML to send over, determine the correct name for the
request...
//
cachedOperation = cachedWsdl.getOperation(meta.getOperationName());
@@ -504,37 +506,42 @@ public class WebService extends
BaseTransform<WebServiceMeta, WebServiceData> {
return vHttpMethod;
}
- private HttpClient getHttpClient(HttpClientContext context) {
+ private HttpClient getHttpClient(HttpClientContext context, String
serviceUrl) {
HttpClientManager.HttpClientBuilderFacade clientBuilder =
HttpClientManager.getInstance().createBuilder();
String login = resolve(meta.getHttpLogin());
+ String password =
Encr.decryptPasswordOptionallyEncrypted(resolve(meta.getHttpPassword()));
if (StringUtils.isNotBlank(login)) {
- clientBuilder.setCredentials(
- login,
Encr.decryptPasswordOptionallyEncrypted(resolve(meta.getHttpPassword())));
+ clientBuilder.setCredentials(login, password);
}
- int proxyPort = 0;
if (StringUtils.isNotBlank(meta.getProxyHost())) {
- proxyPort = Const.toInt(resolve(meta.getProxyPort()), 8080);
+ int proxyPort = Const.toInt(resolve(meta.getProxyPort()), 8080);
clientBuilder.setProxy(meta.getProxyHost(), proxyPort);
}
CloseableHttpClient httpClient = clientBuilder.build();
- if (proxyPort != 0) {
- // Preemptive authentication
- HttpHost target = new HttpHost(meta.getProxyHost(), proxyPort, "http");
- // Create AuthCache instance
+ if (StringUtils.isNotBlank(login)) {
+ HttpHost target = HttpHost.create(URI.create(serviceUrl));
AuthCache authCache = new BasicAuthCache();
- // Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
+ char[] passwordChars = password != null ? password.toCharArray() : new
char[0];
+ basicAuth.initPreemptive(new UsernamePasswordCredentials(login,
passwordChars));
authCache.put(target, basicAuth);
- // Add AuthCache to the execution context
context.setAuthCache(authCache);
}
return httpClient;
}
+ private static String readEntity(HttpEntity entity, String charset) throws
IOException {
+ try {
+ return EntityUtils.toString(entity, charset);
+ } catch (org.apache.hc.core5.http.ParseException e) {
+ throw new IOException("Unable to parse SOAP response body", e);
+ }
+ }
+
@Override
public boolean init() {
diff --git
a/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/wsdl/Wsdl.java
b/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/wsdl/Wsdl.java
index 6125e89175..ecbaeb198c 100644
---
a/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/wsdl/Wsdl.java
+++
b/plugins/transforms/webservices/src/main/java/org/apache/hop/pipeline/transforms/webservices/wsdl/Wsdl.java
@@ -18,10 +18,15 @@
package org.apache.hop.pipeline.transforms.webservices.wsdl;
import java.io.IOException;
+import java.io.InputStream;
import java.io.Serial;
import java.net.MalformedURLException;
import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
@@ -38,12 +43,10 @@ import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLLocator;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;
-import org.apache.hop.core.HttpProtocol;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopTransformException;
import org.apache.hop.core.logging.LogChannel;
import org.apache.hop.core.xml.XmlHandler;
-import org.apache.http.auth.AuthenticationException;
import org.w3c.dom.Document;
/** Wsdl abstraction. */
@@ -64,19 +67,15 @@ public final class Wsdl implements java.io.Serializable {
* @param serviceQName Name of the service in the WSDL, if null default to
first service in WSDL.
* @param portName The service port name, if null default to first port in
service.
*/
- public Wsdl(URI wsdlURI, QName serviceQName, String portName) throws
AuthenticationException {
+ public Wsdl(URI wsdlURI, QName serviceQName, String portName) {
this(wsdlURI, serviceQName, portName, null, null);
}
- public Wsdl(URI wsdlURI, QName serviceQName, String portName, String
username, String password)
- throws AuthenticationException {
+ public Wsdl(URI wsdlURI, QName serviceQName, String portName, String
username, String password) {
this.wsdlURI = wsdlURI;
try {
wsdlDefinition = parse(wsdlURI, username, password);
- } catch (AuthenticationException ae) {
- // throw this again since HopException is catching it
- throw ae;
} catch (WSDLException | HopException e) {
throw new RuntimeException(CONST_COULD_NOT_LOAD_WSDL_FILE +
e.getMessage(), e);
}
@@ -132,8 +131,7 @@ public final class Wsdl implements java.io.Serializable {
* @param serviceQName Name of the service in the WSDL.
* @param portName The service port name.
*/
- public Wsdl(WSDLLocator wsdlLocator, QName serviceQName, String portName)
- throws AuthenticationException {
+ public Wsdl(WSDLLocator wsdlLocator, QName serviceQName, String portName) {
this(wsdlLocator, serviceQName, portName, null, null);
}
@@ -142,15 +140,11 @@ public final class Wsdl implements java.io.Serializable {
QName serviceQName,
String portName,
String username,
- String password)
- throws AuthenticationException {
+ String password) {
// load and parse the WSDL
try {
wsdlDefinition = parse(wsdlLocator, username, password);
- } catch (AuthenticationException ae) {
- // throw it again or HopException will catch it
- throw ae;
} catch (WSDLException | HopException e) {
throw new RuntimeException(CONST_COULD_NOT_LOAD_WSDL_FILE +
e.getMessage(), e);
}
@@ -326,7 +320,7 @@ public final class Wsdl implements java.io.Serializable {
* @throws WSDLException on error.
*/
private Definition parse(WSDLLocator wsdlLocator, String username, String
password)
- throws WSDLException, HopException, AuthenticationException {
+ throws WSDLException, HopException {
WSDLReader wsdlReader = getReader();
try {
@@ -348,33 +342,39 @@ public final class Wsdl implements java.io.Serializable {
* @throws WSDLException on error.
*/
private Definition parse(URI wsdlURI, String username, String password)
- throws WSDLException, HopException, AuthenticationException {
+ throws WSDLException, HopException {
WSDLReader wsdlReader = getReader();
return readWsdl(wsdlReader, wsdlURI.toString(), username, password);
}
private Definition readWsdl(WSDLReader wsdlReader, String uri, String
username, String password)
- throws WSDLException, HopException, AuthenticationException {
+ throws WSDLException, HopException {
- try {
- HttpProtocol http = new HttpProtocol();
- Document doc =
- XmlHandler.loadXmlString(http.get(wsdlURI.toString(), username,
password), true, false);
+ try (InputStream wsdlStream = openWsdlStream(uri, username, password)) {
+ Document doc = XmlHandler.loadXmlFile(wsdlStream, uri, false, true);
if (doc != null) {
- return (wsdlReader.readWSDL(doc.getBaseURI(), doc));
+ return wsdlReader.readWSDL(uri, doc);
} else {
throw new HopException("Unable to get document.");
}
} catch (MalformedURLException mue) {
throw new HopException(mue);
- } catch (AuthenticationException ae) {
- // re-throw this. If not IOException seems to catch it
- throw ae;
} catch (IOException ioe) {
throw new HopException(ioe);
}
}
+ private InputStream openWsdlStream(String uri, String username, String
password)
+ throws IOException {
+ URLConnection connection = new URL(uri).openConnection();
+ if (username != null && !username.isEmpty()) {
+ String raw = username + ":" + (password == null ? "" : password);
+ String encoded =
Base64.getEncoder().encodeToString(raw.getBytes(StandardCharsets.UTF_8));
+ connection.setRequestProperty("Authorization", "Basic " + encoded);
+ }
+ return connection.getInputStream();
+ }
+
/**
* Returns this objects WSDL types.
*
diff --git
a/plugins/transforms/webservices/src/test/java/org/apache/hop/pipeline/transforms/webservices/WebServiceTest.java
b/plugins/transforms/webservices/src/test/java/org/apache/hop/pipeline/transforms/webservices/WebServiceTest.java
index 4d9e4131f0..61a58051bf 100644
---
a/plugins/transforms/webservices/src/test/java/org/apache/hop/pipeline/transforms/webservices/WebServiceTest.java
+++
b/plugins/transforms/webservices/src/test/java/org/apache/hop/pipeline/transforms/webservices/WebServiceTest.java
@@ -26,11 +26,11 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.net.URISyntaxException;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hop.core.logging.ILoggingObject;
import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
-import org.apache.http.Header;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.message.BasicHeader;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
diff --git
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/getxmldata/GetXmlData.java
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/getxmldata/GetXmlData.java
index 92266289f1..328297662f 100644
---
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/getxmldata/GetXmlData.java
+++
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/getxmldata/GetXmlData.java
@@ -28,6 +28,11 @@ import java.util.List;
import java.util.zip.GZIPInputStream;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
import org.apache.hop.core.Const;
import org.apache.hop.core.ResultFile;
import org.apache.hop.core.exception.HopException;
@@ -47,11 +52,6 @@ import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransform;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.xml.Dom4JUtil;
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
import org.dom4j.Element;
import org.dom4j.ElementHandler;
import org.dom4j.ElementPath;
@@ -189,7 +189,7 @@ public class GetXmlData extends
BaseTransform<GetXmlDataMeta, GetXmlDataData> {
HttpClient client =
HttpClientManager.getInstance().createDefaultClient();
HttpGet method = new HttpGet(stringXML);
method.addHeader("Accept-Encoding", "gzip");
- HttpResponse response = client.execute(method);
+ ClassicHttpResponse response = (ClassicHttpResponse)
client.execute(method);
Header contentEncoding = response.getFirstHeader("Content-Encoding");
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
diff --git
a/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java
b/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java
index c0c8383be7..6f1ec6654b 100644
--- a/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java
+++ b/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java
@@ -19,6 +19,7 @@
package org.apache.hop.ui.www.service;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.core5.http.ContentType;
import org.apache.hop.core.Const;
import org.apache.hop.core.NotePadMeta;
import org.apache.hop.core.logging.LogChannel;
@@ -40,7 +41,6 @@ import org.apache.hop.ui.core.widget.TextVar;
import org.apache.hop.ui.hopgui.HopGui;
import org.apache.hop.ui.hopgui.file.pipeline.HopPipelineFileType;
import org.apache.hop.www.service.WebService;
-import org.apache.http.entity.ContentType;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;