This is an automated email from the ASF dual-hosted git repository.

zregvart pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new 925a290  CAMEL-11637: Unable to assign null value to a S...
925a290 is described below

commit 925a290a7b274b5504e59059a2bd4da034f08d45
Author: Zoran Regvart <[email protected]>
AuthorDate: Tue Feb 27 12:58:33 2018 +0100

    CAMEL-11637: Unable to assign null value to a S...
    
    ...alesforce object field
    
    This adds endpoint and incoming message header parameter
    `serializeNulls`, if set to `true` when using JSON data format `null`
    values of data transfer objects will be serialized with the literal
    `null` value.
    
    For example, `Account` DTO with `description` field set to `null` will
    be serialized as:
    
        {
            "name": "Acme",
            ...
            "description": null,
            ...
        }
---
 .../src/main/docs/salesforce-component.adoc        |   6 +-
 .../salesforce/SalesforceEndpointConfig.java       |  17 ++
 .../component/salesforce/api/utils/JsonUtils.java  |  28 +++
 .../internal/processor/AbstractRestProcessor.java  |   4 +-
 .../internal/processor/JsonRestProcessor.java      |  27 ++-
 .../internal/processor/XmlRestProcessor.java       |   4 +-
 .../AbstractRestProcessorApprovalTest.java         |  26 ++-
 .../internal/processor/JsonRestProcessorTest.java  | 105 +++++++--
 .../SalesforceComponentConfiguration.java          | 257 +++++++++++----------
 9 files changed, 305 insertions(+), 169 deletions(-)

diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc
 
b/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc
index 789c56c..67ed69d 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc
@@ -609,7 +609,6 @@ with the following path and query parameters:
 
 ==== Path Parameters (2 parameters):
 
-
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
 | Name | Description | Default | Type
@@ -617,9 +616,7 @@ with the following path and query parameters:
 | *topicName* | The name of the topic to use |  | String
 |===
 
-
-==== Query Parameters (43 parameters):
-
+==== Query Parameters (44 parameters):
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
@@ -652,6 +649,7 @@ with the following path and query parameters:
 | *reportId* (common) | Salesforce1 Analytics report Id |  | String
 | *reportMetadata* (common) | Salesforce1 Analytics report metadata for 
filtering |  | ReportMetadata
 | *resultId* (common) | Bulk API Result ID |  | String
+| *serializeNulls* (common) | Should the NULL values of given DTO be 
serialized with empty (NULL) values. This affects only JSON data format. | 
false | boolean
 | *sObjectBlobFieldName* (common) | SObject blob field name |  | String
 | *sObjectClass* (common) | Fully qualified SObject class name, usually 
generated using camel-salesforce-maven-plugin |  | String
 | *sObjectFields* (common) | SObject fields to retrieve |  | String
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java
 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java
