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 794594945a Issue #1871 (XML cleanup of the HTTP transform) (#6753)
794594945a is described below

commit 794594945a2c1fd2dbd44b4ec2f9905c95b02337
Author: Matt Casters <[email protected]>
AuthorDate: Thu Mar 12 08:19:20 2026 +0100

    Issue #1871 (XML cleanup of the HTTP transform) (#6753)
    
    * Issue #1871 (XML cleanup of the HTTP transform)
    
    * Issue #1871 (spotless)
---
 .../apache/hop/pipeline/transforms/http/Http.java  | 351 ++++++-----
 .../hop/pipeline/transforms/http/HttpData.java     |   2 +-
 .../hop/pipeline/transforms/http/HttpDialog.java   | 162 ++---
 .../hop/pipeline/transforms/http/HttpMeta.java     | 412 +++++++------
 .../hop/pipeline/transforms/http/HttpDataTest.java |  27 +-
 .../transforms/http/HttpMetaLoadSaveTest.java      |  91 ---
 .../hop/pipeline/transforms/http/HttpMetaTest.java | 373 +++---------
 .../hop/pipeline/transforms/http/HttpTest.java     | 670 ---------------------
 .../http/src/test/resources/transform.xml          |  61 ++
 9 files changed, 619 insertions(+), 1530 deletions(-)

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 e9cb74f7c3..0f961084eb 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
@@ -18,6 +18,7 @@
 package org.apache.hop.pipeline.transforms.http;
 
 import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URI;
 import java.net.UnknownHostException;
@@ -27,6 +28,7 @@ import org.apache.commons.lang.StringUtils;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.core.exception.HopValueException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.RowDataUtil;
 import org.apache.hop.core.row.RowMeta;
@@ -52,6 +54,7 @@ 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;
 
 /** Retrieves data from an Http endpoint */