index a23e83d..253857d 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java
@@ -95,6 +95,8 @@ public class SalesforceEndpointConfig implements Cloneable {
 
     public static final String NOT_FOUND_BEHAVIOUR = "notFoundBehaviour";
 
+    public static final String SERIALIZE_NULLS = "serializeNulls";
+
     // general properties
     @UriParam
     private String apiVersion = DEFAULT_VERSION;
@@ -122,6 +124,8 @@ public class SalesforceEndpointConfig implements Cloneable {
     private String sObjectQuery;
     @UriParam(displayName = "SObject Search")
     private String sObjectSearch;
+    @UriParam(displayName = "Serialize NULL values")
+    private boolean serializeNulls;
     @UriParam
     private String apexMethod;
     @UriParam
@@ -339,6 +343,18 @@ public class SalesforceEndpointConfig implements Cloneable 
{
         this.sObjectSearch = sObjectSearch;
     }
 
+    /**
+     * Should the NULL values of given DTO be serialized with
+     * empty (NULL) values. This affects only JSON data format.
+     */
+    public void setSerializeNulls(boolean serializeNulls) {
+        this.serializeNulls = serializeNulls;
+    }
+
+    public boolean isSerializeNulls() {
+        return serializeNulls;
+    }
+
     public String getApexMethod() {
         return apexMethod;
     }
@@ -611,6 +627,7 @@ public class SalesforceEndpointConfig implements Cloneable {
         valueMap.put(SOBJECT_CLASS, sObjectClass);
         valueMap.put(SOBJECT_QUERY, sObjectQuery);
         valueMap.put(SOBJECT_SEARCH, sObjectSearch);
+        valueMap.put(SERIALIZE_NULLS, serializeNulls);
         valueMap.put(APEX_METHOD, apexMethod);
         valueMap.put(APEX_URL, apexUrl);
         valueMap.put(LIMIT, limit);
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
index 415bb61..d3619da 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
@@ -27,10 +27,19 @@ import java.util.stream.Collectors;
 import static java.util.stream.Collectors.joining;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.BeanDescription;
 import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationConfig;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
+import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
+import com.fasterxml.jackson.databind.ser.PropertyWriter;
+import com.fasterxml.jackson.databind.ser.SerializerFactory;
+import com.fasterxml.jackson.databind.ser.std.NullSerializer;
 import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
 import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
 import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
@@ -308,4 +317,23 @@ public abstract class JsonUtils {
         return getJsonSchemaAsSchema(allSchemas, DEFAULT_ID_PREFIX + 
":GlobalObjects");
     }
 
+    public static ObjectMapper withNullSerialization(final ObjectMapper 
objectMapper) {
+        final SerializerFactory factory = BeanSerializerFactory.instance
+            .withSerializerModifier(new BeanSerializerModifier() {
+                @Override
+                public JsonSerializer<?> modifySerializer(final 
SerializationConfig config,
+                    final BeanDescription beanDesc, final JsonSerializer<?> 
serializer) {
+                    for (final PropertyWriter writer : 
(Iterable<PropertyWriter>) serializer::properties) {
+                        if (writer instanceof BeanPropertyWriter) {
+                            ((BeanPropertyWriter) 
writer).assignNullSerializer(NullSerializer.instance);
+                        }
+                    }
+
+                    return serializer;
+                }
+            });
+
+        return objectMapper.copy().setSerializerFactory(factory);
+    }
+
 }
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java
 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java
index b4b9210..b612b7e 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java
@@ -271,7 +271,7 @@ public abstract class AbstractRestProcessor extends 
AbstractSalesforceProcessor
             requestsBody = new ApprovalRequests(request);
         }
 
-        final InputStream request = getRequestStream(requestsBody);
+        final InputStream request = getRequestStream(incomingMessage, 
requestsBody);
 
         restClient.approval(request, determineHeaders(exchange), 
processWithResponseCallback(exchange, callback));
     }
@@ -744,7 +744,7 @@ public abstract class AbstractRestProcessor extends 
AbstractSalesforceProcessor
      *            object to serialize
      * @return stream to read serialized object from
      */
-    protected abstract InputStream getRequestStream(Object object) throws 
SalesforceException;
+    protected abstract InputStream getRequestStream(Message in, Object object) 
throws SalesforceException;
 
     private void setResponseClass(Exchange exchange, String sObjectName) 
throws SalesforceException {
 
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java
 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java
index 07d804f..99a6e55 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java
@@ -30,6 +30,7 @@ import org.apache.camel.AsyncCallback;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.component.salesforce.SalesforceEndpoint;
+import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
 import org.apache.camel.component.salesforce.api.SalesforceException;
 import org.apache.camel.component.salesforce.api.TypeReferences;
 import org.apache.camel.component.salesforce.api.dto.AbstractDTOBase;
@@ -56,7 +57,11 @@ public class JsonRestProcessor extends AbstractRestProcessor 
{
         if (endpoint.getConfiguration().getObjectMapper() != null) {
             this.objectMapper = endpoint.getConfiguration().getObjectMapper();
         } else {
-            this.objectMapper = JsonUtils.createObjectMapper();
+            if (endpoint.getConfiguration().isSerializeNulls()) {
+                this.objectMapper = 
JsonUtils.withNullSerialization(JsonUtils.createObjectMapper());
+            } else {
+                this.objectMapper = JsonUtils.createObjectMapper();
+            }
         }
     }
 
@@ -138,7 +143,7 @@ public class JsonRestProcessor extends 
AbstractRestProcessor {
             AbstractDTOBase dto = in.getBody(AbstractDTOBase.class);
             if (dto != null) {
                 // marshall the DTO
-                request = getRequestStream(dto);
+                request = getRequestStream(in, dto);
             } else {
                 // if all else fails, get body as String
                 final String body = in.getBody(String.class);
@@ -156,10 +161,10 @@ public class JsonRestProcessor extends 
AbstractRestProcessor {
     }
 
     @Override
-    protected InputStream getRequestStream(final Object object) throws 
SalesforceException {
+    protected InputStream getRequestStream(final Message in, final Object 
object) throws SalesforceException {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         try {
-            objectMapper.writeValue(out, object);
+            prepareMapper(in).writeValue(out, object);
         } catch (IOException e) {
             final String msg = "Error marshaling request: " + e.getMessage();
             throw new SalesforceException(msg, e);
@@ -189,11 +194,11 @@ public class JsonRestProcessor extends 
AbstractRestProcessor {
                 final Object response;
                 Class<?> responseClass = exchange.getProperty(RESPONSE_CLASS, 
Class.class);
                 if (!rawPayload && responseClass != null) {
-                    response = objectMapper.readValue(responseEntity, 
responseClass);
+                    response = prepareMapper(in).readValue(responseEntity, 
responseClass);
                 } else {
                     TypeReference<?> responseType = 
exchange.getProperty(RESPONSE_TYPE, TypeReference.class);
                     if (!rawPayload && responseType != null) {
-                        response = objectMapper.readValue(responseEntity, 
responseType);
+                        response = prepareMapper(in).readValue(responseEntity, 
responseType);
                     } else {
                         // return the response as a stream, for getBlobField
                         response = responseEntity;
@@ -222,4 +227,14 @@ public class JsonRestProcessor extends 
AbstractRestProcessor {
         }
 
     }
+
+    private ObjectMapper prepareMapper(final Message in) {
+        final Object serializeNulls = 
in.getHeader(SalesforceEndpointConfig.SERIALIZE_NULLS);
+        if (Boolean.TRUE.equals(serializeNulls)) {
+            return JsonUtils.withNullSerialization(objectMapper);
+        }
+
+        return objectMapper;
+    }
+
 }
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java
 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java
index cbcf881..14919f3 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java
@@ -180,7 +180,7 @@ public class XmlRestProcessor extends AbstractRestProcessor 
{
                 AbstractDTOBase dto = in.getBody(AbstractDTOBase.class);
                 if (dto != null) {
                     // marshall the DTO
-                    request = getRequestStream(dto);
+                    request = getRequestStream(in, dto);
                 } else {
                     // if all else fails, get body as String
                     final String body = in.getBody(String.class);
@@ -204,7 +204,7 @@ public class XmlRestProcessor extends AbstractRestProcessor 
{
     }
 
     @Override
-    protected InputStream getRequestStream(final Object object) throws 
SalesforceException {
+    protected InputStream getRequestStream(final Message in, final Object 
object) throws SalesforceException {
         final XStream localXStream = xStream.get();
         // first process annotations on the class, for things like alias, etc.
         localXStream.processAnnotations(object.getClass());
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessorApprovalTest.java
 
b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessorApprovalTest.java
index fd66d90..8372263 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessorApprovalTest.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessorApprovalTest.java
@@ -41,6 +41,8 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -78,7 +80,7 @@ public class AbstractRestProcessorApprovalTest {
         }
 
         @Override
-        protected InputStream getRequestStream(final Object object) throws 
SalesforceException {
+        protected InputStream getRequestStream(final Message in, final Object 
object) throws SalesforceException {
             return null;
         }
 
@@ -113,7 +115,7 @@ public class AbstractRestProcessorApprovalTest {
 
         final TestRestProcessor processor = sendBodyAndHeader(approvalRequest, 
template);
 
-        verify(processor).getRequestStream(new 
ApprovalRequests(approvalRequest.applyTemplate(template)));
+        verify(processor).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(approvalRequest.applyTemplate(template))));
     }
 
     @Test
@@ -130,8 +132,8 @@ public class AbstractRestProcessorApprovalTest {
         final TestRestProcessor processor = 
sendBodyAndHeader(Arrays.asList(approvalRequest1, approvalRequest2),
                 template);
 
-        verify(processor).getRequestStream(new ApprovalRequests(
-                Arrays.asList(approvalRequest1.applyTemplate(template), 
approvalRequest2.applyTemplate(template))));
+        verify(processor).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(
+                Arrays.asList(approvalRequest1.applyTemplate(template), 
approvalRequest2.applyTemplate(template)))));
     }
 
     @Test
@@ -165,7 +167,7 @@ public class AbstractRestProcessorApprovalTest {
 
         final TestRestProcessor processor = sendBody(approvalRequest);
 
-        verify(processor).getRequestStream(new 
ApprovalRequests(approvalRequest));
+        verify(processor).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(approvalRequest)));
     }
 
     @Test
@@ -174,7 +176,7 @@ public class AbstractRestProcessorApprovalTest {
         request.setComments("hi there");
         final TestRestProcessor processor = sendBodyAndHeader(null, request);
 
-        verify(processor).getRequestStream(new ApprovalRequests(request));
+        verify(processor).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(request)));
     }
 
     @Test
@@ -189,7 +191,7 @@ public class AbstractRestProcessorApprovalTest {
         combined.setComments("hi there");
         combined.setContextId("context-id");
 
-        verify(processor).getRequestStream(new ApprovalRequests(combined));
+        verify(processor).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(combined)));
     }
 
     @Test
@@ -202,7 +204,7 @@ public class AbstractRestProcessorApprovalTest {
 
         final TestRestProcessor processor = 
sendBody(Arrays.asList(approvalRequest1, approvalRequest2));
 
-        verify(processor).getRequestStream(new 
ApprovalRequests(Arrays.asList(approvalRequest1, approvalRequest2)));
+        verify(processor).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(Arrays.asList(approvalRequest1, approvalRequest2))));
     }
 
     @Test