@@ -74,18 +77,22 @@ public class Http extends BaseTransform<HttpMeta, HttpData> 
{
   private Object[] execHttp(IRowMeta rowMeta, Object[] row) throws 
HopException {
     if (first) {
       first = false;
-      data.argnrs = new int[meta.getArgumentField().length];
+      int nrQueryParameters = 
meta.getLookupParameters().getQueryParameters().size();
+      data.argNrs = new int[nrQueryParameters];
 
-      for (int i = 0; i < meta.getArgumentField().length; i++) {
-        data.argnrs[i] = rowMeta.indexOfValue(meta.getArgumentField()[i]);
-        if (data.argnrs[i] < 0) {
+      for (int i = 0; i < nrQueryParameters; i++) {
+        HttpMeta.QueryParameter queryParameter =
+            meta.getLookupParameters().getQueryParameters().get(i);
+
+        data.argNrs[i] = rowMeta.indexOfValue(queryParameter.getField());
+        if (data.argNrs[i] < 0) {
           logError(
               BaseMessages.getString(PKG, "HTTP.Log.ErrorFindingField")
-                  + meta.getArgumentField()[i]
+                  + queryParameter.getField()
                   + "]");
           throw new HopTransformException(
               BaseMessages.getString(
-                  PKG, "HTTP.Exception.CouldnotFindField", 
meta.getArgumentField()[i]));
+                  PKG, "HTTP.Exception.CouldnotFindField", 
queryParameter.getField()));
         }
       }
     }
@@ -95,26 +102,7 @@ public class Http extends BaseTransform<HttpMeta, HttpData> 
{
 
   @VisibleForTesting
   Object[] callHttpService(IRowMeta rowMeta, Object[] rowData) throws 
HopException {
-    HttpClientManager.HttpClientBuilderFacade clientBuilder =
-        HttpClientManager.getInstance().createBuilder();
-
-    if (data.realConnectionTimeout > -1) {
-      clientBuilder.setConnectionTimeout(data.realConnectionTimeout);
-    }
-    if (data.realSocketTimeout > -1) {
-      clientBuilder.setSocketTimeout(data.realSocketTimeout);
-    }
-    if (StringUtils.isNotBlank(data.realHttpLogin)) {
-      clientBuilder.setCredentials(data.realHttpLogin, data.realHttpPassword);
-    }
-    if (StringUtils.isNotBlank(data.realProxyHost)) {
-      clientBuilder.setProxy(data.realProxyHost, data.realProxyPort);
-    }
-    if (meta.isIgnoreSsl()) {
-      clientBuilder.ignoreSsl(true);
-    }
-
-    CloseableHttpClient httpClient = clientBuilder.build();
+    CloseableHttpClient httpClient = createClientBuilder().build();
 
     // Prepare Http get
     URI uri = null;
@@ -125,21 +113,7 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
       HttpGet method = new HttpGet(uri);
 
       // Add Custom Http headers
-      if (data.useHeaderParameters) {
-        for (int i = 0; i < data.headerParametersNrs.length; i++) {
-          method.addHeader(
-              data.headerParameters[i].getName(),
-              data.inputRowMeta.getString(rowData, 
data.headerParametersNrs[i]));
-          if (isDebug()) {
-            logDebug(
-                BaseMessages.getString(
-                    PKG,
-                    "HTTPDialog.Log.HeaderValue",
-                    data.headerParameters[i].getName(),
-                    data.inputRowMeta.getString(rowData, 
data.headerParametersNrs[i])));
-          }
-        }
-      }
+      addHeadersToMethod(rowData, method);
 
       Object[] newRow = null;
       if (rowData != null) {
@@ -157,17 +131,7 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
           target = new HttpHost(data.realProxyHost, data.realProxyPort, 
"http");
         }
 
-        // 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);
-
-        httpResponse = httpClient.execute(target, method, localContext);
+        httpResponse = httpClient.execute(target, method, 
buildtHttpClientContext(target));
 
         // calculate the responseTime
         long responseTime = System.currentTimeMillis() - startTime;
@@ -181,66 +145,27 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
         }
 
         String body;
-        switch (statusCode) {
-          case HttpURLConnection.HTTP_UNAUTHORIZED:
-            throw new HopTransformException(
-                BaseMessages.getString(PKG, "HTTP.Exception.Authentication", 
data.realUrl));
-          case -1:
-            throw new HopTransformException(
-                BaseMessages.getString(PKG, 
"HTTP.Exception.IllegalStatusCode", data.realUrl));
-          case HttpURLConnection.HTTP_NO_CONTENT:
-            body = "";
-            break;
-          default:
-            HttpEntity entity = httpResponse.getEntity();
-            if (entity != null) {
-              body =
-                  StringUtils.isEmpty(meta.getEncoding())
-                      ? EntityUtils.toString(entity)
-                      : EntityUtils.toString(entity, meta.getEncoding());
-            } else {
-              body = "";
-            }
-            break;
-        }
+        body = handResponse(statusCode, httpResponse);
 
-        Header[] headers = searchForHeaders(httpResponse);
-
-        JSONObject json = new JSONObject();
-        for (Header header : headers) {
-          Object previousValue = json.get(header.getName());
-          if (previousValue == null) {
-            json.put(header.getName(), header.getValue());
-          } else if (previousValue instanceof List) {
-            List<String> list = (List<String>) previousValue;
-            list.add(header.getValue());
-          } else {
-            ArrayList<String> list = new ArrayList<>();
-            list.add((String) previousValue);
-            list.add(header.getValue());
-            json.put(header.getName(), list);
-          }
-        }
-        String headerString = json.toJSONString();
+        String headerString = 
extractHeaderString(searchForHeaders(httpResponse));
 
         int returnFieldsOffset = rowMeta.size();
-        if (!Utils.isEmpty(meta.getFieldName())) {
+        if (!Utils.isEmpty(meta.getResultFields().getFieldName())) {
           newRow = RowDataUtil.addValueData(newRow, returnFieldsOffset, body);
           returnFieldsOffset++;
         }
 
-        if (!Utils.isEmpty(meta.getResultCodeFieldName())) {
+        if (!Utils.isEmpty(meta.getResultFields().getResultCodeFieldName())) {
           newRow = RowDataUtil.addValueData(newRow, returnFieldsOffset, (long) 
statusCode);
           returnFieldsOffset++;
         }
-        if (!Utils.isEmpty(meta.getResponseTimeFieldName())) {
+        if (!Utils.isEmpty(meta.getResultFields().getResponseTimeFieldName())) 
{
           newRow = RowDataUtil.addValueData(newRow, returnFieldsOffset, 
responseTime);
           returnFieldsOffset++;
         }
-        if (!Utils.isEmpty(meta.getResponseHeaderFieldName())) {
+        if 
(!Utils.isEmpty(meta.getResultFields().getResponseHeaderFieldName())) {
           newRow = RowDataUtil.addValueData(newRow, returnFieldsOffset, 
headerString);
         }
-
       } finally {
         if (httpResponse != null) {
           httpResponse.close();
@@ -257,6 +182,106 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
     }
   }
 
+  private static String extractHeaderString(Header[] headers) {
+    JSONObject json = new JSONObject();
+    for (Header header : headers) {
+      Object previousValue = json.get(header.getName());
+      if (previousValue == null) {
+        json.put(header.getName(), header.getValue());
+      } else if (previousValue instanceof List) {
+        List<String> list = (List<String>) previousValue;
+        list.add(header.getValue());
+      } else {
+        ArrayList<String> list = new ArrayList<>();
+        list.add((String) previousValue);
+        list.add(header.getValue());
+        json.put(header.getName(), list);
+      }
+    }
+    return json.toJSONString();
+  }
+
+  private String handResponse(int statusCode, CloseableHttpResponse 
httpResponse)
+      throws HopTransformException, IOException {
+    String body;
+    switch (statusCode) {
+      case HttpURLConnection.HTTP_UNAUTHORIZED:
+        throw new HopTransformException(
+            BaseMessages.getString(PKG, "HTTP.Exception.Authentication", 
data.realUrl));
+      case -1:
+        throw new HopTransformException(
+            BaseMessages.getString(PKG, "HTTP.Exception.IllegalStatusCode", 
data.realUrl));
+      case HttpURLConnection.HTTP_NO_CONTENT:
+        body = "";
+        break;
+      default:
+        HttpEntity entity = httpResponse.getEntity();
+        if (entity != null) {
+          body =
+              StringUtils.isEmpty(meta.getEncoding())
+                  ? EntityUtils.toString(entity)
+                  : EntityUtils.toString(entity, meta.getEncoding());
+        } else {
+          body = "";
+        }
+        break;
+    }
+    return body;
+  }
+
+  private static @NotNull HttpClientContext buildtHttpClientContext(HttpHost 
target) {
+    // 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);
+    return localContext;
+  }
+
+  private void addHeadersToMethod(Object[] rowData, HttpGet method) throws 
HopValueException {
+    if (data.useHeaderParameters) {
+      for (int i = 0; i < data.headerParametersNrs.length; i++) {
+        method.addHeader(
+            data.headerParameters[i].getName(),
+            data.inputRowMeta.getString(rowData, data.headerParametersNrs[i]));
+        if (isDebug()) {
+          logDebug(
+              BaseMessages.getString(
+                  PKG,
+                  "HTTPDialog.Log.HeaderValue",
+                  data.headerParameters[i].getName(),
+                  data.inputRowMeta.getString(rowData, 
data.headerParametersNrs[i])));
+        }
+      }
+    }
+  }
+
+  private HttpClientManager.HttpClientBuilderFacade createClientBuilder() {
+    HttpClientManager.HttpClientBuilderFacade clientBuilder =
+        HttpClientManager.getInstance().createBuilder();
+
+    if (data.realConnectionTimeout > -1) {
+      clientBuilder.setConnectionTimeout(data.realConnectionTimeout);
+    }
+    if (data.realSocketTimeout > -1) {
+      clientBuilder.setSocketTimeout(data.realSocketTimeout);
+    }
+    if (StringUtils.isNotBlank(data.realHttpLogin)) {
+      clientBuilder.setCredentials(data.realHttpLogin, data.realHttpPassword);
+    }
+    if (StringUtils.isNotBlank(data.realProxyHost)) {
+      clientBuilder.setProxy(data.realProxyHost, data.realProxyPort);
+    }
+    if (meta.isIgnoreSsl()) {
+      clientBuilder.ignoreSsl(true);
+    }
+    return clientBuilder;
+  }
+
   private URIBuilder constructUrlBuilder(IRowMeta outputRowMeta, Object[] row) 
throws HopException {
     URIBuilder uriBuilder;
     try {
@@ -272,9 +297,10 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
 
       uriBuilder = new URIBuilder(baseUrl); // the base URL with variable 
substitution
 
-      for (int i = 0; i < data.argnrs.length; i++) {
-        String key = meta.getArgumentParameter()[i];
-        String value = outputRowMeta.getString(row, data.argnrs[i]);
+      for (int i = 0; i < data.argNrs.length; i++) {
+        HttpMeta.QueryParameter parameter = 
meta.getLookupParameters().getQueryParameters().get(i);
+        String key = parameter.getParameter();
+        String value = outputRowMeta.getString(row, data.argNrs[i]);
         if (!key.isEmpty()) {
           uriBuilder.addParameter(key, Const.NVL(value, ""));
         }
@@ -304,61 +330,8 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
     }
 
     if (first) {
-      data.inputRowMeta = data.withoutPreviousTransforms ? new RowMeta() : 
getInputRowMeta();
-      data.outputRowMeta = data.inputRowMeta.clone();
-      meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, 
metadataProvider);
-
-      if (meta.isUrlInField()) {
-        if (Utils.isEmpty(meta.getUrlField())) {
-          logError(BaseMessages.getString(PKG, "HTTP.Log.NoField"));
-          throw new HopException(BaseMessages.getString(PKG, 
"HTTP.Log.NoField"));
-        }
-
-        // cache the position of the field
-        if (data.indexOfUrlField < 0) {
-          String realUrlFieldName = resolve(meta.getUrlField());
-          data.indexOfUrlField = 
data.inputRowMeta.indexOfValue(realUrlFieldName);
-          if (data.indexOfUrlField < 0) {
-            // The field is unreachable !
-            logError(BaseMessages.getString(PKG, "HTTP.Log.ErrorFindingField", 
realUrlFieldName));
-            throw new HopException(
-                BaseMessages.getString(
-                    PKG, CONST_HTTP_EXCEPTION_ERROR_FINDING_FIELD, 
realUrlFieldName));
-          }
-        }
-      } else {
-        data.realUrl = resolve(meta.getUrl());
-      }
-
-      // check for headers
-      int nrHeaders = meta.getHeaderField().length;
-      if (nrHeaders > 0) {
-        data.useHeaderParameters = true;
-      }
-
-      data.headerParametersNrs = new int[nrHeaders];
-      data.headerParameters = new NameValuePair[nrHeaders];
-
-      // get the headers
-      for (int i = 0; i < nrHeaders; i++) {
-        int fieldIndex = 
data.inputRowMeta.indexOfValue(meta.getHeaderField()[i]);
-        if (fieldIndex < 0) {
-          logError(
-              BaseMessages.getString(PKG, 
CONST_HTTP_EXCEPTION_ERROR_FINDING_FIELD)
-                  + meta.getHeaderField()[i]
-                  + "]");
-          throw new HopTransformException(
-              BaseMessages.getString(
-                  PKG, CONST_HTTP_EXCEPTION_ERROR_FINDING_FIELD, 
meta.getHeaderField()[i]));
-        }
-
-        data.headerParametersNrs[i] = fieldIndex;
-        data.headerParameters[i] =
-            new BasicNameValuePair(
-                resolve(meta.getHeaderParameter()[i]),
-                data.outputRowMeta.getString(r, data.headerParametersNrs[i]));
-      }
-    } // end if first
+      firstProcessRow(r);
+    }
 
     try {
       Object[] outputRowData = execHttp(data.inputRowMeta, r); // add new 
values to the row
@@ -368,11 +341,9 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
         logDetailed(BaseMessages.getString(PKG, "HTTP.LineNumber") + 
getLinesRead());
       }
     } catch (HopException e) {
-      boolean sendToErrorRow = false;
-      String errorMessage = null;
+      String errorMessage;
 
       if (getTransformMeta().isDoingErrorHandling()) {
-        sendToErrorRow = true;
         errorMessage = e.toString();
       } else {
         logError(BaseMessages.getString(PKG, "HTTP.ErrorInTransformRunning") + 
e.getMessage());
@@ -381,15 +352,71 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
         setOutputDone(); // signal end to receiver(s)
         return false;
       }
-      if (sendToErrorRow) {
-        // Simply add this row to the error row
-        putError(data.inputRowMeta, r, 1, errorMessage, null, "HTTP001");
-      }
+      // Simply add this row to the error row
+      putError(data.inputRowMeta, r, 1, errorMessage, null, "HTTP001");
     }
 
     return true;
   }
 
+  private void firstProcessRow(Object[] r) throws HopException {
+    data.inputRowMeta = data.withoutPreviousTransforms ? new RowMeta() : 
getInputRowMeta();
+    data.outputRowMeta = data.inputRowMeta.clone();
+    meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, 
metadataProvider);
+
+    if (meta.isUrlInField()) {
+      if (Utils.isEmpty(meta.getUrlField())) {
+        logError(BaseMessages.getString(PKG, "HTTP.Log.NoField"));
+        throw new HopException(BaseMessages.getString(PKG, 
"HTTP.Log.NoField"));
+      }
+
+      // cache the position of the field
+      if (data.indexOfUrlField < 0) {
+        String realUrlFieldName = resolve(meta.getUrlField());
+        data.indexOfUrlField = 
data.inputRowMeta.indexOfValue(realUrlFieldName);
+        if (data.indexOfUrlField < 0) {
+          // The field is unreachable !
+          logError(BaseMessages.getString(PKG, "HTTP.Log.ErrorFindingField", 
realUrlFieldName));
+          throw new HopException(
+              BaseMessages.getString(
+                  PKG, CONST_HTTP_EXCEPTION_ERROR_FINDING_FIELD, 
realUrlFieldName));
+        }
+      }
+    } else {
+      data.realUrl = resolve(meta.getUrl());
+    }
+
+    // check for headers
+    int nrHeaders = meta.getLookupParameters().getHeaders().size();
+    if (nrHeaders > 0) {
+      data.useHeaderParameters = true;
+    }
+
+    data.headerParametersNrs = new int[nrHeaders];
+    data.headerParameters = new NameValuePair[nrHeaders];
+
+    // get the headers
+    for (int i = 0; i < nrHeaders; i++) {
+      HttpMeta.HeaderParameter headerParameter = 
meta.getLookupParameters().getHeaders().get(i);
+      int fieldIndex = 
data.inputRowMeta.indexOfValue(headerParameter.getField());
+      if (fieldIndex < 0) {
+        logError(
+            BaseMessages.getString(PKG, 
CONST_HTTP_EXCEPTION_ERROR_FINDING_FIELD)
+                + headerParameter.getField()
+                + "]");
+        throw new HopTransformException(
+            BaseMessages.getString(
+                PKG, CONST_HTTP_EXCEPTION_ERROR_FINDING_FIELD, 
headerParameter.getField()));
+      }
+
+      data.headerParametersNrs[i] = fieldIndex;
+      data.headerParameters[i] =
+          new BasicNameValuePair(
+              resolve(headerParameter.getParameter()),
+              data.outputRowMeta.getString(r, data.headerParametersNrs[i]));
+    }
+  }
+
   @Override
   public boolean init() {
 
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 447f4dd1c9..bb2802ae43 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
@@ -24,7 +24,7 @@ import org.apache.http.NameValuePair;
 
 @SuppressWarnings("java:S1104")
 public class HttpData extends BaseTransformData implements ITransformData {
-  public int[] argnrs;
+  public int[] argNrs;
   public IRowMeta outputRowMeta;
   public IRowMeta inputRowMeta;
   public int indexOfUrlField;
diff --git 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
index 71ada4b8de..9382762663 100644
--- 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
+++ 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
@@ -49,7 +49,6 @@ import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Cursor;
-import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
 import org.eclipse.swt.layout.FormLayout;
@@ -98,8 +97,8 @@ public class HttpDialog extends BaseTransformDialog {
 
   private final HttpMeta input;
 
-  private ColumnInfo[] colinf;
-  private ColumnInfo[] colinfHeaders;
+  private ColumnInfo[] argumentColumns;
+  private ColumnInfo[] headerColumns;
 
   private final List<String> inputFields = new ArrayList<>();
 
@@ -148,11 +147,11 @@ public class HttpDialog extends BaseTransformDialog {
     // START Settings GROUP
 
     Group gSettings = setupSettingGroup(wGeneralComp);
-    Control lastControl = setupUrlLine(lsMod, null, gSettings);
+    Control lastControl = setupUrlLine(lsMod, gSettings);
     lastControl = setupUrlInFieldLine(lastControl, gSettings);
     lastControl = setupIgnoreSslLine(lastControl, gSettings);
     lastControl = setupUrlFieldNameLine(lsMod, lastControl, gSettings);
-    lastControl = setupEncodingLine(lsMod, lastControl, gSettings);
+    setupEncodingLine(lsMod, lastControl, gSettings);
     setupConnectionTimeoutLine(lsMod, gSettings);
     setupSocketTimeoutLine(lsMod, gSettings);
     setupCloseWaitTimeLine(lsMod, gSettings);
@@ -213,7 +212,6 @@ public class HttpDialog extends BaseTransformDialog {
 
     // END Http Proxy GROUP
     // ////////////////////////
-    lastControl = gProxy;
 
     FormData fdGeneralComp = new FormData();
     fdGeneralComp.left = new FormAttachment(0, 0);
@@ -242,7 +240,7 @@ public class HttpDialog extends BaseTransformDialog {
     wAdditionalComp.setLayout(addLayout);
     PropsUi.setLook(wAdditionalComp);
 
-    setupParamBlock(lsMod, lastControl, wAdditionalComp);
+    setupParamBlock(lsMod, wAdditionalComp);
     setupHeadBlock(lsMod, wAdditionalComp);
 
     //
@@ -285,15 +283,6 @@ public class HttpDialog extends BaseTransformDialog {
     fdTabFolder.bottom = new FormAttachment(wOk, -margin);
     wTabFolder.setLayoutData(fdTabFolder);
 
-    lsResize =
-        event -> {
-          Point size = shell.getSize();
-          wFields.setSize(size.x - 10, size.y - 50);
-          wFields.table.setSize(size.x - 10, size.y - 50);
-          wFields.redraw();
-        };
-    shell.addListener(SWT.Resize, lsResize);
-
     getData();
     wTabFolder.setSelection(0);
     activeUrlInfield();
@@ -314,9 +303,9 @@ public class HttpDialog extends BaseTransformDialog {
     fdlHeaders.top = new FormAttachment(wFields, margin);
     wlHeaders.setLayoutData(fdlHeaders);
 
-    final int HeadersRows = input.getHeaderParameter().length;
+    final int nrHeadersRows = input.getLookupParameters().getHeaders().size();
 
-    colinfHeaders =
+    headerColumns =
         new ColumnInfo[] {
           new ColumnInfo(
               BaseMessages.getString(PKG, "HTTPDialog.ColumnInfo.Field"),
@@ -328,14 +317,14 @@ public class HttpDialog extends BaseTransformDialog {
               ColumnInfo.COLUMN_TYPE_TEXT,
               false),
         };
-    colinfHeaders[1].setUsingVariables(true);
+    headerColumns[1].setUsingVariables(true);
     wHeaders =
         new TableView(
             variables,
             wAdditionalComp,
             SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI,
-            colinfHeaders,
-            HeadersRows,
+            headerColumns,
+            nrHeadersRows,
             lsMod,
             props);
 
@@ -355,8 +344,7 @@ public class HttpDialog extends BaseTransformDialog {
     wHeaders.setLayoutData(fdHeaders);
   }
 
-  private void setupParamBlock(
-      ModifyListener lsMod, Control lastControl, Composite wAdditionalComp) {
+  private void setupParamBlock(ModifyListener lsMod, Composite 
wAdditionalComp) {
     int margin = PropsUi.getMargin();
     Label wlFields = new Label(wAdditionalComp, SWT.NONE);
     wlFields.setText(BaseMessages.getString(PKG, 
"HTTPDialog.Parameters.Label"));
@@ -374,9 +362,8 @@ public class HttpDialog extends BaseTransformDialog {
     wGet.setLayoutData(fdGet);
     wGet.addListener(SWT.Selection, e -> get());
 
-    final int FieldsRows = input.getArgumentField().length;
-
-    colinf =
+    final int nrFieldRows = 
input.getLookupParameters().getQueryParameters().size();
+    argumentColumns =
         new ColumnInfo[] {
           new ColumnInfo(
               BaseMessages.getString(PKG, "HTTPDialog.ColumnInfo.Name"),
@@ -394,8 +381,8 @@ public class HttpDialog extends BaseTransformDialog {
             variables,
             wAdditionalComp,
             SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI,
-            colinf,
-            FieldsRows,
+            argumentColumns,
+            nrFieldRows,
             lsMod,
             props);
 
@@ -403,7 +390,7 @@ public class HttpDialog extends BaseTransformDialog {
     fdFields.left = new FormAttachment(0, 0);
     fdFields.top = new FormAttachment(wlFields, margin);
     fdFields.right = new FormAttachment(wGet, -margin);
-    fdFields.bottom = new FormAttachment(wlFields, 200);
+    fdFields.bottom = new FormAttachment(45, 0);
     wFields.setLayoutData(fdFields);
   }
 
@@ -695,7 +682,7 @@ public class HttpDialog extends BaseTransformDialog {
     wConnectionTimeOut.setLayoutData(fdConnectionTimeOut);
   }
 
-  private Control setupEncodingLine(ModifyListener lsMod, Control lastControl, 
Group gSettings) {
+  private void setupEncodingLine(ModifyListener lsMod, Control lastControl, 
Group gSettings) {
     // Encoding
     //
     int margin = PropsUi.getMargin();
@@ -732,8 +719,6 @@ public class HttpDialog extends BaseTransformDialog {
             busy.dispose();
           }
         });
-    lastControl = wEncoding;
-    return lastControl;
   }
 
   private Control setupUrlFieldNameLine(
@@ -845,7 +830,7 @@ public class HttpDialog extends BaseTransformDialog {
     return lastControl;
   }
 
-  private Control setupUrlLine(ModifyListener lsMod, Control previousControl, 
Group gSettings) {
+  private Control setupUrlLine(ModifyListener lsMod, Group gSettings) {
     // The URL to use
     //
     int margin = PropsUi.getMargin();
@@ -905,8 +890,8 @@ public class HttpDialog extends BaseTransformDialog {
     // Something was changed in the row.
     //
     String[] fieldNames = ConstUi.sortFieldNames(inputFields);
-    colinf[0].setComboValues(fieldNames);
-    colinfHeaders[0].setComboValues(fieldNames);
+    argumentColumns[0].setComboValues(fieldNames);
+    headerColumns[0].setComboValues(fieldNames);
   }
 
   private void activeUrlInfield() {
@@ -922,24 +907,18 @@ public class HttpDialog extends BaseTransformDialog {
       logDebug(BaseMessages.getString(PKG, "HTTPDialog.Log.GettingKeyInfo"));
     }
 
-    if (input.getArgumentField() != null) {
-      for (int i = 0; i < input.getArgumentField().length; i++) {
-        TableItem item = wFields.table.getItem(i);
-        item.setText(1, Const.NVL(input.getArgumentField()[i], ""));
-        item.setText(2, Const.NVL(input.getArgumentParameter()[i], ""));
-      }
+    for (int i = 0; i < 
input.getLookupParameters().getQueryParameters().size(); i++) {
+      HttpMeta.QueryParameter param = 
input.getLookupParameters().getQueryParameters().get(i);
+      TableItem item = wFields.table.getItem(i);
+      item.setText(1, Const.NVL(param.getField(), ""));
+      item.setText(2, Const.NVL(param.getParameter(), ""));
     }
 
-    if (input.getHeaderField() != null) {
-      for (int i = 0; i < input.getHeaderField().length; i++) {
-        TableItem item = wHeaders.table.getItem(i);
-        if (input.getHeaderField()[i] != null) {
-          item.setText(1, input.getHeaderField()[i]);
-        }
-        if (input.getHeaderParameter()[i] != null) {
-          item.setText(2, input.getHeaderParameter()[i]);
-        }
-      }
+    for (int i = 0; i < input.getLookupParameters().getHeaders().size(); i++) {
+      HttpMeta.HeaderParameter param = 
input.getLookupParameters().getHeaders().get(i);
+      TableItem item = wHeaders.table.getItem(i);
+      item.setText(1, Const.NVL(param.getField(), ""));
+      item.setText(2, Const.NVL(param.getParameter(), ""));
     }
     wSocketTimeOut.setText(Const.NVL(input.getSocketTimeout(), ""));
     wConnectionTimeOut.setText(Const.NVL(input.getConnectionTimeout(), ""));
@@ -951,28 +930,14 @@ public class HttpDialog extends BaseTransformDialog {
     wUrlField.setText(Const.NVL(input.getUrlField(), ""));
     wEncoding.setText(Const.NVL(input.getEncoding(), ""));
 
-    wResult.setText(Const.NVL(input.getFieldName(), ""));
-    if (input.getHttpLogin() != null) {
-      wHttpLogin.setText(input.getHttpLogin());
-    }
-    if (input.getHttpPassword() != null) {
-      wHttpPassword.setText(input.getHttpPassword());
-    }
-    if (input.getProxyHost() != null) {
-      wProxyHost.setText(input.getProxyHost());
-    }
-    if (input.getProxyPort() != null) {
-      wProxyPort.setText(input.getProxyPort());
-    }
-    if (input.getResultCodeFieldName() != null) {
-      wResultCode.setText(input.getResultCodeFieldName());
-    }
-    if (input.getResponseTimeFieldName() != null) {
-      wResponseTime.setText(input.getResponseTimeFieldName());
-    }
-    if (input.getResponseHeaderFieldName() != null) {
-      wResponseHeader.setText(input.getResponseHeaderFieldName());
-    }
+    wHttpLogin.setText(Const.NVL(input.getHttpLogin(), ""));
+    wHttpPassword.setText(Const.NVL(input.getHttpPassword(), ""));
+    wProxyHost.setText(Const.NVL(input.getProxyHost(), ""));
+    wProxyPort.setText(Const.NVL(input.getProxyPort(), ""));
+    wResult.setText(Const.NVL(input.getResultFields().getFieldName(), ""));
+    
wResultCode.setText(Const.NVL(input.getResultFields().getResultCodeFieldName(), 
""));
+    
wResponseTime.setText(Const.NVL(input.getResultFields().getResponseTimeFieldName(),
 ""));
+    
wResponseHeader.setText(Const.NVL(input.getResultFields().getResponseHeaderFieldName(),
 ""));
 
     wFields.setRowNums();
     wFields.optWidth(true);
@@ -991,51 +956,48 @@ public class HttpDialog extends BaseTransformDialog {
       return;
     }
 
-    int nrargs = wFields.nrNonEmpty();
-    int nrheaders = wHeaders.nrNonEmpty();
+    getInfo(input);
 
-    input.allocate(nrargs, nrheaders);
+    transformName = wTransformName.getText(); // return value
 
-    if (isDebug()) {
-      logDebug(
-          BaseMessages.getString(PKG, "HTTPDialog.Log.FoundArguments", 
String.valueOf(nrargs)));
-    }
-    for (int i = 0; i < nrargs; i++) {
-      TableItem item = wFields.getNonEmpty(i);
-      input.getArgumentField()[i] = item.getText(1);
-      input.getArgumentParameter()[i] = item.getText(2);
-    }
+    dispose();
+  }
 
-    if (log.isDebug()) {
-      logDebug(
-          BaseMessages.getString(PKG, "HTTPDialog.Log.FoundHeaders", 
String.valueOf(nrheaders)));
+  private void getInfo(HttpMeta input) {
+
+    input.getLookupParameters().getQueryParameters().clear();
+    for (TableItem item : wFields.getNonEmptyItems()) {
+      String field = item.getText(1);
+      String parameter = item.getText(2);
+      input
+          .getLookupParameters()
+          .getQueryParameters()
+          .add(new HttpMeta.QueryParameter(field, parameter));
     }
-    for (int i = 0; i < nrheaders; i++) {
-      TableItem item = wHeaders.getNonEmpty(i);
-      input.getHeaderField()[i] = item.getText(1);
-      input.getHeaderParameter()[i] = item.getText(2);
+
+    input.getLookupParameters().getHeaders().clear();
+    for (TableItem item : wHeaders.getNonEmptyItems()) {
+      String field = item.getText(1);
+      String parameter = item.getText(2);
+      input.getLookupParameters().getHeaders().add(new 
HttpMeta.HeaderParameter(field, parameter));
     }
 
     input.setUrl(wUrl.getText());
     input.setUrlField(wUrlField.getText());
     input.setUrlInField(wUrlInField.getSelection());
     input.setIgnoreSsl(wIgnoreSsl.getSelection());
-    input.setFieldName(wResult.getText());
     input.setEncoding(wEncoding.getText());
     input.setHttpLogin(wHttpLogin.getText());
     input.setHttpPassword(wHttpPassword.getText());
     input.setProxyHost(wProxyHost.getText());
     input.setProxyPort(wProxyPort.getText());
-    input.setResultCodeFieldName(wResultCode.getText());
-    input.setResponseTimeFieldName(wResponseTime.getText());
-    input.setResponseHeaderFieldName(wResponseHeader.getText());
+    input.getResultFields().setFieldName(wResult.getText());
+    input.getResultFields().setResultCodeFieldName(wResultCode.getText());
+    input.getResultFields().setResponseTimeFieldName(wResponseTime.getText());
+    
input.getResultFields().setResponseHeaderFieldName(wResponseHeader.getText());
     input.setSocketTimeout(wSocketTimeOut.getText());
     input.setConnectionTimeout(wConnectionTimeOut.getText());
     input.setCloseIdleConnectionsTime(wCloseIdleConnectionsTime.getText());
-
-    transformName = wTransformName.getText(); // return value
-
-    dispose();
   }
 
   private void get() {
diff --git 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
index bb1f9ce09c..51a8b88ff8 100644
--- 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
+++ 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
@@ -17,29 +17,25 @@
 
 package org.apache.hop.pipeline.transforms.http;
 
+import java.util.ArrayList;
 import java.util.List;
 import lombok.Getter;
 import lombok.Setter;
 import org.apache.hop.core.CheckResult;
-import org.apache.hop.core.Const;
 import org.apache.hop.core.ICheckResult;
 import org.apache.hop.core.annotations.Transform;
-import org.apache.hop.core.encryption.Encr;
-import org.apache.hop.core.exception.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.value.ValueMetaInteger;
 import org.apache.hop.core.row.value.ValueMetaString;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.BaseTransformMeta;
 import org.apache.hop.pipeline.transform.TransformMeta;
-import org.w3c.dom.Node;
 
 @Transform(
     id = "Http",
@@ -68,107 +64,126 @@ public class HttpMeta extends BaseTransformMeta<Http, 
HttpData> {
   public static final String CONST_SPACES = "      ";
   public static final String CONST_PARAMETER = "parameter";
 
+  @HopMetadataProperty(
+      key = "lookup",
+      injectionKey = "LOOKUP",
+      injectionKeyDescription = "HttpMeta.Injection.Lookup")
+  private LookupParameters lookupParameters;
+
+  @HopMetadataProperty(
+      key = "socketTimeout",
+      injectionKey = "SOCKET_TIMEOUT",
+      injectionKeyDescription = "HttpMeta.Injection.SOCKET_TIMEOUT")
   private String socketTimeout;
+
+  @HopMetadataProperty(
+      key = "connectionTimeout",
+      injectionKey = "CONNECTION_TIMEOUT",
+      injectionKeyDescription = "HttpMeta.Injection.CONNECTION_TIMEOUT")
   private String connectionTimeout;
+
+  @HopMetadataProperty(
+      key = "closeIdleConnectionsTime",
+      injectionKey = "CLOSE_IDLE_CONNECTIONS_TIME",
+      injectionKeyDescription = 
"HttpMeta.Injection.CLOSE_IDLE_CONNECTIONS_TIME")
   private String closeIdleConnectionsTime;
 
   /** URL / service to be called */
+  @HopMetadataProperty(
+      key = "url",
+      injectionKey = "URL",
+      injectionKeyDescription = "HttpMeta.Injection.URL")
   private String url;
 
-  /** function arguments : fieldname */
-  private String[] argumentField;
-
-  /** IN / OUT / INOUT */
-  private String[] argumentParameter;
-
-  /** function result: new value name */
-  private String fieldName;
-
   /** The encoding to use for retrieval of the data */
+  @HopMetadataProperty(
+      key = "encoding",
+      injectionKey = "ENCODING",
+      injectionKeyDescription = "HttpMeta.Injection.ENCODING")
   private String encoding;
 
+  @HopMetadataProperty(
+      key = "urlInField",
+      injectionKey = "URL_IN_FIELD",
+      injectionKeyDescription = "HttpMeta.Injection.URL_IN_FIELD")
   private boolean urlInField;
 
+  @HopMetadataProperty(
+      key = "ignoreSsl",
+      injectionKey = "IGNORE_SSL",
+      injectionKeyDescription = "HttpMeta.Injection.IGNORE_SSL")
   private boolean ignoreSsl;
 
+  @HopMetadataProperty(
+      key = "urlField",
+      injectionKey = "URL_FIELD",
+      injectionKeyDescription = "HttpMeta.Injection.URL_FIELD")
   private String urlField;
 
+  @HopMetadataProperty(
+      key = "proxyHost",
+      injectionKey = "PROXY_HOST",
+      injectionKeyDescription = "HttpMeta.Injection.PROXY_HOST")
   private String proxyHost;
 
+  @HopMetadataProperty(
+      key = "proxyPort",
+      injectionKey = "PROXY_PORT",
+      injectionKeyDescription = "HttpMeta.Injection.PROXY_PORT")
   private String proxyPort;
 
+  @HopMetadataProperty(
+      key = "httpLogin",
+      injectionKey = "HTTP_LOGIN",
+      injectionKeyDescription = "HttpMeta.Injection.HTTP_LOGIN")
   private String httpLogin;
 
+  @HopMetadataProperty(
+      key = "httpPassword",
+      injectionKey = "HTTP_PASSWORD",
+      injectionKeyDescription = "HttpMeta.Injection.HTTP_PASSWORD",
+      password = true)
   private String httpPassword;
 
-  private String resultCodeFieldName;
-  private String responseTimeFieldName;
-  private String responseHeaderFieldName;
-
-  private String[] headerParameter;
-  private String[] headerField;
+  @HopMetadataProperty(
+      key = "result",
+      injectionKey = "RESULT",
+      injectionKeyDescription = "HttpMeta.Injection.RESULT")
+  private ResultFields resultFields;
 
   public HttpMeta() {
     super(); // allocate BaseTransformMeta
+    this.lookupParameters = new LookupParameters();
+    this.resultFields = new ResultFields();
+    this.socketTimeout = String.valueOf(DEFAULT_SOCKET_TIMEOUT);
+    this.connectionTimeout = String.valueOf(DEFAULT_CONNECTION_TIMEOUT);
+    this.closeIdleConnectionsTime = 
String.valueOf(DEFAULT_CLOSE_CONNECTIONS_TIME);
+    this.resultFields.fieldName = CONST_RESULT;
+
+    this.encoding = "UTF-8";
   }
 
-  @Override
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    readData(transformNode);
-  }
-
-  public void allocate(int nrargs, int nrqueryparams) {
-    argumentField = new String[nrargs];
-    argumentParameter = new String[nrargs];
-    headerField = new String[nrqueryparams];
-    headerParameter = new String[nrqueryparams];
+  public HttpMeta(HttpMeta m) {
+    this();
+    this.closeIdleConnectionsTime = m.closeIdleConnectionsTime;
+    this.connectionTimeout = m.connectionTimeout;
+    this.encoding = m.encoding;
+    this.httpLogin = m.httpLogin;
+    this.httpPassword = m.httpPassword;
+    this.ignoreSsl = m.ignoreSsl;
+    this.proxyHost = m.proxyHost;
+    this.proxyPort = m.proxyPort;
+    this.socketTimeout = m.socketTimeout;
+    this.url = m.url;
+    this.urlField = m.urlField;
+    this.urlInField = m.urlInField;
+    this.lookupParameters = new LookupParameters(m.lookupParameters);
+    this.resultFields = new ResultFields(m.resultFields);
   }
 
   @Override
   public Object clone() {
-    HttpMeta retval = (HttpMeta) super.clone();
-    int nrargs = argumentField.length;
-    int nrheaderparams = headerField.length;
-
-    retval.allocate(nrargs, nrheaderparams);
-
-    System.arraycopy(argumentField, 0, retval.argumentField, 0, nrargs);
-    System.arraycopy(argumentParameter, 0, retval.argumentParameter, 0, 
nrargs);
-    System.arraycopy(headerField, 0, retval.headerField, 0, nrheaderparams);
-    System.arraycopy(headerParameter, 0, retval.headerParameter, 0, 
nrheaderparams);
-
-    return retval;
-  }
-
-  @Override
-  public void setDefault() {
-    socketTimeout = String.valueOf(DEFAULT_SOCKET_TIMEOUT);
-    connectionTimeout = String.valueOf(DEFAULT_CONNECTION_TIMEOUT);
-    closeIdleConnectionsTime = String.valueOf(DEFAULT_CLOSE_CONNECTIONS_TIME);
-    int i;
-    int nrargs;
-    int nrquery;
-    nrargs = 0;
-    nrquery = 0;
-
-    allocate(nrargs, nrquery);
-
-    for (i = 0; i < nrargs; i++) {
-      argumentField[i] = "arg" + i;
-      argumentParameter[i] = "arg";
-    }
-
-    for (i = 0; i < nrquery; i++) {
-      headerField[i] = CONST_HEADER + i;
-      headerParameter[i] = CONST_HEADER;
-    }
-
-    fieldName = CONST_RESULT;
-    resultCodeFieldName = "";
-    responseTimeFieldName = "";
-    responseHeaderFieldName = "";
-    encoding = "UTF-8";
+    return new HttpMeta(this);
   }
 
   @Override
@@ -178,24 +193,23 @@ public class HttpMeta extends BaseTransformMeta<Http, 
HttpData> {
       IRowMeta[] info,
       TransformMeta nextTransform,
       IVariables variables,
-      IHopMetadataProvider metadataProvider)
-      throws HopTransformException {
-    if (!Utils.isEmpty(fieldName)) {
-      IValueMeta v = new ValueMetaString(fieldName);
+      IHopMetadataProvider metadataProvider) {
+    if (!Utils.isEmpty(resultFields.fieldName)) {
+      IValueMeta v = new ValueMetaString(resultFields.fieldName);
       v.setOrigin(name);
       inputRowMeta.addValueMeta(v);
     }
-    if (!Utils.isEmpty(resultCodeFieldName)) {
-      IValueMeta v = new 
ValueMetaInteger(variables.resolve(resultCodeFieldName));
+    if (!Utils.isEmpty(resultFields.resultCodeFieldName)) {
+      IValueMeta v = new 
ValueMetaInteger(variables.resolve(resultFields.resultCodeFieldName));
       v.setOrigin(name);
       inputRowMeta.addValueMeta(v);
     }
-    if (!Utils.isEmpty(responseTimeFieldName)) {
-      IValueMeta v = new 
ValueMetaInteger(variables.resolve(responseTimeFieldName));
+    if (!Utils.isEmpty(resultFields.responseTimeFieldName)) {
+      IValueMeta v = new 
ValueMetaInteger(variables.resolve(resultFields.responseTimeFieldName));
       v.setOrigin(name);
       inputRowMeta.addValueMeta(v);
     }
-    String headerFieldName = variables.resolve(responseHeaderFieldName);
+    String headerFieldName = 
variables.resolve(resultFields.responseHeaderFieldName);
     if (!Utils.isEmpty(headerFieldName)) {
       IValueMeta v = new ValueMetaString(headerFieldName);
       v.setOrigin(name);
@@ -203,111 +217,6 @@ public class HttpMeta extends BaseTransformMeta<Http, 
HttpData> {
     }
   }
 
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder(300);
-
-    retval.append("    ").append(XmlHandler.addTagValue("url", url));
-    retval.append("    " + XmlHandler.addTagValue("urlInField", urlInField));
-    retval.append("    " + XmlHandler.addTagValue("ignoreSsl", ignoreSsl));
-    retval.append("    " + XmlHandler.addTagValue("urlField", urlField));
-    retval.append("    " + XmlHandler.addTagValue("encoding", encoding));
-    retval.append("    " + XmlHandler.addTagValue("httpLogin", httpLogin));
-    retval.append(
-        "    "
-            + XmlHandler.addTagValue(
-                "httpPassword", 
Encr.encryptPasswordIfNotUsingVariables(httpPassword)));
-    retval.append("    " + XmlHandler.addTagValue("proxyHost", proxyHost));
-    retval.append("    " + XmlHandler.addTagValue("proxyPort", proxyPort));
-    retval.append("    " + XmlHandler.addTagValue("socketTimeout", 
socketTimeout));
-    retval.append("    " + XmlHandler.addTagValue("connectionTimeout", 
connectionTimeout));
-    retval.append(
-        "    " + XmlHandler.addTagValue("closeIdleConnectionsTime", 
closeIdleConnectionsTime));
-
-    retval.append("    <lookup>").append(Const.CR);
-
-    for (int i = 0; i < argumentField.length; i++) {
-      retval.append("      <arg>").append(Const.CR);
-      retval.append(CONST_SPACES_LONG).append(XmlHandler.addTagValue("name", 
argumentField[i]));
-      retval
-          .append(CONST_SPACES_LONG)
-          .append(XmlHandler.addTagValue(CONST_PARAMETER, 
argumentParameter[i]));
-      retval.append("      </arg>").append(Const.CR);
-    }
-    for (int i = 0; i < headerField.length; i++) {
-      retval.append("      <header>" + Const.CR);
-      retval.append(CONST_SPACES_LONG + XmlHandler.addTagValue("name", 
headerField[i]));
-      retval.append(
-          CONST_SPACES_LONG + XmlHandler.addTagValue(CONST_PARAMETER, 
headerParameter[i]));
-      retval.append("      </header>" + Const.CR);
-    }
-
-    retval.append("    </lookup>").append(Const.CR);
-
-    retval.append("    <result>").append(Const.CR);
-    retval.append(CONST_SPACES).append(XmlHandler.addTagValue("name", 
fieldName));
-    retval.append(CONST_SPACES).append(XmlHandler.addTagValue("code", 
resultCodeFieldName));
-    retval
-        .append(CONST_SPACES)
-        .append(XmlHandler.addTagValue("response_time", 
responseTimeFieldName));
-    retval
-        .append(CONST_SPACES)
-        .append(XmlHandler.addTagValue("response_header", 
responseHeaderFieldName));
-    retval.append("    </result>").append(Const.CR);
-
-    return retval.toString();
-  }
-
-  private void readData(Node transformNode) throws HopXmlException {
-    try {
-      int nrargs;
-
-      url = XmlHandler.getTagValue(transformNode, "url");
-      urlInField = "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, 
"urlInField"));
-      ignoreSsl = "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, 
"ignoreSsl"));
-      urlField = XmlHandler.getTagValue(transformNode, "urlField");
-      encoding = XmlHandler.getTagValue(transformNode, "encoding");
-      httpLogin = XmlHandler.getTagValue(transformNode, "httpLogin");
-      httpPassword =
-          Encr.decryptPasswordOptionallyEncrypted(
-              XmlHandler.getTagValue(transformNode, "httpPassword"));
-      proxyHost = XmlHandler.getTagValue(transformNode, "proxyHost");
-      proxyPort = XmlHandler.getTagValue(transformNode, "proxyPort");
-
-      socketTimeout = XmlHandler.getTagValue(transformNode, "socketTimeout");
-      connectionTimeout = XmlHandler.getTagValue(transformNode, 
"connectionTimeout");
-      closeIdleConnectionsTime = XmlHandler.getTagValue(transformNode, 
"closeIdleConnectionsTime");
-
-      Node lookup = XmlHandler.getSubNode(transformNode, "lookup");
-      nrargs = XmlHandler.countNodes(lookup, "arg");
-
-      int nrheaders = XmlHandler.countNodes(lookup, CONST_HEADER);
-      allocate(nrargs, nrheaders);
-
-      for (int i = 0; i < nrargs; i++) {
-        Node anode = XmlHandler.getSubNodeByNr(lookup, "arg", i);
-
-        argumentField[i] = XmlHandler.getTagValue(anode, "name");
-        argumentParameter[i] = XmlHandler.getTagValue(anode, CONST_PARAMETER);
-      }
-
-      for (int i = 0; i < nrheaders; i++) {
-        Node anode = XmlHandler.getSubNodeByNr(lookup, CONST_HEADER, i);
-        headerField[i] = XmlHandler.getTagValue(anode, "name");
-        headerParameter[i] = XmlHandler.getTagValue(anode, CONST_PARAMETER);
-      }
-
-      fieldName = XmlHandler.getTagValue(transformNode, CONST_RESULT, "name");
-      resultCodeFieldName = XmlHandler.getTagValue(transformNode, 
CONST_RESULT, "code");
-      responseTimeFieldName = XmlHandler.getTagValue(transformNode, 
CONST_RESULT, "response_time");
-      responseHeaderFieldName =
-          XmlHandler.getTagValue(transformNode, CONST_RESULT, 
"response_header");
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(PKG, 
"HTTPMeta.Exception.UnableToReadTransformMeta"), e);
-    }
-  }
-
   @Override
   public void check(
       List<ICheckResult> remarks,
@@ -375,4 +284,131 @@ public class HttpMeta extends BaseTransformMeta<Http, 
HttpData> {
   public boolean supportsErrorHandling() {
     return true;
   }
+
+  @Getter
+  @Setter
+  public static class QueryParameter {
+    @HopMetadataProperty(
+        key = "name",
+        injectionKey = "ARGUMENT_FIELD",
+        injectionKeyDescription = "HttpMeta.Injection.ARGUMENT_FIELD")
+    private String field;
+
+    @HopMetadataProperty(
+        key = "parameter",
+        injectionKey = "ARGUMENT_PARAMETER",
+        injectionKeyDescription = "HttpMeta.Injection.ARGUMENT_PARAMETER")
+    private String parameter;
+
+    public QueryParameter() {}
+
+    public QueryParameter(String field, String parameter) {
+      this.field = field;
+      this.parameter = parameter;
+    }
+
+    public QueryParameter(QueryParameter p) {
+      this.field = p.field;
+      this.parameter = p.parameter;
+    }
+  }
+
+  @Getter
+  @Setter
+  public static class HeaderParameter {
+    @HopMetadataProperty(
+        key = "name",
+        injectionKey = "HEADER_FIELD",
+        injectionKeyDescription = "HttpMeta.Injection.HEADER_FIELD")
+    private String field;
+
+    @HopMetadataProperty(
+        key = "parameter",
+        injectionKey = "HEADER_PARAMETER",
+        injectionKeyDescription = "HttpMeta.Injection.HEADER_PARAMETER")
+    private String parameter;
+
+    public HeaderParameter() {}
+
+    public HeaderParameter(String field, String parameter) {
+      this.field = field;
+      this.parameter = parameter;
+    }
+
+    public HeaderParameter(HeaderParameter p) {
+      this.field = p.field;
+      this.parameter = p.parameter;
+    }
+  }
+
+  @Getter
+  @Setter
+  public static class LookupParameters {
+    @HopMetadataProperty(
+        key = "arg",
+        injectionKey = "ARG",
+        injectionKeyDescription = "HttpMeta.Injection.ARG")
+    private List<QueryParameter> queryParameters;
+
+    @HopMetadataProperty(
+        key = "header",
+        injectionKey = "HEADER",
+        injectionKeyDescription = "HttpMeta.Injection.HEADER")
+    private List<HeaderParameter> headers;
+
+    public LookupParameters() {
+      this.queryParameters = new ArrayList<>();
+      this.headers = new ArrayList<>();
+    }
+
+    public LookupParameters(LookupParameters p) {
+      this();
+      p.headers.forEach(h -> this.headers.add(new HeaderParameter(h)));
+      p.queryParameters.forEach(q -> this.queryParameters.add(new 
QueryParameter(q)));
+    }
+  }
+
+  @Getter
+  @Setter
+  public static class ResultFields {
+    /** function result: new value name */
+    @HopMetadataProperty(
+        key = "name",
+        injectionKey = "RESULT_FIELD_NAME",
+        injectionKeyDescription = "HttpMeta.Injection.RESULT_FIELD_NAME")
+    private String fieldName;
+
+    @HopMetadataProperty(
+        key = "code",
+        injectionKey = "RESULT_CODE_FIELD_NAME",
+        injectionKeyDescription = "HttpMeta.Injection.RESULT_CODE_FIELD_NAME")
+    private String resultCodeFieldName;
+
+    @HopMetadataProperty(
+        key = "response_time",
+        injectionKey = "RESPONSE_TIME_FIELD_NAME",
+        injectionKeyDescription = 
"HttpMeta.Injection.RESPONSE_TIME_FIELD_NAME")
+    private String responseTimeFieldName;
+
+    @HopMetadataProperty(
+        key = "response_header",
+        injectionKey = "RESPONSE_HEADER_FIELD_NAME",
+        injectionKeyDescription = 
"HttpMeta.Injection.RESPONSE_HEADER_FIELD_NAME")
+    private String responseHeaderFieldName;
+
+    public ResultFields() {
+      this.fieldName = "";
+      this.resultCodeFieldName = "";
+      this.responseTimeFieldName = "";
+      this.responseHeaderFieldName = "";
+    }
+
+    public ResultFields(ResultFields r) {
+      this();
+      this.fieldName = r.fieldName;
+      this.responseHeaderFieldName = r.responseHeaderFieldName;
+      this.responseTimeFieldName = r.responseTimeFieldName;
+      this.resultCodeFieldName = r.resultCodeFieldName;
+    }
+  }
 }
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 a222711b6b..91e0b37730 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
@@ -24,16 +24,12 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.hop.core.row.RowMeta;
-import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
 import org.apache.http.NameValuePair;
 import org.apache.http.message.BasicNameValuePair;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
 
-@ExtendWith(RestoreHopEnvironmentExtension.class)
 class HttpDataTest {
-
   private HttpData data;
 
   @BeforeEach
@@ -102,13 +98,13 @@ class HttpDataTest {
 
   @Test
   void testArgnrs() {
-    assertNull(data.argnrs, "argnrs should be null initially");
+    assertNull(data.argNrs, "argnrs should be null initially");
 
-    data.argnrs = new int[] {0, 1, 2};
-    assertEquals(3, data.argnrs.length);
-    assertEquals(0, data.argnrs[0]);
-    assertEquals(1, data.argnrs[1]);
-    assertEquals(2, data.argnrs[2]);
+    data.argNrs = new int[] {0, 1, 2};
+    assertEquals(3, data.argNrs.length);
+    assertEquals(0, data.argNrs[0]);
+    assertEquals(1, data.argNrs[1]);
+    assertEquals(2, data.argNrs[2]);
   }
 
   @Test
@@ -143,19 +139,18 @@ class HttpDataTest {
     assertFalse(data.useHeaderParameters, "useHeaderParameters should be false 
by default");
 
     data.useHeaderParameters = true;
-    assertEquals(true, data.useHeaderParameters);
+    assertTrue(data.useHeaderParameters);
   }
 
   @Test
   void testHeaderParameters() {
     assertNull(data.headerParameters, "headerParameters should be null 
initially");
 
-    NameValuePair[] params =
+    data.headerParameters =
         new NameValuePair[] {
           new BasicNameValuePair("Authorization", "Bearer token"),
           new BasicNameValuePair("Content-Type", "application/json")
         };
-    data.headerParameters = params;
 
     assertNotNull(data.headerParameters);
     assertEquals(2, data.headerParameters.length);
@@ -217,9 +212,9 @@ class HttpDataTest {
 
   @Test
   void testArgnrsWithEmptyArray() {
-    data.argnrs = new int[0];
-    assertNotNull(data.argnrs);
-    assertEquals(0, data.argnrs.length);
+    data.argNrs = new int[0];
+    assertNotNull(data.argNrs);
+    assertEquals(0, data.argNrs.length);
   }
 
   @Test
diff --git 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaLoadSaveTest.java
 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaLoadSaveTest.java
deleted file mode 100644
index 102bcbb149..0000000000
--- 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaLoadSaveTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.http;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-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.pipeline.transforms.loadsave.LoadSaveTester;
-import 
org.apache.hop.pipeline.transforms.loadsave.validator.ArrayLoadSaveValidator;
-import 
org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator;
-import 
org.apache.hop.pipeline.transforms.loadsave.validator.StringLoadSaveValidator;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-class HttpMetaLoadSaveTest {
-  @RegisterExtension
-  static RestoreHopEngineEnvironmentExtension env = new 
RestoreHopEngineEnvironmentExtension();
-
-  LoadSaveTester loadSaveTester;
-
-  @BeforeEach
-  void testLoadSaveRoundTrip() throws Exception {
-    HopEnvironment.init();
-    PluginRegistry.init();
-    List<String> attributes =
-        Arrays.asList(
-            "url",
-            "urlInField",
-            "urlField",
-            "encoding",
-            "httpLogin",
-            "httpPassword",
-            "proxyHost",
-            "proxyPort",
-            "socketTimeout",
-            "connectionTimeout",
-            "closeIdleConnectionsTime",
-            "argumentField",
-            "argumentParameter",
-            "headerField",
-            "headerParameter",
-            "fieldName",
-            "resultCodeFieldName",
-            "responseTimeFieldName",
-            "responseHeaderFieldName");
-    Map<String, IFieldLoadSaveValidator<?>> fieldLoadSaveValidatorAttributeMap 
= new HashMap<>();
-
-    // Arrays need to be consistent length
-    IFieldLoadSaveValidator<String[]> stringArrayLoadSaveValidator =
-        new ArrayLoadSaveValidator<>(new StringLoadSaveValidator(), 25);
-    fieldLoadSaveValidatorAttributeMap.put("argumentField", 
stringArrayLoadSaveValidator);
-    fieldLoadSaveValidatorAttributeMap.put("argumentParameter", 
stringArrayLoadSaveValidator);
-    fieldLoadSaveValidatorAttributeMap.put("headerField", 
stringArrayLoadSaveValidator);
-    fieldLoadSaveValidatorAttributeMap.put("headerParameter", 
stringArrayLoadSaveValidator);
-
-    loadSaveTester =
-        new LoadSaveTester(
-            HttpMeta.class,
-            attributes,
-            new HashMap<>(),
-            new HashMap<>(),
-            fieldLoadSaveValidatorAttributeMap,
-            new HashMap<>());
-  }
-
-  @Test
-  void testSerialization() throws HopException {
-    loadSaveTester.testSerialization();
-  }
-}
diff --git 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaTest.java
 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaTest.java
index f78f5f2ec3..084f9532c7 100644
--- 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaTest.java
+++ 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaTest.java
@@ -18,324 +18,93 @@
 package org.apache.hop.pipeline.transforms.http;
 
 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;
-import static org.mockito.Mockito.mock;
 
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.hop.core.ICheckResult;
-import org.apache.hop.core.row.IRowMeta;
-import org.apache.hop.core.row.RowMeta;
-import org.apache.hop.core.variables.Variables;
-import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
-import org.apache.hop.pipeline.PipelineMeta;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+import org.apache.hop.core.HopClientEnvironment;
+import org.apache.hop.core.exception.HopXmlException;
+import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.apache.hop.metadata.serializer.xml.XmlMetadataUtil;
 import org.apache.hop.pipeline.transform.TransformMeta;
-import org.junit.jupiter.api.BeforeEach;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
 
-@ExtendWith(RestoreHopEnvironmentExtension.class)
 class HttpMetaTest {
-
-  private HttpMeta meta;
-
-  @BeforeEach
-  void setUp() {
-    meta = new HttpMeta();
+  @BeforeAll
+  static void before() throws Exception {
+    HopClientEnvironment.init();
   }
 
   @Test
-  void testSetDefault() {
-    meta.setDefault();
-
-    assertEquals(String.valueOf(HttpMeta.DEFAULT_SOCKET_TIMEOUT), 
meta.getSocketTimeout());
-    assertEquals(String.valueOf(HttpMeta.DEFAULT_CONNECTION_TIMEOUT), 
meta.getConnectionTimeout());
-    assertEquals(
-        String.valueOf(HttpMeta.DEFAULT_CLOSE_CONNECTIONS_TIME),
-        meta.getCloseIdleConnectionsTime());
-    assertEquals("result", meta.getFieldName());
-    assertEquals("UTF-8", meta.getEncoding());
-    assertEquals("", meta.getResultCodeFieldName());
-    assertEquals("", meta.getResponseTimeFieldName());
-    assertEquals("", meta.getResponseHeaderFieldName());
+  void testLoadSave() throws Exception {
+    Path path = 
Paths.get(Objects.requireNonNull(getClass().getResource("/transform.xml")).toURI());
+    HttpMeta meta = loadHttpMetaFromXml(Files.readString(path));
+
+    validateHttpMeta(meta);
+
+    // Do an XML round trip
+    //
+    String xmlCopy =
+        XmlHandler.openTag(TransformMeta.XML_TAG)
+            + XmlMetadataUtil.serializeObjectToXml(meta)
+            + XmlHandler.closeTag(TransformMeta.XML_TAG);
+    HttpMeta metaCopy = loadHttpMetaFromXml(xmlCopy);
+    validateHttpMeta(metaCopy);
   }
 
-  @Test
-  void testUrlConfiguration() {
-    // Test URL properties
-    meta.setUrl("http://example.com";);
-    assertEquals("http://example.com";, meta.getUrl());
-
-    meta.setUrlInField(true);
-    assertTrue(meta.isUrlInField());
-
-    meta.setUrlField("urlColumn");
-    assertEquals("urlColumn", meta.getUrlField());
-
-    meta.setUrlInField(false);
-    assertFalse(meta.isUrlInField());
+  private static @NotNull HttpMeta loadHttpMetaFromXml(String xml) throws 
HopXmlException {
+    HttpMeta meta = new HttpMeta();
+    XmlMetadataUtil.deSerializeFromXml(
+        XmlHandler.loadXmlString(xml, TransformMeta.XML_TAG),
+        HttpMeta.class,
+        meta,
+        new MemoryMetadataProvider());
+    return meta;
   }
 
-  @Test
-  void testOutputFieldConfiguration() {
-    // Test all output field properties
-    meta.setFieldName("resultField");
-    assertEquals("resultField", meta.getFieldName());
-
-    meta.setResultCodeFieldName("responseCode");
-    assertEquals("responseCode", meta.getResultCodeFieldName());
-
-    meta.setResponseTimeFieldName("responseTime");
-    assertEquals("responseTime", meta.getResponseTimeFieldName());
-
-    meta.setResponseHeaderFieldName("responseHeader");
-    assertEquals("responseHeader", meta.getResponseHeaderFieldName());
-  }
-
-  @Test
-  void testProxyAndAuthConfiguration() {
-    // Test proxy and authentication properties
-    meta.setProxyHost("proxy.example.com");
-    assertEquals("proxy.example.com", meta.getProxyHost());
-
-    meta.setProxyPort("8080");
-    assertEquals("8080", meta.getProxyPort());
+  private static void validateHttpMeta(HttpMeta meta) {
+    assertNotNull(meta.getResultFields());
+    assertEquals("result", meta.getResultFields().getFieldName());
+    assertEquals("httpStatus", 
meta.getResultFields().getResultCodeFieldName());
+    assertEquals("responseHeaders", 
meta.getResultFields().getResponseHeaderFieldName());
+    assertEquals("responseTime", 
meta.getResultFields().getResponseTimeFieldName());
 
-    meta.setHttpLogin("username");
-    assertEquals("username", meta.getHttpLogin());
-
-    meta.setHttpPassword("password123");
-    assertEquals("password123", meta.getHttpPassword());
-  }
-
-  @Test
-  void testTimeoutAndConnectionConfiguration() {
-    // Test timeout and connection properties
-    meta.setSocketTimeout("5000");
-    assertEquals("5000", meta.getSocketTimeout());
-
-    meta.setConnectionTimeout("3000");
-    assertEquals("3000", meta.getConnectionTimeout());
-
-    meta.setCloseIdleConnectionsTime("10000");
-    assertEquals("10000", meta.getCloseIdleConnectionsTime());
-
-    meta.setEncoding("ISO-8859-1");
-    assertEquals("ISO-8859-1", meta.getEncoding());
-
-    meta.setIgnoreSsl(true);
+    assertEquals("UTF-8", meta.getEncoding());
+    assertEquals("http-user", meta.getHttpLogin());
+    assertEquals("http-password", meta.getHttpPassword());
+    assertEquals("url", meta.getUrl());
+    assertEquals("urlField", meta.getUrlField());
+    assertEquals("10000", meta.getConnectionTimeout());
+    assertEquals("98765", meta.getCloseIdleConnectionsTime());
+    assertEquals("proxyHost", meta.getProxyHost());
+    assertEquals("proxyPort", meta.getProxyPort());
+    assertTrue(meta.isUrlInField());
     assertTrue(meta.isIgnoreSsl());
 
-    meta.setIgnoreSsl(false);
-    assertFalse(meta.isIgnoreSsl());
-  }
-
-  @Test
-  void testArgumentsAndHeadersConfiguration() {
-    // Test allocate
-    meta.allocate(3, 2);
-    assertNotNull(meta.getArgumentField());
-    assertNotNull(meta.getArgumentParameter());
-    assertNotNull(meta.getHeaderField());
-    assertNotNull(meta.getHeaderParameter());
-    assertEquals(3, meta.getArgumentField().length);
-    assertEquals(3, meta.getArgumentParameter().length);
-    assertEquals(2, meta.getHeaderField().length);
-    assertEquals(2, meta.getHeaderParameter().length);
-
-    // Test argument fields and parameters
-    String[] argFields = {"field1", "field2", "field3"};
-    meta.setArgumentField(argFields);
-    assertEquals(3, meta.getArgumentField().length);
-    assertEquals("field1", meta.getArgumentField()[0]);
-    assertEquals("field2", meta.getArgumentField()[1]);
-
-    String[] argParams = {"param1", "param2"};
-    meta.setArgumentParameter(argParams);
-    assertEquals("param1", meta.getArgumentParameter()[0]);
-    assertEquals("param2", meta.getArgumentParameter()[1]);
-
-    // Test header fields and parameters
-    String[] headerFields = {"Authorization", "Content-Type"};
-    meta.setHeaderField(headerFields);
-    assertEquals(2, meta.getHeaderField().length);
-    assertEquals("Authorization", meta.getHeaderField()[0]);
-
-    String[] headerParams = {"Bearer token", "application/json"};
-    meta.setHeaderParameter(headerParams);
-    assertEquals(2, meta.getHeaderParameter().length);
-    assertEquals("Bearer token", meta.getHeaderParameter()[0]);
-  }
-
-  @Test
-  void testClone() {
-    meta.allocate(2, 1);
-    meta.setUrl("http://test.com";);
-    meta.setFieldName("output");
-    meta.getArgumentField()[0] = "arg1";
-    meta.getArgumentField()[1] = "arg2";
-    meta.getArgumentParameter()[0] = "param1";
-    meta.getArgumentParameter()[1] = "param2";
-    meta.getHeaderField()[0] = "header1";
-    meta.getHeaderParameter()[0] = "value1";
-
-    HttpMeta cloned = (HttpMeta) meta.clone();
-
-    assertNotNull(cloned);
-    assertEquals(meta.getUrl(), cloned.getUrl());
-    assertEquals(meta.getFieldName(), cloned.getFieldName());
-    assertEquals(2, cloned.getArgumentField().length);
-    assertEquals("arg1", cloned.getArgumentField()[0]);
-    assertEquals("arg2", cloned.getArgumentField()[1]);
-  }
-
-  @Test
-  void testGetFields() throws Exception {
-    Variables variables = new Variables();
-
-    // Test with all fields configured
-    meta.setFieldName("resultField");
-    meta.setResultCodeFieldName("statusCode");
-    meta.setResponseTimeFieldName("responseTime");
-    meta.setResponseHeaderFieldName("headers");
-
-    IRowMeta rowMetaAll = new RowMeta();
-    meta.getFields(rowMetaAll, "HttpTransform", null, null, variables, null);
-    assertEquals(4, rowMetaAll.size());
-    assertNotNull(rowMetaAll.searchValueMeta("resultField"));
-    assertNotNull(rowMetaAll.searchValueMeta("statusCode"));
-    assertNotNull(rowMetaAll.searchValueMeta("responseTime"));
-    assertNotNull(rowMetaAll.searchValueMeta("headers"));
-    assertEquals("HttpTransform", 
rowMetaAll.searchValueMeta("resultField").getOrigin());
-
-    // Test with empty field names
-    meta.setFieldName("");
-    meta.setResultCodeFieldName("");
-    meta.setResponseTimeFieldName("");
-    meta.setResponseHeaderFieldName("");
-    IRowMeta rowMetaEmpty = new RowMeta();
-    meta.getFields(rowMetaEmpty, "HttpTransform", null, null, variables, null);
-    assertEquals(0, rowMetaEmpty.size());
-
-    // Test with only result field
-    meta.setFieldName("result");
-    IRowMeta rowMetaSingle = new RowMeta();
-    meta.getFields(rowMetaSingle, "HttpTransform", null, null, variables, 
null);
-    assertEquals(1, rowMetaSingle.size());
-    assertNotNull(rowMetaSingle.searchValueMeta("result"));
-  }
-
-  @Test
-  void testGetXml() {
-    meta.allocate(1, 1);
-    meta.setUrl("http://example.com";);
-    meta.setUrlInField(true);
-    meta.setUrlField("urlField");
-    meta.setEncoding("UTF-8");
-    meta.setHttpLogin("user");
-    meta.setHttpPassword("pass");
-    meta.setProxyHost("proxy");
-    meta.setProxyPort("8080");
-    meta.getArgumentField()[0] = "arg1";
-    meta.getArgumentParameter()[0] = "param1";
-    meta.getHeaderField()[0] = "header1";
-    meta.getHeaderParameter()[0] = "value1";
-    meta.setFieldName("result");
-    meta.setResultCodeFieldName("code");
-
-    String xml = meta.getXml();
-
-    assertNotNull(xml);
-    assertTrue(xml.contains("<url>http://example.com</url>"));
-    assertTrue(xml.contains("<urlInField>Y</urlInField>"));
-    assertTrue(xml.contains("<urlField>urlField</urlField>"));
-    assertTrue(xml.contains("<encoding>UTF-8</encoding>"));
-    assertTrue(xml.contains("<httpLogin>user</httpLogin>"));
-    assertTrue(xml.contains("<proxyHost>proxy</proxyHost>"));
-    assertTrue(xml.contains("<proxyPort>8080</proxyPort>"));
-    assertTrue(xml.contains("<name>arg1</name>"));
-    assertTrue(xml.contains("<parameter>param1</parameter>"));
-    assertTrue(xml.contains("<name>result</name>"));
-    assertTrue(xml.contains("<code>code</code>"));
-  }
-
-  @Test
-  void testCheckValidation() {
-    TransformMeta transformMeta = mock(TransformMeta.class);
-    PipelineMeta pipelineMeta = mock(PipelineMeta.class);
-    IRowMeta prev = mock(IRowMeta.class);
-    List<ICheckResult> remarks;
-
-    // Test with input transform
-    remarks = new ArrayList<>();
-    meta.check(
-        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
-    assertTrue(remarks.size() > 0);
-    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_OK));
-
-    // Test without input transform
-    remarks = new ArrayList<>();
-    meta.check(remarks, pipelineMeta, transformMeta, prev, new String[] {}, 
null, null, null, null);
-    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_ERROR));
-
-    // Test URL in field - missing field name
-    remarks = new ArrayList<>();
-    meta.setUrlInField(true);
-    meta.setUrlField("");
-    meta.check(
-        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
-    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_ERROR));
-
-    // Test URL in field - with field name
-    remarks = new ArrayList<>();
-    meta.setUrlField("urlColumn");
-    meta.check(
-        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
-    assertFalse(remarks.isEmpty());
-
-    // Test URL not in field - missing URL
-    remarks = new ArrayList<>();
-    meta.setUrlInField(false);
-    meta.setUrl("");
-    meta.check(
-        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
-    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_ERROR));
-
-    // Test URL not in field - with URL
-    remarks = new ArrayList<>();
-    meta.setUrl("http://example.com";);
-    meta.check(
-        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
-    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_OK));
-  }
-
-  @Test
-  void testMiscellaneous() {
-    // Test error handling support
-    assertTrue(meta.supportsErrorHandling());
-
-    // Test constants
-    assertEquals(10000, HttpMeta.DEFAULT_SOCKET_TIMEOUT);
-    assertEquals(10000, HttpMeta.DEFAULT_CONNECTION_TIMEOUT);
-    assertEquals(HttpMeta.DEFAULT_CLOSE_CONNECTIONS_TIME, -1);
-    assertEquals("header", HttpMeta.CONST_HEADER);
-    assertEquals("result", HttpMeta.CONST_RESULT);
-    assertEquals("        ", HttpMeta.CONST_SPACES_LONG);
-    assertEquals("      ", HttpMeta.CONST_SPACES);
-    assertEquals("parameter", HttpMeta.CONST_PARAMETER);
-
-    // Test allocate with zero
-    meta.allocate(0, 0);
-    assertEquals(0, meta.getArgumentField().length);
-    assertEquals(0, meta.getArgumentParameter().length);
-    assertEquals(0, meta.getHeaderField().length);
-    assertEquals(0, meta.getHeaderParameter().length);
-
-    // Test clone without allocation
-    HttpMeta cloned = (HttpMeta) meta.clone();
-    assertNotNull(cloned);
-    assertEquals(0, cloned.getArgumentField().length);
+    assertNotNull(meta.getLookupParameters());
+    assertEquals(2, meta.getLookupParameters().getQueryParameters().size());
+    HttpMeta.QueryParameter p0 = 
meta.getLookupParameters().getQueryParameters().get(0);
+    assertEquals("queryField1", p0.getField());
+    assertEquals("queryParameter1", p0.getParameter());
+    HttpMeta.QueryParameter p1 = 
meta.getLookupParameters().getQueryParameters().get(1);
+    assertEquals("queryField2", p1.getField());
+    assertEquals("queryParameter2", p1.getParameter());
+
+    assertEquals(3, meta.getLookupParameters().getHeaders().size());
+    HttpMeta.HeaderParameter h0 = 
meta.getLookupParameters().getHeaders().get(0);
+    assertEquals("headerField1", h0.getField());
+    assertEquals("headerParameter1", h0.getParameter());
+    HttpMeta.HeaderParameter h1 = 
meta.getLookupParameters().getHeaders().get(1);
+    assertEquals("headerField2", h1.getField());
+    assertEquals("headerParameter2", h1.getParameter());
+    HttpMeta.HeaderParameter h2 = 
meta.getLookupParameters().getHeaders().get(2);
+    assertEquals("headerField3", h2.getField());
+    assertEquals("headerParameter3", h2.getParameter());
   }
 }
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
deleted file mode 100644
index f73c4dd0a1..0000000000
--- 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
+++ /dev/null
@@ -1,670 +0,0 @@
-/*
- * 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.http;
-
-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;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import java.io.ByteArrayInputStream;
-import java.net.HttpURLConnection;
-import java.nio.charset.StandardCharsets;
-import org.apache.hop.core.logging.ILoggingObject;
-import org.apache.hop.core.row.IRowMeta;
-import org.apache.hop.core.row.RowMeta;
-import org.apache.hop.core.row.value.ValueMetaString;
-import org.apache.hop.core.util.HttpClientManager;
-import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
-import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.protocol.HttpContext;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.MockedStatic;
-
-@ExtendWith(RestoreHopEnvironmentExtension.class)
-class HttpTest {
-
-  private TransformMockHelper<HttpMeta, HttpData> transformMockHelper;
-  private Http httpTransform;
-  private HttpMeta httpMeta;
-  private HttpData httpData;
-
-  private static final String TEST_RESPONSE_BODY = "Test response from HTTP 
server";
-  private static final String TEST_URL = "http://example.com/api";;
-
-  @BeforeEach
-  void setUp() throws Exception {
-    transformMockHelper = new TransformMockHelper<>("HTTP Test", 
HttpMeta.class, HttpData.class);
-    when(transformMockHelper.logChannelFactory.create(any(), 
any(ILoggingObject.class)))
-        .thenReturn(transformMockHelper.iLogChannel);
-    when(transformMockHelper.pipeline.isRunning()).thenReturn(true);
-
-    // Mock getPrevTransforms to return an array with one element by default
-    when(transformMockHelper.pipelineMeta.getPrevTransforms(any()))
-        .thenReturn(new org.apache.hop.pipeline.transform.TransformMeta[1]);
-
-    httpMeta = new HttpMeta();
-    httpMeta.setDefault();
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("response");
-    httpMeta.allocate(0, 0);
-
-    httpData = new HttpData();
-
-    httpTransform =
-        new Http(
-            transformMockHelper.transformMeta,
-            httpMeta,
-            httpData,
-            0,
-            transformMockHelper.pipelineMeta,
-            transformMockHelper.pipeline);
-  }
-
-  @AfterEach
-  void tearDown() {
-    transformMockHelper.cleanUp();
-  }
-
-  @Test
-  void testProcessRowWithNullRow() throws Exception {
-    // Create a spy to mock getRow()
-    Http httpSpy = spy(httpTransform);
-    doReturn(null).when(httpSpy).getRow();
-
-    // Process row - should return false when no more rows
-    boolean result = httpSpy.processRow();
-
-    assertFalse(result, "processRow should return false when row is null");
-  }
-
-  @Test
-  void testProcessRowFullExecution() throws Exception {
-    // Configure meta
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("responseBody");
-    httpMeta.setResultCodeFieldName("statusCode");
-    httpMeta.allocate(0, 0);
-
-    // Set up input row meta
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("id"));
-    Object[] inputRow = new Object[] {"test123"};
-
-    // Mock HttpClientManager for the full execution
-    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
-      HttpClientManager mockManager = mock(HttpClientManager.class);
-      HttpClientManager.HttpClientBuilderFacade mockBuilder =
-          mock(HttpClientManager.HttpClientBuilderFacade.class);
-      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
-      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
-
-      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
-      when(mockManager.createBuilder()).thenReturn(mockBuilder);
-      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
-      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
-      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
-      when(mockBuilder.build()).thenReturn(mockClient);
-
-      // Mock HTTP response
-      StatusLine mockStatusLine = mock(StatusLine.class);
-      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
-      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
-
-      HttpEntity mockEntity = mock(HttpEntity.class);
-      when(mockEntity.getContent())
-          .thenReturn(
-              new ByteArrayInputStream("Success 
response".getBytes(StandardCharsets.UTF_8)));
-      when(mockResponse.getEntity()).thenReturn(mockEntity);
-      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
-
-      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
-          .thenReturn(mockResponse);
-
-      // Create spy to mock getRow and capture putRow
-      Http httpSpy = spy(httpTransform);
-      doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
-      doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
-
-      // Capture output
-      final Object[][] outputRows = new Object[1][];
-      org.mockito.Mockito.doAnswer(
-              invocation -> {
-                outputRows[0] = invocation.getArgument(1);
-                return null;
-              })
-          .when(httpSpy)
-          .putRow(any(IRowMeta.class), any(Object[].class));
-
-      // Initialize and process
-      httpSpy.init();
-      boolean result1 = httpSpy.processRow();
-      boolean result2 = httpSpy.processRow();
-
-      // Verify
-      assertTrue(result1, "First processRow should return true");
-      assertFalse(result2, "Second processRow should return false (no more 
rows)");
-      assertNotNull(outputRows[0], "Output row should be captured");
-      assertTrue(outputRows[0].length >= 3, "Output should have input + 
response fields");
-      assertEquals("test123", outputRows[0][0], "First field should be input 
ID");
-      assertEquals("Success response", outputRows[0][1], "Second field should 
be response body");
-      assertEquals(200L, outputRows[0][2], "Third field should be status 
code");
-    }
-  }
-
-  @Test
-  void testProcessRowWithQueryParameters() throws Exception {
-    // Configure meta with query parameters
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("response");
-    httpMeta.allocate(2, 0);
-    httpMeta.getArgumentField()[0] = "userId";
-    httpMeta.getArgumentField()[1] = "action";
-    httpMeta.getArgumentParameter()[0] = "id";
-    httpMeta.getArgumentParameter()[1] = "action";
-
-    // Set up input row meta
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("userId"));
-    inputRowMeta.addValueMeta(new ValueMetaString("action"));
-    Object[] inputRow = new Object[] {"user123", "view"};
-
-    // Mock HttpClientManager
-    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
-      HttpClientManager mockManager = mock(HttpClientManager.class);
-      HttpClientManager.HttpClientBuilderFacade mockBuilder =
-          mock(HttpClientManager.HttpClientBuilderFacade.class);
-      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
-      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
-
-      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
-      when(mockManager.createBuilder()).thenReturn(mockBuilder);
-      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
-      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
-      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
-      when(mockBuilder.build()).thenReturn(mockClient);
-
-      StatusLine mockStatusLine = mock(StatusLine.class);
-      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
-      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
-
-      HttpEntity mockEntity = mock(HttpEntity.class);
-      when(mockEntity.getContent())
-          .thenReturn(new ByteArrayInputStream("Query 
result".getBytes(StandardCharsets.UTF_8)));
-      when(mockResponse.getEntity()).thenReturn(mockEntity);
-      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
-
-      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
-          .thenReturn(mockResponse);
-
-      // Create spy
-      Http httpSpy = spy(httpTransform);
-      doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
-      doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
-
-      final Object[][] outputRows = new Object[1][];
-      org.mockito.Mockito.doAnswer(
-              invocation -> {
-                outputRows[0] = invocation.getArgument(1);
-                return null;
-              })
-          .when(httpSpy)
-          .putRow(any(IRowMeta.class), any(Object[].class));
-
-      // Initialize and process
-      httpSpy.init();
-      boolean result = httpSpy.processRow();
-
-      // Verify
-      assertTrue(result, "processRow should return true");
-      assertNotNull(outputRows[0], "Output row should be captured");
-      assertEquals("user123", outputRows[0][0], "First field should be 
userId");
-      assertEquals("view", outputRows[0][1], "Second field should be action");
-      assertEquals("Query result", outputRows[0][2], "Third field should be 
response");
-    }
-  }
-
-  @Test
-  void testProcessRowWithHeaders() throws Exception {
-    // Configure meta with custom headers
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("response");
-    httpMeta.allocate(0, 1);
-    httpMeta.getHeaderField()[0] = "authToken";
-    httpMeta.getHeaderParameter()[0] = "Authorization";
-
-    // Set up input row meta
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("authToken"));
-    Object[] inputRow = new Object[] {"Bearer token123"};
-
-    // Mock HttpClientManager
-    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
-      HttpClientManager mockManager = mock(HttpClientManager.class);
-      HttpClientManager.HttpClientBuilderFacade mockBuilder =
-          mock(HttpClientManager.HttpClientBuilderFacade.class);
-      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
-      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
-
-      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
-      when(mockManager.createBuilder()).thenReturn(mockBuilder);
-      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
-      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
-      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
-      when(mockBuilder.build()).thenReturn(mockClient);
-
-      StatusLine mockStatusLine = mock(StatusLine.class);
-      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
-      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
-
-      HttpEntity mockEntity = mock(HttpEntity.class);
-      when(mockEntity.getContent())
-          .thenReturn(new 
ByteArrayInputStream("Authorized".getBytes(StandardCharsets.UTF_8)));
-      when(mockResponse.getEntity()).thenReturn(mockEntity);
-      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
-
-      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
-          .thenReturn(mockResponse);
-
-      // Create spy
-      Http httpSpy = spy(httpTransform);
-      doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
-      doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
-
-      final Object[][] outputRows = new Object[1][];
-      org.mockito.Mockito.doAnswer(
-              invocation -> {
-                outputRows[0] = invocation.getArgument(1);
-                return null;
-              })
-          .when(httpSpy)
-          .putRow(any(IRowMeta.class), any(Object[].class));
-
-      // Initialize and process
-      httpSpy.init();
-      boolean result = httpSpy.processRow();
-
-      // Verify
-      assertTrue(result, "processRow should return true");
-      assertNotNull(outputRows[0], "Output row should be captured");
-      assertEquals("Bearer token123", outputRows[0][0], "First field should be 
auth token");
-      assertEquals("Authorized", outputRows[0][1], "Second field should be 
response");
-    }
-  }
-
-  @Test
-  void testInit() {
-    httpMeta.setProxyHost("proxy.example.com");
-    httpMeta.setProxyPort("8080");
-    httpMeta.setHttpLogin("user");
-    httpMeta.setHttpPassword("password");
-    httpMeta.setSocketTimeout("5000");
-    httpMeta.setConnectionTimeout("3000");
-
-    boolean result = httpTransform.init();
-
-    assertTrue(result, "init should return true");
-    assertEquals("proxy.example.com", httpData.realProxyHost);
-    assertEquals(8080, httpData.realProxyPort);
-    assertEquals("user", httpData.realHttpLogin);
-    assertEquals("password", httpData.realHttpPassword);
-    assertEquals(5000, httpData.realSocketTimeout);
-    assertEquals(3000, httpData.realConnectionTimeout);
-  }
-
-  @Test
-  void testRequestStatusCode() {
-    CloseableHttpResponse response = mock(CloseableHttpResponse.class);
-    StatusLine statusLine = mock(StatusLine.class);
-    when(response.getStatusLine()).thenReturn(statusLine);
-    when(statusLine.getStatusCode()).thenReturn(200);
-
-    int statusCode = httpTransform.requestStatusCode(response);
-
-    assertEquals(200, statusCode);
-  }
-
-  @Test
-  void testSearchForHeaders() {
-    CloseableHttpResponse response = mock(CloseableHttpResponse.class);
-    Header[] headers =
-        new Header[] {
-          new BasicHeader("Content-Type", "application/json"),
-          new BasicHeader("Authorization", "Bearer token")
-        };
-    when(response.getAllHeaders()).thenReturn(headers);
-
-    Header[] result = httpTransform.searchForHeaders(response);
-
-    assertNotNull(result);
-    assertEquals(2, result.length);
-    assertEquals("Content-Type", result[0].getName());
-    assertEquals("application/json", result[0].getValue());
-  }
-
-  @Test
-  void testCallHttpServiceBasicCall() throws Exception {
-    // Set up input row meta
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("id"));
-    Object[] inputRow = new Object[] {"123"};
-
-    // Configure meta
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("result");
-    httpMeta.allocate(0, 0);
-
-    // Set up data
-    httpData.realUrl = TEST_URL;
-    httpData.realConnectionTimeout = 10000;
-    httpData.realSocketTimeout = 10000;
-    httpData.argnrs = new int[0];
-    httpData.inputRowMeta = inputRowMeta;
-    httpData.useHeaderParameters = false;
-    httpData.headerParametersNrs = new int[0];
-
-    // Mock HttpClientManager
-    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
-      HttpClientManager mockManager = mock(HttpClientManager.class);
-      HttpClientManager.HttpClientBuilderFacade mockBuilder =
-          mock(HttpClientManager.HttpClientBuilderFacade.class);
-      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
-      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
-
-      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
-      when(mockManager.createBuilder()).thenReturn(mockBuilder);
-      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
-      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
-      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
-      // ignoreSsl is a void method, use doNothing
-      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
-      when(mockBuilder.build()).thenReturn(mockClient);
-
-      // Mock HTTP response
-      StatusLine mockStatusLine = mock(StatusLine.class);
-      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
-      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
-
-      HttpEntity mockEntity = mock(HttpEntity.class);
-      when(mockEntity.getContent())
-          .thenReturn(
-              new 
ByteArrayInputStream(TEST_RESPONSE_BODY.getBytes(StandardCharsets.UTF_8)));
-      when(mockResponse.getEntity()).thenReturn(mockEntity);
-      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
-
-      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
-          .thenReturn(mockResponse);
-
-      // Call the method
-      Object[] result = httpTransform.callHttpService(inputRowMeta, inputRow);
-
-      // Verify result
-      assertNotNull(result, "Result should not be null");
-      // Result should have input + response field (method adds response body 
to end)
-      assertTrue(result.length >= 2, "Result should have at least input + 
response fields");
-      assertEquals("123", result[0], "First field should be input ID");
-      assertEquals(TEST_RESPONSE_BODY, result[1], "Second field should be 
response body");
-    }
-  }
-
-  @Test
-  void testCallHttpServiceWithResponseFields() throws Exception {
-    // Test that callHttpService prepares to return all response fields
-
-    // Configure meta with all response fields
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("responseBody");
-    httpMeta.setResultCodeFieldName("statusCode");
-    httpMeta.setResponseTimeFieldName("responseTime");
-    httpMeta.setResponseHeaderFieldName("responseHeaders");
-    httpMeta.allocate(0, 0);
-
-    // Set up input row
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("id"));
-
-    // Prepare output row meta
-    IRowMeta outputRowMeta = inputRowMeta.clone();
-    httpMeta.getFields(outputRowMeta, "HTTP", null, null, httpTransform, null);
-
-    // Verify output fields are configured
-    assertEquals(5, outputRowMeta.size()); // 1 input + 4 output fields
-    assertNotNull(outputRowMeta.searchValueMeta("responseBody"));
-    assertNotNull(outputRowMeta.searchValueMeta("statusCode"));
-    assertNotNull(outputRowMeta.searchValueMeta("responseTime"));
-    assertNotNull(outputRowMeta.searchValueMeta("responseHeaders"));
-  }
-
-  @Test
-  void testCallHttpServiceWithQueryParameters() throws Exception {
-    // Test callHttpService setup with query parameters
-
-    // Configure with query parameters
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("result");
-    httpMeta.allocate(2, 0);
-    httpMeta.getArgumentField()[0] = "userId";
-    httpMeta.getArgumentField()[1] = "filter";
-    httpMeta.getArgumentParameter()[0] = "id";
-    httpMeta.getArgumentParameter()[1] = "filter";
-
-    // Set up input row
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("userId"));
-    inputRowMeta.addValueMeta(new ValueMetaString("filter"));
-    Object[] inputRow = new Object[] {"user123", "active"};
-
-    // Set up data for the call
-    httpData.realUrl = TEST_URL;
-    httpData.argnrs = new int[] {0, 1};
-    httpData.inputRowMeta = inputRowMeta;
-
-    // Verify parameters are accessible for URL building
-    assertEquals("user123", inputRowMeta.getString(inputRow, 0));
-    assertEquals("active", inputRowMeta.getString(inputRow, 1));
-    assertEquals("id", httpMeta.getArgumentParameter()[0]);
-    assertEquals("filter", httpMeta.getArgumentParameter()[1]);
-  }
-
-  @Test
-  void testCallHttpServiceWithHeaders() throws Exception {
-    // Test callHttpService setup with custom headers
-
-    // Configure with custom headers
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.setFieldName("result");
-    httpMeta.allocate(0, 2);
-    httpMeta.getHeaderField()[0] = "tokenField";
-    httpMeta.getHeaderField()[1] = "contentTypeField";
-    httpMeta.getHeaderParameter()[0] = "Authorization";
-    httpMeta.getHeaderParameter()[1] = "Content-Type";
-
-    // Set up input row
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("tokenField"));
-    inputRowMeta.addValueMeta(new ValueMetaString("contentTypeField"));
-    Object[] inputRow = new Object[] {"Bearer token123", "application/json"};
-
-    // Set up data
-    httpData.realUrl = TEST_URL;
-    httpData.argnrs = new int[0];
-    httpData.inputRowMeta = inputRowMeta;
-    httpData.useHeaderParameters = true;
-    httpData.headerParametersNrs = new int[] {0, 1};
-
-    // Verify headers are accessible
-    assertEquals("Bearer token123", inputRowMeta.getString(inputRow, 0));
-    assertEquals("application/json", inputRowMeta.getString(inputRow, 1));
-    assertEquals("Authorization", httpMeta.getHeaderParameter()[0]);
-    assertEquals("Content-Type", httpMeta.getHeaderParameter()[1]);
-  }
-
-  @Test
-  void testGetFieldsAddsOutputFields() throws Exception {
-    httpMeta.setFieldName("responseBody");
-    httpMeta.setResultCodeFieldName("statusCode");
-    httpMeta.setResponseTimeFieldName("responseTime");
-    httpMeta.setResponseHeaderFieldName("headers");
-
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("input1"));
-
-    httpMeta.getFields(inputRowMeta, "HTTP", null, null, httpTransform, null);
-
-    assertEquals(5, inputRowMeta.size(), "Should have 5 fields (1 input + 4 
output)");
-    assertNotNull(inputRowMeta.searchValueMeta("responseBody"));
-    assertNotNull(inputRowMeta.searchValueMeta("statusCode"));
-    assertNotNull(inputRowMeta.searchValueMeta("responseTime"));
-    assertNotNull(inputRowMeta.searchValueMeta("headers"));
-  }
-
-  @Test
-  void testProcessRowWithUrlInField() throws Exception {
-    // Configure meta to use URL from field
-    httpMeta.setUrlInField(true);
-    httpMeta.setUrlField("urlColumn");
-    httpMeta.setFieldName("result");
-    httpMeta.allocate(0, 0);
-
-    // Set up input row meta
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("urlColumn"));
-
-    // Create row with URL value
-    Object[] inputRow = new Object[] {"http://example.com/test"};
-
-    // Create spy and mock getRow
-    Http httpSpy = spy(httpTransform);
-    doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
-    doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
-
-    // Initialize
-    httpSpy.init();
-    httpSpy.setInputRowMeta(inputRowMeta);
-
-    // Note: Full execution would require mocking HTTP client
-    // This test verifies the URL field resolution logic
-    httpData.inputRowMeta = inputRowMeta;
-    httpData.indexOfUrlField = inputRowMeta.indexOfValue("urlColumn");
-
-    assertEquals(0, httpData.indexOfUrlField, "URL field index should be 
found");
-  }
-
-  @Test
-  void testProcessRowWithArgumentFields() throws Exception {
-    // Configure meta with argument fields
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.allocate(2, 0);
-    httpMeta.getArgumentField()[0] = "param1";
-    httpMeta.getArgumentField()[1] = "param2";
-    httpMeta.getArgumentParameter()[0] = "key1";
-    httpMeta.getArgumentParameter()[1] = "key2";
-    httpMeta.setFieldName("result");
-
-    // Set up input row meta
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("param1"));
-    inputRowMeta.addValueMeta(new ValueMetaString("param2"));
-
-    // Create row with parameter values
-    Object[] inputRow = new Object[] {"value1", "value2"};
-
-    // Create spy
-    Http httpSpy = spy(httpTransform);
-    doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
-    doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
-
-    // Initialize
-    httpSpy.init();
-    httpSpy.setInputRowMeta(inputRowMeta);
-
-    // Set up data for first row processing
-    httpData.inputRowMeta = inputRowMeta;
-    httpData.argnrs = new int[2];
-    httpData.argnrs[0] = inputRowMeta.indexOfValue("param1");
-    httpData.argnrs[1] = inputRowMeta.indexOfValue("param2");
-
-    assertEquals(0, httpData.argnrs[0]);
-    assertEquals(1, httpData.argnrs[1]);
-  }
-
-  @Test
-  void testProcessRowWithHeaderFields() throws Exception {
-    // Configure meta with header fields
-    httpMeta.setUrl(TEST_URL);
-    httpMeta.allocate(0, 2);
-    httpMeta.getHeaderField()[0] = "authField";
-    httpMeta.getHeaderField()[1] = "contentTypeField";
-    httpMeta.getHeaderParameter()[0] = "Authorization";
-    httpMeta.getHeaderParameter()[1] = "Content-Type";
-    httpMeta.setFieldName("result");
-
-    // Set up input row meta
-    IRowMeta inputRowMeta = new RowMeta();
-    inputRowMeta.addValueMeta(new ValueMetaString("authField"));
-    inputRowMeta.addValueMeta(new ValueMetaString("contentTypeField"));
-
-    // Create row with header values
-    Object[] inputRow = new Object[] {"Bearer token", "application/json"};
-
-    // Create spy
-    Http httpSpy = spy(httpTransform);
-    doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
-    doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
-
-    // Initialize
-    httpSpy.init();
-    httpSpy.setInputRowMeta(inputRowMeta);
-
-    // Set up data
-    httpData.inputRowMeta = inputRowMeta;
-    httpData.headerParametersNrs = new int[2];
-    httpData.headerParametersNrs[0] = inputRowMeta.indexOfValue("authField");
-    httpData.headerParametersNrs[1] = 
inputRowMeta.indexOfValue("contentTypeField");
-
-    assertEquals(0, httpData.headerParametersNrs[0]);
-    assertEquals(1, httpData.headerParametersNrs[1]);
-  }
-}
diff --git a/plugins/transforms/http/src/test/resources/transform.xml 
b/plugins/transforms/http/src/test/resources/transform.xml
new file mode 100644
index 0000000000..0069dc7739
--- /dev/null
+++ b/plugins/transforms/http/src/test/resources/transform.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  ~
+  -->
+<transform>
+    <url>url</url>
+    <urlInField>Y</urlInField>
+    <ignoreSsl>Y</ignoreSsl>
+    <urlField>urlField</urlField>
+    <encoding>UTF-8</encoding>
+    <httpLogin>http-user</httpLogin>
+    <httpPassword>Encrypted 2be98afa01ed382c9bb18bd63c99dbdde</httpPassword>
+    <proxyHost>proxyHost</proxyHost>
+    <proxyPort>proxyPort</proxyPort>
+    <socketTimeout>10000</socketTimeout>
+    <connectionTimeout>10000</connectionTimeout>
+    <closeIdleConnectionsTime>98765</closeIdleConnectionsTime>
+    <lookup>
+        <arg>
+            <name>queryField1</name>
+            <parameter>queryParameter1</parameter>
+        </arg>
+        <arg>
+            <name>queryField2</name>
+            <parameter>queryParameter2</parameter>
+        </arg>
+        <header>
+            <name>headerField1</name>
+            <parameter>headerParameter1</parameter>
+        </header>
+        <header>
+            <name>headerField2</name>
+            <parameter>headerParameter2</parameter>
+        </header>
+        <header>
+            <name>headerField3</name>
+            <parameter>headerParameter3</parameter>
+        </header>
+    </lookup>
+    <result>
+        <name>result</name>
+        <code>httpStatus</code>
+        <response_time>responseTime</response_time>
+        <response_header>responseHeaders</response_header>
+    </result>
+</transform>
+

Reply via email to