@@ -215,7 +217,7 @@ public class AbstractRestProcessorApprovalTest {
         final ApprovalRequest request = new ApprovalRequest();
         request.setContextId("contextId");
 
-        verify(processor).getRequestStream(new ApprovalRequests(request));
+        verify(processor).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(request)));
     }
 
     @Test
@@ -230,13 +232,13 @@ public class AbstractRestProcessorApprovalTest {
 
         final TestRestProcessor processor1 = sendBodyAndHeaders(null, 
template, null);
 
-        verify(processor1).getRequestStream(new 
ApprovalRequests(requestWithComment("third priority")));
+        verify(processor1).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(requestWithComment("third priority"))));
 
         final TestRestProcessor processor2 = sendBodyAndHeaders(null, 
template, headers);
-        verify(processor2).getRequestStream(new 
ApprovalRequests(requestWithComment("second priority")));
+        verify(processor2).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(requestWithComment("second priority"))));
 
         final TestRestProcessor processor3 = sendBodyAndHeaders(body, 
template, headers);
-        verify(processor3).getRequestStream(new 
ApprovalRequests(requestWithComment("first priority")));
+        verify(processor3).getRequestStream(any(Message.class), eq(new 
ApprovalRequests(requestWithComment("first priority"))));
     }
 
     TestRestProcessor sendBody(final Object body) throws SalesforceException {
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessorTest.java
 
b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessorTest.java
index 97b7b4d..2f54860 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessorTest.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessorTest.java
@@ -16,55 +16,118 @@
  */
 package org.apache.camel.component.salesforce.internal.processor;
 
-import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
+
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
+import org.apache.camel.Message;
 import org.apache.camel.component.salesforce.SalesforceComponent;
 import org.apache.camel.component.salesforce.SalesforceEndpoint;
 import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
+import org.apache.camel.component.salesforce.api.SalesforceException;
 import org.apache.camel.component.salesforce.api.dto.AbstractDTOBase;
 import org.apache.camel.component.salesforce.internal.OperationName;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.impl.DefaultExchange;
+import org.apache.camel.impl.DefaultMessage;
 import org.apache.commons.io.IOUtils;
-import org.hamcrest.core.Is;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.assertj.core.api.Assertions.assertThat;
 
 public class JsonRestProcessorTest {
 
+    static class TestObject extends AbstractDTOBase {
+
+        private ZonedDateTime creationDate;
+
+        public ZonedDateTime getCreationDate() {
+            return creationDate;
+        }
+
+        public void setCreationDate(final ZonedDateTime creationDate) {
+            this.creationDate = creationDate;
+        }
+    }
+
+    @Test
+    public void byDefaultItShouldNotSerializeNullValues() throws 
SalesforceException, IOException {
+        final SalesforceComponent salesforce = new SalesforceComponent();
+        final SalesforceEndpointConfig configuration = new 
SalesforceEndpointConfig();
+        final SalesforceEndpoint endpoint = new SalesforceEndpoint("", 
salesforce, configuration,
+            OperationName.UPDATE_SOBJECT, "");
+
+        final JsonRestProcessor jsonProcessor = new 
JsonRestProcessor(endpoint);
+
+        final Message in = new DefaultMessage(new DefaultCamelContext());
+        try (InputStream stream = jsonProcessor.getRequestStream(in, new 
TestObject());
+            InputStreamReader reader = new InputStreamReader(stream, 
StandardCharsets.UTF_8)) {
+            final String json = IOUtils.toString(reader);
+
+            assertThat(json).isEqualTo("{}");
+        }
+    }
+
     @Test
     public void getRequestStream() throws Exception {
-        SalesforceComponent comp = new SalesforceComponent();
-        SalesforceEndpointConfig conf = new SalesforceEndpointConfig();
-        OperationName op = OperationName.CREATE_BATCH;
-        SalesforceEndpoint endpoint = new SalesforceEndpoint("", comp, conf, 
op, "");
-        JsonRestProcessor jsonRestProcessor = new JsonRestProcessor(endpoint);
-        DefaultCamelContext context = new DefaultCamelContext();
-        Exchange exchange = new DefaultExchange(context, 
ExchangePattern.InOut);
-        TestObject doc = new TestObject();
+        final SalesforceComponent comp = new SalesforceComponent();
+        final SalesforceEndpointConfig conf = new SalesforceEndpointConfig();
+        final OperationName op = OperationName.CREATE_BATCH;
+        final SalesforceEndpoint endpoint = new SalesforceEndpoint("", comp, 
conf, op, "");
+        final JsonRestProcessor jsonRestProcessor = new 
JsonRestProcessor(endpoint);
+        final DefaultCamelContext context = new DefaultCamelContext();
+        final Exchange exchange = new DefaultExchange(context, 
ExchangePattern.InOut);
+        final TestObject doc = new TestObject();
         doc.setCreationDate(ZonedDateTime.of(1717, 1, 2, 3, 4, 5, 6, 
ZoneId.systemDefault()));
 
         exchange.getIn().setBody(doc);
-        ByteArrayInputStream is = (ByteArrayInputStream) 
jsonRestProcessor.getRequestStream(exchange);
-        String result = IOUtils.toString(is);
-        assertThat(result, result.length() <= 48, Is.is(true));
+        try (InputStream stream = jsonRestProcessor.getRequestStream(exchange);
+            InputStreamReader reader = new InputStreamReader(stream, 
StandardCharsets.UTF_8)) {
+            final String result = IOUtils.toString(reader);
+            assertThat(result.length()).isLessThanOrEqualTo(48);
+        }
     }
 
-    static class TestObject extends AbstractDTOBase {
+    @Test
+    public void shouldSerializeNullValuesViaEndpointConfiguration() throws 
SalesforceException, IOException {
+        final SalesforceComponent salesforce = new SalesforceComponent();
+        final SalesforceEndpointConfig configuration = new 
SalesforceEndpointConfig();
+        configuration.setSerializeNulls(true);
+        final SalesforceEndpoint endpoint = new SalesforceEndpoint("", 
salesforce, configuration,
+            OperationName.UPDATE_SOBJECT, "");
 
-        private ZonedDateTime creationDate;
+        final JsonRestProcessor jsonProcessor = new 
JsonRestProcessor(endpoint);
 
-        public ZonedDateTime getCreationDate() {
-            return creationDate;
-        }
+        final Message in = new DefaultMessage(new DefaultCamelContext());
+        try (InputStream stream = jsonProcessor.getRequestStream(in, new 
TestObject());
+            InputStreamReader reader = new InputStreamReader(stream, 
StandardCharsets.UTF_8)) {
+            final String json = IOUtils.toString(reader);
 
-        public void setCreationDate(ZonedDateTime creationDate) {
-            this.creationDate = creationDate;
+            assertThat(json).isEqualTo("{\"creationDate\":null}");
         }
     }
 
+    @Test
+    public void shouldSerializeNullValuesViaHeaderConfiguration() throws 
SalesforceException, IOException {
+        final SalesforceComponent salesforce = new SalesforceComponent();
+        final SalesforceEndpointConfig configuration = new 
SalesforceEndpointConfig();
+        final SalesforceEndpoint endpoint = new SalesforceEndpoint("", 
salesforce, configuration,
+            OperationName.UPDATE_SOBJECT, "");
+
+        final JsonRestProcessor jsonProcessor = new 
JsonRestProcessor(endpoint);
+
+        final Message in = new DefaultMessage(new DefaultCamelContext());
+        in.setHeader(SalesforceEndpointConfig.SERIALIZE_NULLS, true);
+        try (InputStream stream = jsonProcessor.getRequestStream(in, new 
TestObject());
+            InputStreamReader reader = new InputStreamReader(stream, 
StandardCharsets.UTF_8)) {
+            final String json = IOUtils.toString(reader);
+
+            assertThat(json).isEqualTo("{\"creationDate\":null}");
+        }
+    }
 }
\ No newline at end of file
diff --git 
a/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java
 
b/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java
index 13aacea..46b0312 100644
--- 
a/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java
+++ 
b/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java
@@ -450,128 +450,6 @@ public class SalesforceComponentConfiguration
         this.resolvePropertyPlaceholders = resolvePropertyPlaceholders;
     }
 
-    public static class SalesforceLoginConfigNestedConfiguration {
-        public static final Class CAMEL_NESTED_CLASS = 
org.apache.camel.component.salesforce.SalesforceLoginConfig.class;
-        private String instanceUrl;
-        /**
-         * Salesforce login URL, defaults to https://login.salesforce.com
-         */
-        private String loginUrl;
-        /**
-         * Salesforce connected application Consumer Key
-         */
-        private String clientId;
-        /**
-         * Salesforce connected application Consumer Secret
-         */
-        private String clientSecret;
-        /**
-         * Keystore parameters for keystore containing certificate and private
-         * key needed for OAuth 2.0 JWT Bearer Token Flow.
-         */
-        @NestedConfigurationProperty
-        private KeyStoreParameters keystore;
-        /**
-         * Salesforce connected application Consumer token
-         */
-        private String refreshToken;
-        private AuthenticationType type;
-        /**
-         * Salesforce account user name
-         */
-        private String userName;
-        /**
-         * Salesforce account password
-         */
-        private String password;
-        /**
-         * Flag to enable/disable lazy OAuth, default is false. When enabled,
-         * OAuth token retrieval or generation is not done until the first API
-         * call
-         */
-        private Boolean lazyLogin;
-
-        public String getInstanceUrl() {
-            return instanceUrl;
-        }
-
-        public void setInstanceUrl(String instanceUrl) {
-            this.instanceUrl = instanceUrl;
-        }
-
-        public String getLoginUrl() {
-            return loginUrl;
-        }
-
-        public void setLoginUrl(String loginUrl) {
-            this.loginUrl = loginUrl;
-        }
-
-        public String getClientId() {
-            return clientId;
-        }
-
-        public void setClientId(String clientId) {
-            this.clientId = clientId;
-        }
-
-        public String getClientSecret() {
-            return clientSecret;
-        }
-
-        public void setClientSecret(String clientSecret) {
-            this.clientSecret = clientSecret;
-        }
-
-        public KeyStoreParameters getKeystore() {
-            return keystore;
-        }
-
-        public void setKeystore(KeyStoreParameters keystore) {
-            this.keystore = keystore;
-        }
-
-        public String getRefreshToken() {
-            return refreshToken;
-        }
-
-        public void setRefreshToken(String refreshToken) {
-            this.refreshToken = refreshToken;
-        }
-
-        public AuthenticationType getType() {
-            return type;
-        }
-
-        public void setType(AuthenticationType type) {
-            this.type = type;
-        }
-
-        public String getUserName() {
-            return userName;
-        }
-
-        public void setUserName(String userName) {
-            this.userName = userName;
-        }
-
-        public String getPassword() {
-            return password;
-        }
-
-        public void setPassword(String password) {
-            this.password = password;
-        }
-
-        public Boolean getLazyLogin() {
-            return lazyLogin;
-        }
-
-        public void setLazyLogin(Boolean lazyLogin) {
-            this.lazyLogin = lazyLogin;
-        }
-    }
-
     public static class SalesforceEndpointConfigNestedConfiguration {
         public static final Class CAMEL_NESTED_CLASS = 
org.apache.camel.component.salesforce.SalesforceEndpointConfig.class;
         /**
@@ -628,6 +506,11 @@ public class SalesforceComponentConfiguration
          */
         private String sObjectSearch;
         /**
+         * Should the NULL values of given DTO be serialized with empty (NULL)
+         * values. This affects only JSON data format.
+         */
+        private Boolean serializeNulls = false;
+        /**
          * APEX method name
          */
         private String apexMethod;
@@ -901,6 +784,14 @@ public class SalesforceComponentConfiguration
             this.sObjectSearch = sObjectSearch;
         }
 
+        public Boolean getSerializeNulls() {
+            return serializeNulls;
+        }
+
+        public void setSerializeNulls(Boolean serializeNulls) {
+            this.serializeNulls = serializeNulls;
+        }
+
         public String getApexMethod() {
             return apexMethod;
         }
@@ -1177,4 +1068,126 @@ public class SalesforceComponentConfiguration
             this.notFoundBehaviour = notFoundBehaviour;
         }
     }
+
+    public static class SalesforceLoginConfigNestedConfiguration {
+        public static final Class CAMEL_NESTED_CLASS = 
org.apache.camel.component.salesforce.SalesforceLoginConfig.class;
+        private String instanceUrl;
+        /**
+         * Salesforce login URL, defaults to https://login.salesforce.com
+         */
+        private String loginUrl;
+        /**
+         * Salesforce connected application Consumer Key
+         */
+        private String clientId;
+        /**
+         * Salesforce connected application Consumer Secret
+         */
+        private String clientSecret;
+        /**
+         * Keystore parameters for keystore containing certificate and private
+         * key needed for OAuth 2.0 JWT Bearer Token Flow.
+         */
+        @NestedConfigurationProperty
+        private KeyStoreParameters keystore;
+        /**
+         * Salesforce connected application Consumer token
+         */
+        private String refreshToken;
+        private AuthenticationType type;
+        /**
+         * Salesforce account user name
+         */
+        private String userName;
+        /**
+         * Salesforce account password
+         */
+        private String password;
+        /**
+         * Flag to enable/disable lazy OAuth, default is false. When enabled,
+         * OAuth token retrieval or generation is not done until the first API
+         * call
+         */
+        private Boolean lazyLogin;
+
+        public String getInstanceUrl() {
+            return instanceUrl;
+        }
+
+        public void setInstanceUrl(String instanceUrl) {
+            this.instanceUrl = instanceUrl;
+        }
+
+        public String getLoginUrl() {
+            return loginUrl;
+        }
+
+        public void setLoginUrl(String loginUrl) {
+            this.loginUrl = loginUrl;
+        }
+
+        public String getClientId() {
+            return clientId;
+        }
+
+        public void setClientId(String clientId) {
+            this.clientId = clientId;
+        }
+
+        public String getClientSecret() {
+            return clientSecret;
+        }
+
+        public void setClientSecret(String clientSecret) {
+            this.clientSecret = clientSecret;
+        }
+
+        public KeyStoreParameters getKeystore() {
+            return keystore;
+        }
+
+        public void setKeystore(KeyStoreParameters keystore) {
+            this.keystore = keystore;
+        }
+
+        public String getRefreshToken() {
+            return refreshToken;
+        }
+
+        public void setRefreshToken(String refreshToken) {
+            this.refreshToken = refreshToken;
+        }
+
+        public AuthenticationType getType() {
+            return type;
+        }
+
+        public void setType(AuthenticationType type) {
+            this.type = type;
+        }
+
+        public String getUserName() {
+            return userName;
+        }
+
+        public void setUserName(String userName) {
+            this.userName = userName;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+
+        public void setPassword(String password) {
+            this.password = password;
+        }
+
+        public Boolean getLazyLogin() {
+            return lazyLogin;
+        }
+
+        public void setLazyLogin(Boolean lazyLogin) {
+            this.lazyLogin = lazyLogin;
+        }
+    }
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to