You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by zr...@apache.org on 2017/03/07 20:24:13 UTC

[5/6] camel git commit: CAMEL-10857 Make Salesforce integration tests w...

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java
index ddfa832..716fda1 100644
--- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java
@@ -16,20 +16,19 @@
  */
 package org.apache.camel.component.salesforce;
 
-import java.io.FileOutputStream;
 import java.io.InputStream;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 
 import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Processor;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.salesforce.api.SalesforceException;
 import org.apache.camel.component.salesforce.api.SalesforceMultipleChoicesException;
@@ -47,7 +46,7 @@ import org.apache.camel.component.salesforce.dto.generated.Document;
 import org.apache.camel.component.salesforce.dto.generated.Line_Item__c;
 import org.apache.camel.component.salesforce.dto.generated.Merchandise__c;
 import org.apache.camel.component.salesforce.dto.generated.QueryRecordsLine_Item__c;
-import org.apache.camel.component.salesforce.dto.generated.Tasks__c;
+import org.apache.camel.component.salesforce.dto.generated.Task;
 import org.apache.camel.util.jsse.SSLContextParameters;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
@@ -55,232 +54,126 @@ import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Ignore;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@Category(Standalone.class)
+@RunWith(Parameterized.class)
 public class RestApiIntegrationTest extends AbstractSalesforceTestBase {
 
-    private static final Logger LOG = LoggerFactory.getLogger(RestApiIntegrationTest.class);
-    private static final String TEST_LINE_ITEM_ID = "1";
-    private static final String NEW_LINE_ITEM_ID = "100";
-    private static final String TEST_DOCUMENT_ID = "Test Document";
-
-    private static String testId;
-
-    @Test
-    public void testRetry() throws Exception {
-        SalesforceComponent sf = context().getComponent("salesforce", SalesforceComponent.class);
-        String accessToken = sf.getSession().getAccessToken();
-
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setSslContext(new SSLContextParameters().createSSLContext(context));
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.setConnectTimeout(60000);
-        httpClient.start();
-
-        String uri = sf.getLoginConfig().getLoginUrl() + "/services/oauth2/revoke?token=" + accessToken;
-        Request logoutGet = httpClient.newRequest(uri)
-                .method(HttpMethod.GET)
-                .timeout(1, TimeUnit.MINUTES);
-
-        ContentResponse response = logoutGet.send();
-        assertEquals(HttpStatus.OK_200, response.getStatus());
-
-        doTestGetGlobalObjects("");
-    }
-
-    @Test
-    public void testRetryFailure() throws Exception {
-        SalesforceComponent sf = context().getComponent("salesforce", SalesforceComponent.class);
-        String accessToken = sf.getSession().getAccessToken();
-
-        SslContextFactory sslContextFactory = new SslContextFactory();
-        sslContextFactory.setSslContext(new SSLContextParameters().createSSLContext());
-        HttpClient httpClient = new HttpClient(sslContextFactory);
-        httpClient.setConnectTimeout(60000);
-        httpClient.start();
-
-        String uri = sf.getLoginConfig().getLoginUrl() + "/services/oauth2/revoke?token=" + accessToken;
-        Request logoutGet = httpClient.newRequest(uri)
-                .method(HttpMethod.GET)
-                .timeout(1, TimeUnit.MINUTES);
-
-        ContentResponse response = logoutGet.send();
-        assertEquals(HttpStatus.OK_200, response.getStatus());
-
-        // set component config to bad password to cause relogin attempts to fail
-        final String password = sf.getLoginConfig().getPassword();
-        sf.getLoginConfig().setPassword("bad_password");
+    /**
+     * Request DTO for Salesforce APEX REST calls. See
+     * https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_rest_methods.htm.
+     */
+    @XStreamAlias("request")
+    public static class MerchandiseRequest extends AbstractDTOBase {
+        private Merchandise__c merchandise;
 
-        try {
-            doTestGetGlobalObjects("");
-            fail("Expected CamelExecutionException!");
-        } catch (CamelExecutionException e) {
-            if (e.getCause() instanceof SalesforceException) {
-                SalesforceException cause = (SalesforceException) e.getCause();
-                assertEquals("Expected 400 on authentication retry failure", HttpStatus.BAD_REQUEST_400, cause.getStatusCode());
-            } else {
-                fail("Expected SalesforceException!");
-            }
-        } finally {
-            // reset password and retries to allow other tests to pass
-            sf.getLoginConfig().setPassword(password);
+        public MerchandiseRequest(final Merchandise__c merchandise) {
+            this.merchandise = merchandise;
         }
-    }
 
-    @Test
-    public void testGetVersions() throws Exception {
-        doTestGetVersions("");
-        doTestGetVersions("Xml");
-    }
-
-    @SuppressWarnings("unchecked")
-    private void doTestGetVersions(String suffix) throws Exception {
-        // test getVersions doesn't need a body
-        // assert expected result
-        Object o = template().requestBody("direct:getVersions" + suffix, (Object) null);
-        List<Version> versions = null;
-        if (o instanceof Versions) {
-            versions = ((Versions) o).getVersions();
-        } else {
-            versions = (List<Version>) o;
+        public Merchandise__c getMerchandise() {
+            return merchandise;
         }
-        assertNotNull(versions);
-        LOG.debug("Versions: {}", versions);
-    }
 
-    @Test
-    public void testGetResources() throws Exception {
-        doTestGetResources("");
-        doTestGetResources("Xml");
+        public void setMerchandise(final Merchandise__c merchandise) {
+            this.merchandise = merchandise;
+        }
     }
 
-    private void doTestGetResources(String suffix) throws Exception {
-
+    /**
+     * Response DTO for Salesforce APEX REST calls. See
+     * https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_rest_methods.htm.
+     */
+    @XStreamAlias("response")
+    public static class MerchandiseResponse extends Merchandise__c {
+        // XML response contains a type string with the SObject type name
+        private String type;
 
-        RestResources resources = template().requestBody("direct:getResources" + suffix, null, RestResources.class);
-        assertNotNull(resources);
-        LOG.debug("Resources: {}", resources);
-    }
+        public String getType() {
+            return type;
+        }
 
-    @Test
-    public void testGetGlobalObjects() throws Exception {
-        doTestGetGlobalObjects("");
-        doTestGetGlobalObjects("Xml");
+        public void setType(final String type) {
+            this.type = type;
+        }
     }
 
-    private void doTestGetGlobalObjects(String suffix) throws Exception {
+    private static final AtomicInteger NEW_LINE_ITEM_ID = new AtomicInteger(100);
 
+    private static final String TEST_DOCUMENT_ID = "Test Document";
 
-        GlobalObjects globalObjects = template().requestBody("direct:getGlobalObjects" + suffix, null, GlobalObjects.class);
-        assertNotNull(globalObjects);
-        LOG.debug("GlobalObjects: {}", globalObjects);
-    }
+    private static final AtomicInteger TEST_LINE_ITEM_ID = new AtomicInteger(1);
 
-    @Test
-    public void testGetBasicInfo() throws Exception {
-        doTestGetBasicInfo("");
-        doTestGetBasicInfo("Xml");
-    }
+    @Parameter
+    public String format;
 
-    private void doTestGetBasicInfo(String suffix) throws Exception {
-        SObjectBasicInfo objectBasicInfo = template().requestBody("direct:getBasicInfo" + suffix, null, SObjectBasicInfo.class);
-        assertNotNull(objectBasicInfo);
-        LOG.debug("SObjectBasicInfo: {}", objectBasicInfo);
+    private String testId;
 
-        // set test Id for testGetSObject
-        assertFalse("RecentItems is empty", objectBasicInfo.getRecentItems().isEmpty());
-        testId = objectBasicInfo.getRecentItems().get(0).getId();
+    @After
+    public void removeData() {
+        template.request("salesforce:deleteSObject?sObjectName=Merchandise__c&sObjectId=" + testId, (Processor) (e) -> {
+            // NOOP
+        });
+        template.request("direct:deleteLineItems", (Processor) (e) -> {
+            // NOOP
+        });
     }
 
-    @Test
-    public void testGetDescription() throws Exception {
-        doTestGetDescription("");
-        doTestGetDescription("Xml");
-    }
-
-    private void doTestGetDescription(String suffix) throws Exception {
+    @Before
+    public void setupData() {
+        final Merchandise__c merchandise = new Merchandise__c();
+        merchandise.setName("Test Merchandise");
+        merchandise.setPrice__c(10.0);
+        merchandise.setTotal_Inventory__c(100.0);
+        final CreateSObjectResult result = template().requestBody("salesforce:createSObject", merchandise,
+            CreateSObjectResult.class);
 
-
-        SObjectDescription sObjectDescription = template().requestBody("direct:getDescription" + suffix, null, SObjectDescription.class);
-        assertNotNull(sObjectDescription);
-        LOG.debug("SObjectDescription: {}", sObjectDescription);
+        testId = result.getId();
     }
 
     @Test
-    public void testGetSObject() throws Exception {
-        doTestGetSObject("");
-        doTestGetSObject("Xml");
-    }
-
-    private void doTestGetSObject(String suffix) throws Exception {
-        if (testId == null) {
-            // execute getBasicInfo to get test id from recent items
-            doTestGetBasicInfo("");
-        }
-
-        Merchandise__c merchandise = template().requestBody("direct:getSObject" + suffix, testId, Merchandise__c.class);
+    public void testApexCall() throws Exception {
+        // request merchandise with id in URI template
+        Merchandise__c merchandise = template().requestBodyAndHeader("direct:apexCallGet", null, "id", testId,
+            Merchandise__c.class);
         assertNotNull(merchandise);
-        if (suffix.isEmpty()) {
-            assertNull(merchandise.getTotal_Inventory__c());
-            assertNotNull(merchandise.getPrice__c());
-        } else {
-            assertNotNull(merchandise.getTotal_Inventory__c());
-            assertNull(merchandise.getPrice__c());
-        }
-        LOG.debug("SObjectById: {}", merchandise);
-    }
-
-    @Test
-    @Ignore("Depends on a Task object with a datetime field")
-    public void testCreateUpdateDeleteTasks() throws Exception {
-        doTestCreateUpdateDeleteTasks("");
-        doTestCreateUpdateDeleteTasks("Xml");
-    }
 
-    private void doTestCreateUpdateDeleteTasks(String suffix) throws Exception {
-        Tasks__c taken = new Tasks__c();
-        taken.setName("Task1");
-        taken.setStart__c(ZonedDateTime.of(1700, 1, 2, 3, 4, 5, 6, ZoneId.systemDefault()));
-        CreateSObjectResult result = template().requestBody("direct:createSObject" + suffix,
-                taken, CreateSObjectResult.class);
-        assertNotNull(result);
-        assertTrue("Create success", result.getSuccess());
-        LOG.debug("Create: " + result);
-
-        // test JSON update
-        // make the plane cheaper
-        taken.setId(result.getId());
-        taken.setStart__c(ZonedDateTime.of(1991, 1, 2, 3, 4, 5, 6, ZoneId.systemDefault()));
+        // request merchandise with id as query param
+        merchandise = template().requestBodyAndHeader("direct:apexCallGetWithId", null,
+            SalesforceEndpointConfig.APEX_QUERY_PARAM_PREFIX + "id", testId, Merchandise__c.class);
+        assertNotNull(merchandise);
 
-        assertNull(template().requestBodyAndHeader("direct:updateSObject" + suffix,
-                taken, SalesforceEndpointConfig.SOBJECT_ID, result.getId()));
-        LOG.debug("Update successful");
+        // patch merchandise
+        // clear fields that won't be modified
+        merchandise.clearBaseFields();
+        merchandise.setId(testId);
+        merchandise.setPrice__c(null);
+        merchandise.setTotal_Inventory__c(null);
 
-        // delete the newly created SObject
-        assertNull(template().requestBody("direct:deleteSObjectTaken" + suffix, result.getId()));
-        LOG.debug("Delete successful");
+        merchandise = template().requestBody("direct:apexCallPatch", new MerchandiseRequest(merchandise),
+            Merchandise__c.class);
+        assertNotNull(merchandise);
     }
 
     @Test
     public void testCreateUpdateDelete() throws Exception {
-        doTestCreateUpdateDelete("");
-        doTestCreateUpdateDelete("Xml");
-    }
-
-    private void doTestCreateUpdateDelete(String suffix) throws Exception {
-        Merchandise__c merchandise = new Merchandise__c();
+        final Merchandise__c merchandise = new Merchandise__c();
         merchandise.setName("Wee Wee Wee Plane");
         merchandise.setDescription__c("Microlite plane");
         merchandise.setPrice__c(2000.0);
         merchandise.setTotal_Inventory__c(50.0);
-        CreateSObjectResult result = template().requestBody("direct:createSObject" + suffix,
-                merchandise, CreateSObjectResult.class);
+        final CreateSObjectResult result = template().requestBody("direct:createSObject", merchandise,
+            CreateSObjectResult.class);
         assertNotNull(result);
         assertTrue("Create success", result.getSuccess());
-        LOG.debug("Create: " + result);
 
         // test JSON update
         // make the plane cheaper
@@ -290,50 +183,61 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase {
         // also need to set the Id
         merchandise.setId(result.getId());
 
-        assertNull(template().requestBodyAndHeader("direct:updateSObject" + suffix,
-                merchandise, SalesforceEndpointConfig.SOBJECT_ID, result.getId()));
-        LOG.debug("Update successful");
+        assertNull(template().requestBodyAndHeader("direct:updateSObject", merchandise,
+            SalesforceEndpointConfig.SOBJECT_ID, result.getId()));
 
         // delete the newly created SObject
-        assertNull(template().requestBody("direct:deleteSObject" + suffix, result.getId()));
-        LOG.debug("Delete successful");
+        assertNull(template().requestBody("direct:deleteSObject", result.getId()));
     }
 
     @Test
-    public void testCreateUpdateDeleteWithId() throws Exception {
-        doTestCreateUpdateDeleteWithId("");
-        doTestCreateUpdateDeleteWithId("Xml");
-    }
+    public void testCreateUpdateDeleteTasks() throws Exception {
+        final Task taken = new Task();
+        taken.setDescription("Task1");
+        taken.setActivityDate(ZonedDateTime.of(1700, 1, 2, 3, 4, 5, 6, ZoneId.systemDefault()));
+        final CreateSObjectResult result = template().requestBody("direct:createSObject", taken,
+            CreateSObjectResult.class);
+        assertNotNull(result);
+        assertTrue("Create success", result.getSuccess());
 
-    private void doTestCreateUpdateDeleteWithId(String suffix) throws Exception {
-        template().request("direct:deleteLineItems", null);
+        // test JSON update
+        // make the plane cheaper
+        taken.setId(result.getId());
+        taken.setActivityDate(ZonedDateTime.of(1991, 1, 2, 3, 4, 5, 6, ZoneId.systemDefault()));
 
+        assertNull(template().requestBodyAndHeader("direct:updateSObject", taken, SalesforceEndpointConfig.SOBJECT_ID,
+            result.getId()));
+
+        // delete the newly created SObject
+        assertNull(template().requestBody("direct:deleteSObjectTaken", result.getId()));
+    }
+
+    @Test
+    public void testCreateUpdateDeleteWithId() throws Exception {
         Line_Item__c lineItem = new Line_Item__c();
-        lineItem.setName("1");
-        CreateSObjectResult result = template().requestBody("direct:createLineItem",
-                lineItem, CreateSObjectResult.class);
+        final String lineItemId = String.valueOf(TEST_LINE_ITEM_ID.incrementAndGet());
+        lineItem.setName(lineItemId);
+        CreateSObjectResult result = template().requestBody("direct:createLineItem", lineItem,
+            CreateSObjectResult.class);
         assertNotNull(result);
         assertTrue(result.getSuccess());
 
         // get line item with Name 1
-        lineItem = template().requestBody("direct:getSObjectWithId" + suffix, TEST_LINE_ITEM_ID,
-                Line_Item__c.class);
+        lineItem = template().requestBody("direct:getSObjectWithId", lineItemId, Line_Item__c.class);
         assertNotNull(lineItem);
-        LOG.debug("GetWithId: {}", lineItem);
 
         // test insert with id
         // set the unit price and sold
         lineItem.setUnit_Price__c(1000.0);
         lineItem.setUnits_Sold__c(50.0);
         // update line item with Name NEW_LINE_ITEM_ID
-        lineItem.setName(NEW_LINE_ITEM_ID);
+        final String newLineItemId = String.valueOf(NEW_LINE_ITEM_ID.incrementAndGet());
+        lineItem.setName(newLineItemId);
 
-        result = template().requestBodyAndHeader("direct:upsertSObject" + suffix,
-                lineItem, SalesforceEndpointConfig.SOBJECT_EXT_ID_VALUE, NEW_LINE_ITEM_ID,
-                CreateSObjectResult.class);
+        result = template().requestBodyAndHeader("direct:upsertSObject", lineItem,
+            SalesforceEndpointConfig.SOBJECT_EXT_ID_VALUE, newLineItemId, CreateSObjectResult.class);
         assertNotNull(result);
         assertTrue(result.getSuccess());
-        LOG.debug("CreateWithId: {}", result);
 
         // clear read only parent type fields
         lineItem.setInvoice_Statement__c(null);
@@ -342,204 +246,184 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase {
         lineItem.setUnits_Sold__c(25.0);
 
         // update line item with Name NEW_LINE_ITEM_ID
-        result = template().requestBodyAndHeader("direct:upsertSObject" + suffix,
-                lineItem, SalesforceEndpointConfig.SOBJECT_EXT_ID_VALUE, NEW_LINE_ITEM_ID,
-                CreateSObjectResult.class);
+        result = template().requestBodyAndHeader("direct:upsertSObject", lineItem,
+            SalesforceEndpointConfig.SOBJECT_EXT_ID_VALUE, newLineItemId, CreateSObjectResult.class);
         assertNull(result);
-        LOG.debug("UpdateWithId: {}", result);
 
         // delete the SObject with Name NEW_LINE_ITEM_ID
-        assertNull(template().requestBody("direct:deleteSObjectWithId" + suffix, NEW_LINE_ITEM_ID));
-        LOG.debug("DeleteWithId successful");
+        assertNull(template().requestBody("direct:deleteSObjectWithId", newLineItemId));
     }
 
     @Test
-    public void testGetBlobField() throws Exception {
-        SalesforceComponent component = context().getComponent("salesforce", SalesforceComponent.class);
-        try {
-            doTestGetBlobField("");
-            doTestGetBlobField("Xml");
-        } finally {
-            // reset response content buffer size
-        }
+    public void testGetBasicInfo() throws Exception {
+        final SObjectBasicInfo objectBasicInfo = template().requestBody("direct:getBasicInfo", null,
+            SObjectBasicInfo.class);
+        assertNotNull(objectBasicInfo);
+
+        // set test Id for testGetSObject
+        assertFalse("RecentItems is empty", objectBasicInfo.getRecentItems().isEmpty());
+        testId = objectBasicInfo.getRecentItems().get(0).getId();
     }
 
-    public void doTestGetBlobField(String suffix) throws Exception {
+    @Test
+    public void testGetBlobField() throws Exception {
         // get document with Name "Test Document"
-        final HashMap<String, Object> headers = new HashMap<String, Object>();
+        final HashMap<String, Object> headers = new HashMap<>();
         headers.put(SalesforceEndpointConfig.SOBJECT_NAME, "Document");
         headers.put(SalesforceEndpointConfig.SOBJECT_EXT_ID_NAME, "Name");
-        Document document = template().requestBodyAndHeaders("direct:getSObjectWithId" + suffix, TEST_DOCUMENT_ID,
-                headers, Document.class);
+        final Document document = template().requestBodyAndHeaders("direct:getSObjectWithId", TEST_DOCUMENT_ID, headers,
+            Document.class);
         assertNotNull(document);
-        LOG.debug("GetWithId: {}", document);
 
         // get Body field for this document
-        InputStream body = template().requestBody("direct:getBlobField" + suffix, document, InputStream.class);
-        assertNotNull(body);
-        LOG.debug("GetBlobField: {}", body);
-        // write body to test file
-        final FileChannel fileChannel = new FileOutputStream("target/getBlobField" + suffix + ".txt").getChannel();
-        final ReadableByteChannel src = Channels.newChannel(body);
-        fileChannel.transferFrom(src, 0, document.getBodyLength());
-        fileChannel.close();
-        src.close();
+        try (final InputStream body = template().requestBody("direct:getBlobField", document, InputStream.class)) {
+            assertNotNull(body);
+            assertTrue(body.available() > 0);
+        }
     }
 
     @Test
-    public void testQuery() throws Exception {
-        doTestQuery("");
-        doTestQuery("Xml");
-    }
+    public void testGetDescription() throws Exception {
 
-    private void doTestQuery(String suffix) throws Exception {
-        QueryRecordsLine_Item__c queryRecords = template().requestBody("direct:query" + suffix, null,
-                QueryRecordsLine_Item__c.class);
-        assertNotNull(queryRecords);
-        LOG.debug("ExecuteQuery: {}", queryRecords);
+        final SObjectDescription sObjectDescription = template().requestBody("direct:getDescription", null,
+            SObjectDescription.class);
+        assertNotNull(sObjectDescription);
     }
 
     @Test
-    public void testQueryAll() throws Exception {
-        doTestQueryAll("");
-        doTestQueryAll("Xml");
-    }
+    public void testGetGlobalObjects() throws Exception {
 
-    private void doTestQueryAll(String suffix) throws Exception {
-        QueryRecordsLine_Item__c queryRecords = template().requestBody("direct:queryAll" + suffix, null,
-            QueryRecordsLine_Item__c.class);
-        assertNotNull(queryRecords);
-        LOG.debug("ExecuteQuery: {}", queryRecords);
+        final GlobalObjects globalObjects = template().requestBody("direct:getGlobalObjects", null,
+            GlobalObjects.class);
+        assertNotNull(globalObjects);
     }
 
-
     @Test
-    public void testSearch() throws Exception {
-        doTestSearch("");
-        doTestSearch("Xml");
+    public void testGetResources() throws Exception {
+
+        final RestResources resources = template().requestBody("direct:getResources", null, RestResources.class);
+        assertNotNull(resources);
     }
 
-    @SuppressWarnings("unchecked")
-    private void doTestSearch(String suffix) throws Exception {
+    @Test
+    public void testGetSObject() throws Exception {
+        final Merchandise__c merchandise = template().requestBody("direct:getSObject", testId, Merchandise__c.class);
+        assertNotNull(merchandise);
 
-        Object obj = template().requestBody("direct:search" + suffix, (Object) null);
-        List<SearchResult> searchResults = null;
-        if (obj instanceof SearchResults) {
-            SearchResults results = (SearchResults) obj;
-            searchResults = results.getResults();
+        assertNull(merchandise.getTotal_Inventory__c());
+        assertNotNull(merchandise.getPrice__c());
+    }
+
+    @Test
+    public void testGetVersions() throws Exception {
+        // test getVersions doesn't need a body
+        // assert expected result
+        final Object o = template().requestBody("direct:getVersions", (Object) null);
+        List<Version> versions = null;
+        if (o instanceof Versions) {
+            versions = ((Versions) o).getVersions();
         } else {
-            searchResults = (List<SearchResult>) obj;
+            @SuppressWarnings("unchecked")
+            final List<Version> tmp = (List<Version>) o;
+            versions = tmp;
         }
-        assertNotNull(searchResults);
-        LOG.debug("ExecuteSearch: {}", searchResults);
+        assertNotNull(versions);
     }
 
     @Test
-    public void testApexCall() throws Exception {
-        try {
-            doTestApexCall("");
-            doTestApexCall("Xml");
-        } catch (CamelExecutionException e) {
-            if (e.getCause() instanceof SalesforceException) {
-                SalesforceException cause = (SalesforceException) e.getCause();
-                if (cause.getStatusCode() == HttpStatus.NOT_FOUND_404) {
-                    LOG.error("Make sure test REST resource MerchandiseRestResource.apxc has been loaded: "
-                            + e.getMessage());
-                }
-            }
-            throw e;
-        }
+    public void testQuery() throws Exception {
+        final QueryRecordsLine_Item__c queryRecords = template().requestBody("direct:query", null,
+            QueryRecordsLine_Item__c.class);
+        assertNotNull(queryRecords);
     }
 
-    private void doTestApexCall(String suffix) throws Exception {
+    @Test
+    public void testQueryAll() throws Exception {
+        final QueryRecordsLine_Item__c queryRecords = template().requestBody("direct:queryAll", null,
+            QueryRecordsLine_Item__c.class);
+        assertNotNull(queryRecords);
+    }
 
-        if (testId == null) {
-            // execute getBasicInfo to get test id from recent items
-            doTestGetBasicInfo("");
-        }
+    @Test
+    public void testRetry() throws Exception {
+        final SalesforceComponent sf = context().getComponent("salesforce", SalesforceComponent.class);
+        final String accessToken = sf.getSession().getAccessToken();
 
-        // request merchandise with id in URI template
-        Merchandise__c merchandise = template().requestBodyAndHeader("direct:apexCallGet" + suffix, null,
-                "id", testId, Merchandise__c.class);
-        assertNotNull(merchandise);
-        LOG.debug("ApexCallGet: {}", merchandise);
+        final SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setSslContext(new SSLContextParameters().createSSLContext(context));
+        final HttpClient httpClient = new HttpClient(sslContextFactory);
+        httpClient.setConnectTimeout(60000);
+        httpClient.start();
 
-        // request merchandise with id as query param
-        merchandise = template().requestBodyAndHeader("direct:apexCallGetWithId" + suffix, null,
-                SalesforceEndpointConfig.APEX_QUERY_PARAM_PREFIX + "id", testId, Merchandise__c.class);
-        assertNotNull(merchandise);
-        LOG.debug("ApexCallGetWithId: {}", merchandise);
+        final String uri = sf.getLoginConfig().getLoginUrl() + "/services/oauth2/revoke?token=" + accessToken;
+        final Request logoutGet = httpClient.newRequest(uri).method(HttpMethod.GET).timeout(1, TimeUnit.MINUTES);
 
-        // patch merchandise
-        // clear fields that won't be modified
-        merchandise.clearBaseFields();
-        merchandise.setId(testId);
-        merchandise.setPrice__c(null);
-        merchandise.setTotal_Inventory__c(null);
+        final ContentResponse response = logoutGet.send();
+        assertEquals(HttpStatus.OK_200, response.getStatus());
 
-        merchandise = template().requestBody("direct:apexCallPatch" + suffix,
-                new MerchandiseRequest(merchandise), Merchandise__c.class);
-        assertNotNull(merchandise);
-        LOG.debug("ApexCallPatch: {}", merchandise);
+        testGetGlobalObjects();
     }
 
-    /**
-     * Request DTO for Salesforce APEX REST calls.
-     * See https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_rest_methods.htm.
-     */
-    @XStreamAlias("request")
-    public static class MerchandiseRequest extends AbstractDTOBase {
-        private Merchandise__c merchandise;
-
-        public MerchandiseRequest(Merchandise__c merchandise) {
-            this.merchandise = merchandise;
-        }
+    @Test
+    public void testRetryFailure() throws Exception {
+        final SalesforceComponent sf = context().getComponent("salesforce", SalesforceComponent.class);
+        final String accessToken = sf.getSession().getAccessToken();
 
-        public Merchandise__c getMerchandise() {
-            return merchandise;
-        }
+        final SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setSslContext(new SSLContextParameters().createSSLContext(context));
+        final HttpClient httpClient = new HttpClient(sslContextFactory);
+        httpClient.setConnectTimeout(60000);
+        httpClient.start();
 
-        public void setMerchandise(Merchandise__c merchandise) {
-            this.merchandise = merchandise;
-        }
-    }
+        final String uri = sf.getLoginConfig().getLoginUrl() + "/services/oauth2/revoke?token=" + accessToken;
+        final Request logoutGet = httpClient.newRequest(uri).method(HttpMethod.GET).timeout(1, TimeUnit.MINUTES);
 
-    /**
-     * Response DTO for Salesforce APEX REST calls.
-     * See https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_rest_methods.htm.
-     */
-    @XStreamAlias("response")
-    public static class MerchandiseXmlResponse extends Merchandise__c {
-        // XML response contains a type string with the SObject type name
-        private String type;
+        final ContentResponse response = logoutGet.send();
+        assertEquals(HttpStatus.OK_200, response.getStatus());
 
-        public String getType() {
-            return type;
-        }
+        // set component config to bad password to cause relogin attempts to fail
+        final String password = sf.getLoginConfig().getPassword();
+        sf.getLoginConfig().setPassword("bad_password");
 
-        public void setType(String type) {
-            this.type = type;
+        try {
+            testGetGlobalObjects();
+            fail("Expected CamelExecutionException!");
+        } catch (final CamelExecutionException e) {
+            if (e.getCause() instanceof SalesforceException) {
+                final SalesforceException cause = (SalesforceException) e.getCause();
+                assertEquals("Expected 400 on authentication retry failure", HttpStatus.BAD_REQUEST_400,
+                    cause.getStatusCode());
+            } else {
+                fail("Expected SalesforceException!");
+            }
+        } finally {
+            // reset password and retries to allow other tests to pass
+            sf.getLoginConfig().setPassword(password);
         }
     }
 
     @Test
-    public void testStatus300() throws Exception {
-        doTestStatus300("");
-        doTestStatus300("Xml");
-    }
+    public void testSearch() throws Exception {
 
-    private void doTestStatus300(String suffix) throws Exception {
-        // clone test merchandise with same external Id
-        if (testId == null) {
-            // execute getBasicInfo to get test id from recent items
-            doTestGetBasicInfo("");
+        final Object obj = template().requestBody("direct:search", (Object) null);
+        List<SearchResult> searchResults = null;
+        if (obj instanceof SearchResults) {
+            final SearchResults results = (SearchResults) obj;
+            searchResults = results.getResults();
+        } else {
+            @SuppressWarnings("unchecked")
+            final List<SearchResult> tmp = (List<SearchResult>) obj;
+            searchResults = tmp;
         }
+        assertNotNull(searchResults);
+    }
 
+    @Test
+    public void testStatus300() throws Exception {
         // get test merchandise
         // note that the header value overrides sObjectFields in endpoint
-        Merchandise__c merchandise = template().requestBodyAndHeader("direct:getSObject" + suffix, testId,
-                "sObjectFields", "Name,Description__c,Price__c,Total_Inventory__c",
-                Merchandise__c.class);
+        final Merchandise__c merchandise = template().requestBodyAndHeader("direct:getSObject", testId, "sObjectFields",
+            "Name,Description__c,Price__c,Total_Inventory__c", Merchandise__c.class);
         assertNotNull(merchandise);
         assertNotNull(merchandise.getName());
         assertNotNull(merchandise.getPrice__c());
@@ -548,52 +432,39 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase {
         CreateSObjectResult result = null;
         try {
             merchandise.clearBaseFields();
-            result = template().requestBody("direct:createSObject" + suffix,
-                    merchandise, CreateSObjectResult.class);
+            result = template().requestBody("direct:createSObject", merchandise, CreateSObjectResult.class);
             assertNotNull(result);
             assertNotNull(result.getId());
-            LOG.debug("Clone SObject: {}", result);
 
             // look by external Id to cause 300 error
             // note that the request SObject overrides settings on the endpoint for LineItem__c
             try {
-                template().requestBody("direct:getSObjectWithId" + suffix, merchandise, Merchandise__c.class);
+                template().requestBody("direct:getSObjectWithId", merchandise, Merchandise__c.class);
                 fail("Expected SalesforceException with statusCode 300");
-            } catch (CamelExecutionException e) {
+            } catch (final CamelExecutionException e) {
                 assertTrue(e.getCause() instanceof SalesforceException);
                 assertTrue(e.getCause().getCause() instanceof SalesforceMultipleChoicesException);
-                final SalesforceMultipleChoicesException cause = (SalesforceMultipleChoicesException) e.getCause().getCause();
+                final SalesforceMultipleChoicesException cause = (SalesforceMultipleChoicesException) e.getCause()
+                    .getCause();
                 assertEquals(300, cause.getStatusCode());
                 final List<String> choices = cause.getChoices();
                 assertNotNull(choices);
                 assertFalse(choices.isEmpty());
-                LOG.debug("Multiple choices: {}", choices);
             }
         } finally {
             // delete the test clone
             if (result != null) {
-                template().requestBody("direct:deleteSObject" + suffix, result.getId());
+                template().requestBody("direct:deleteSObject", result.getId());
             }
         }
     }
 
     @Test
     public void testStatus400() throws Exception {
-        doTestStatus400("");
-        doTestStatus400("Xml");
-    }
-
-    private void doTestStatus400(String suffix) throws Exception {
-        // clone test merchandise with same external Id
-        if (testId == null) {
-            // execute getBasicInfo to get test id from recent items
-            doTestGetBasicInfo("");
-        }
-
         // get test merchandise
         // note that the header value overrides sObjectFields in endpoint
-        Merchandise__c merchandise = template().requestBodyAndHeader("direct:getSObject" + suffix, testId,
-                "sObjectFields", "Description__c,Price__c", Merchandise__c.class);
+        final Merchandise__c merchandise = template().requestBodyAndHeader("direct:getSObject", testId, "sObjectFields",
+            "Description__c,Price__c", Merchandise__c.class);
         assertNotNull(merchandise);
         assertNotNull(merchandise.getPrice__c());
         assertNull(merchandise.getTotal_Inventory__c());
@@ -602,10 +473,9 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase {
         // required field Total_Inventory__c is missing
         CreateSObjectResult result = null;
         try {
-            result = template().requestBody("direct:createSObject" + suffix,
-                    merchandise, CreateSObjectResult.class);
+            result = template().requestBody("direct:createSObject", merchandise, CreateSObjectResult.class);
             fail("Expected SalesforceException with statusCode 400");
-        } catch (CamelExecutionException e) {
+        } catch (final CamelExecutionException e) {
             assertTrue(e.getCause() instanceof SalesforceException);
             assertTrue(e.getCause().getCause() instanceof SalesforceException);
             final SalesforceException cause = (SalesforceException) e.getCause().getCause();
@@ -615,29 +485,23 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase {
         } finally {
             // delete the clone if created
             if (result != null) {
-                template().requestBody("direct:deleteSObject" + suffix, result.getId());
+                template().requestBody("direct:deleteSObject", result.getId());
             }
         }
     }
 
     @Test
     public void testStatus404() {
-        doTestStatus404("");
-        doTestStatus404("Xml");
-    }
-
-    private void doTestStatus404(String suffix) {
         // try to get a non existent SObject
         try {
-            template().requestBody("direct:getSObject" + suffix, "ILLEGAL_ID", Merchandise__c.class);
+            template().requestBody("direct:getSObject", "ILLEGAL_ID", Merchandise__c.class);
             fail("Expected SalesforceException");
-        } catch (CamelExecutionException e) {
+        } catch (final CamelExecutionException e) {
             assertTrue(e.getCause() instanceof SalesforceException);
             assertTrue(e.getCause().getCause() instanceof SalesforceException);
             final SalesforceException cause = (SalesforceException) e.getCause().getCause();
             assertEquals(404, cause.getStatusCode());
             assertEquals(1, cause.getErrors().size());
-            LOG.debug("Errors for 404: {}", cause.getErrors());
         }
     }
 
@@ -646,154 +510,93 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase {
 
         // create test route
         return new RouteBuilder() {
+            @Override
             public void configure() {
 
                 // testGetVersion
-                from("direct:getVersions")
-                        .to("salesforce:getVersions");
-
-                // allow overriding format per endpoint
-                from("direct:getVersionsXml")
-                        .to("salesforce:getVersions?format=XML");
+                from("direct:getVersions").to("salesforce:getVersions?format=" + format);
 
                 // testGetResources
-                from("direct:getResources")
-                        .to("salesforce:getResources");
-
-                from("direct:getResourcesXml")
-                        .to("salesforce:getResources?format=XML");
+                from("direct:getResources").to("salesforce:getResources?format=" + format);
 
                 // testGetGlobalObjects
-                from("direct:getGlobalObjects")
-                        .to("salesforce:getGlobalObjects");
-
-                from("direct:getGlobalObjectsXml")
-                        .to("salesforce:getGlobalObjects?format=XML");
+                from("direct:getGlobalObjects").to("salesforce:getGlobalObjects?format=" + format);
 
                 // testGetBasicInfo
-                from("direct:getBasicInfo")
-                        .to("salesforce:getBasicInfo?sObjectName=Merchandise__c");
-
-                from("direct:getBasicInfoXml")
-                        .to("salesforce:getBasicInfo?format=XML&sObjectName=Merchandise__c");
+                from("direct:getBasicInfo").to("salesforce:getBasicInfo?sObjectName=Merchandise__c&format=" + format);
 
                 // testGetDescription
                 from("direct:getDescription")
-                        .to("salesforce:getDescription?sObjectName=Merchandise__c");
-
-                from("direct:getDescriptionXml")
-                        .to("salesforce:getDescription?format=XML&sObjectName=Merchandise__c");
+                    .to("salesforce:getDescription?sObjectName=Merchandise__c&format=" + format);
 
                 // testGetSObject
                 from("direct:getSObject")
-                        .to("salesforce:getSObject?sObjectName=Merchandise__c&sObjectFields=Description__c,Price__c");
-
-                from("direct:getSObjectXml")
-                        .to("salesforce:getSObject?format=XML&sObjectName=Merchandise__c&sObjectFields=Description__c,Total_Inventory__c");
+                    .to("salesforce:getSObject?sObjectName=Merchandise__c&sObjectFields=Description__c,Price__c&format="
+                        + format);
 
                 // testCreateSObject
-                from("direct:createSObject")
-                        .to("salesforce:createSObject?sObjectName=Merchandise__c");
-
-                from("direct:createSObjectXml")
-                        .to("salesforce:createSObject?format=XML&sObjectName=Merchandise__c");
+                from("direct:createSObject").to("salesforce:createSObject?sObjectName=Merchandise__c&format=" + format);
 
                 // testUpdateSObject
-                from("direct:updateSObject")
-                        .to("salesforce:updateSObject?sObjectName=Merchandise__c");
-
-                from("direct:updateSObjectXml")
-                        .to("salesforce:updateSObject?format=XML&sObjectName=Merchandise__c");
+                from("direct:updateSObject").to("salesforce:updateSObject?sObjectName=Merchandise__c&format=" + format);
 
                 // testDeleteSObject
-                from("direct:deleteSObject")
-                        .to("salesforce:deleteSObject?sObjectName=Merchandise__c");
-
-                from("direct:deleteSObjectXml")
-                        .to("salesforce:deleteSObject?format=XML&sObjectName=Merchandise__c");
+                from("direct:deleteSObject").to("salesforce:deleteSObject?sObjectName=Merchandise__c&format=" + format);
 
-                from("direct:deleteSObjectTaken")
-                        .to("salesforce:deleteSObject?sObjectName=Tasks__c");
-                from("direct:deleteSObjectTakenXml")
-                        .to("salesforce:deleteSObject?format=XML&sObjectName=Tasks__c");
+                from("direct:deleteSObjectTaken").to("salesforce:deleteSObject?sObjectName=Task&format=" + format);
 
                 // testGetSObjectWithId
                 from("direct:getSObjectWithId")
-                        .to("salesforce:getSObjectWithId?sObjectName=Line_Item__c&sObjectIdName=Name");
-
-                from("direct:getSObjectWithIdXml")
-                        .to("salesforce:getSObjectWithId?format=XML&sObjectName=Line_Item__c&sObjectIdName=Name");
+                    .to("salesforce:getSObjectWithId?sObjectName=Line_Item__c&sObjectIdName=Name&format=" + format);
 
                 // testUpsertSObject
                 from("direct:deleteLineItems")
-                        .to("salesforce:query?sObjectQuery=SELECT Id FROM Line_Item__C&sObjectClass=" + QueryRecordsLine_Item__c.class.getName())
-                        .transform(simple("${body.records}")).split(body()).transform(simple("${body.id}"))
-                        .to("salesforce:deleteSObject?sObjectName=Line_Item__c");
+                    .to("salesforce:query?sObjectQuery=SELECT Id FROM Line_Item__C&sObjectClass="
+                        + QueryRecordsLine_Item__c.class.getName())
+                    .transform(simple("${body.records}")).split(body()).transform(simple("${body.id}"))
+                    .to("salesforce:deleteSObject?sObjectName=Line_Item__c");
 
-                from("direct:createLineItem")
-                        .to("salesforce:createSObject?sObjectName=Line_Item__c");
+                from("direct:createLineItem").to("salesforce:createSObject?sObjectName=Line_Item__c");
 
                 from("direct:upsertSObject")
-                        .to("salesforce:upsertSObject?sObjectName=Line_Item__c&sObjectIdName=Name");
-
-                from("direct:upsertSObjectXml")
-                        .to("salesforce:upsertSObject?format=XML&sObjectName=Line_Item__c&sObjectIdName=Name");
+                    .to("salesforce:upsertSObject?sObjectName=Line_Item__c&sObjectIdName=Name&format=" + format);
 
                 // testDeleteSObjectWithId
                 from("direct:deleteSObjectWithId")
-                        .to("salesforce:deleteSObjectWithId?sObjectName=Line_Item__c&sObjectIdName=Name");
-
-                from("direct:deleteSObjectWithIdXml")
-                        .to("salesforce:deleteSObjectWithId?format=XML&sObjectName=Line_Item__c&sObjectIdName=Name");
+                    .to("salesforce:deleteSObjectWithId?sObjectName=Line_Item__c&sObjectIdName=Name&format=" + format);
 
                 // testGetBlobField
                 from("direct:getBlobField")
-                        .to("salesforce:getBlobField?sObjectName=Document&sObjectBlobFieldName=Body");
-
-                from("direct:getBlobFieldXml")
-                        .to("salesforce:getBlobField?format=XML&sObjectName=Document&sObjectBlobFieldName=Body");
+                    .to("salesforce:getBlobField?sObjectName=Document&sObjectBlobFieldName=Body&format=" + format);
 
                 // testQuery
-                from("direct:query")
-                        .to("salesforce:query?sObjectQuery=SELECT name from Line_Item__c&sObjectClass=" + QueryRecordsLine_Item__c.class.getName());
-
-                from("direct:queryXml")
-                        .to("salesforce:query?format=XML&sObjectQuery=SELECT name from Line_Item__c&sObjectClass=" + QueryRecordsLine_Item__c.class.getName());
+                from("direct:query").to("salesforce:query?sObjectQuery=SELECT name from Line_Item__c&sObjectClass="
+                    + QueryRecordsLine_Item__c.class.getName() + "&format=" + format);
 
                 // testQueryAll
                 from("direct:queryAll")
-                    .to("salesforce:queryAll?sObjectQuery=SELECT name from Line_Item__c&sObjectClass=" + QueryRecordsLine_Item__c.class.getName());
-
-                from("direct:queryAllXml")
-                    .to("salesforce:queryAll?format=XML&sObjectQuery=SELECT name from Line_Item__c&sObjectClass=" + QueryRecordsLine_Item__c.class.getName());
+                    .to("salesforce:queryAll?sObjectQuery=SELECT name from Line_Item__c&sObjectClass="
+                        + QueryRecordsLine_Item__c.class.getName() + "&format=" + format);
 
                 // testSearch
-                from("direct:search")
-                        .to("salesforce:search?sObjectSearch=FIND {Wee}");
-
-                from("direct:searchXml")
-                        .to("salesforce:search?format=XML&sObjectSearch=FIND {Wee}");
+                from("direct:search").to("salesforce:search?sObjectSearch=FIND {Wee}&format=" + format);
 
                 // testApexCall
                 from("direct:apexCallGet")
-                        .to("salesforce:apexCall?apexMethod=GET&apexUrl=Merchandise/{id}&sObjectName=Merchandise__c");
+                    .to("salesforce:apexCall?apexMethod=GET&apexUrl=Merchandise/{id}&sObjectName=Merchandise__c&format="
+                        + format);
 
-                from("direct:apexCallGetXml")
-                        .to("salesforce:apexCall/Merchandise/{id}?format=XML&apexMethod=GET&sObjectClass=" + MerchandiseXmlResponse.class.getName());
+                from("direct:apexCallGetWithId").to("salesforce:apexCall/Merchandise/?apexMethod=GET&id=dummyId&format="
+                    + format + "&sObjectClass=" + Merchandise__c.class.getName());
 
-                from("direct:apexCallGetWithId")
-                        .to("salesforce:apexCall/Merchandise/?apexMethod=GET&id=dummyId&sObjectClass=" + Merchandise__c.class.getName());
-
-                from("direct:apexCallGetWithIdXml")
-                        .to("salesforce:apexCall?format=XML&apexMethod=GET&apexUrl=Merchandise/&id=dummyId&sObjectClass=" + MerchandiseXmlResponse.class.getName());
-
-                from("direct:apexCallPatch")
-                        .to("salesforce:apexCall?apexMethod=PATCH&apexUrl=Merchandise/&sObjectName=Merchandise__c");
-
-                from("direct:apexCallPatchXml")
-                        .to("salesforce:apexCall/Merchandise/?format=XML&apexMethod=PATCH&sObjectClass=" + MerchandiseXmlResponse.class.getName());
+                from("direct:apexCallPatch").to("salesforce:apexCall/Merchandise/?format=" + format
+                    + "&apexMethod=PATCH&sObjectClass=" + MerchandiseResponse.class.getName());
             }
         };
     }
 
+    @Parameters(name = "format = {0}")
+    public static Iterable<String> parameters() {
+        return Arrays.asList("XML", "JSON");
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentConfigurationIntegrationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentConfigurationIntegrationTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentConfigurationIntegrationTest.java
index 132de83..e5f15e1 100644
--- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentConfigurationIntegrationTest.java
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentConfigurationIntegrationTest.java
@@ -34,7 +34,7 @@ import org.apache.camel.component.salesforce.dto.generated.Line_Item__c;
 import org.apache.camel.component.salesforce.dto.generated.MSPTest;
 import org.apache.camel.component.salesforce.dto.generated.Merchandise__c;
 import org.apache.camel.component.salesforce.dto.generated.QueryRecordsLine_Item__c;
-import org.apache.camel.component.salesforce.dto.generated.Tasks__c;
+import org.apache.camel.component.salesforce.dto.generated.Task;
 import org.apache.camel.component.salesforce.internal.PayloadFormat;
 import org.apache.camel.impl.ParameterConfiguration;
 import org.apache.camel.test.junit4.CamelTestSupport;
@@ -77,7 +77,7 @@ public class SalesforceComponentConfigurationIntegrationTest extends CamelTestSu
         configuration.setParameter("updateTopic", false);
 
         // operation name is base uri
-        configuration.setBaseUri("getSObject");
+        configuration.setBaseUri(componentName + ":getSObject");
 
         SalesforceEndpoint endpoint = assertIsInstanceOf(SalesforceEndpoint.class, configuration.createEndpoint());
         final SalesforceEndpointConfig endpointConfig = endpoint.getConfiguration();
@@ -156,7 +156,7 @@ public class SalesforceComponentConfigurationIntegrationTest extends CamelTestSu
 
         // get sObjectName values, from scanned DTO packages
         assertCompletionOptions(configuration.completeEndpointPath("getSObject?sObjectName="),
-            "Account", "Asset", "Contact", "Tasks__c", "Line_Item__c", "Merchandise__c", "Document", "MSPTest");
+            "Account", "Asset", "Contact", "Task", "Line_Item__c", "Merchandise__c", "Document", "MSPTest");
 
         // get sObjectFields values, from scanned DTO
         assertCompletionOptions(
@@ -170,7 +170,7 @@ public class SalesforceComponentConfigurationIntegrationTest extends CamelTestSu
             Account.class.getName(),
             Asset.class.getName(),
             Contact.class.getName(),
-            Tasks__c.class.getName(),
+            Task.class.getName(),
             Line_Item__c.class.getName(),
             Merchandise__c.class.getName(),
             Document.class.getName(),

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/Standalone.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/Standalone.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/Standalone.java
new file mode 100644
index 0000000..8a13647
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/Standalone.java
@@ -0,0 +1,24 @@
+/**
+ * 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.camel.component.salesforce;
+
+/**
+ * JUnit group tag interface for tests that need to be run standalone, i.e. not in parallel with other tests.
+ */
+public interface Standalone {
+    // tag interface
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Task.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Task.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Task.java
new file mode 100644
index 0000000..33292d6
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Task.java
@@ -0,0 +1,55 @@
+/**
+ * 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.camel.component.salesforce.dto.generated;
+
+import java.time.ZonedDateTime;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase;
+
+/**
+ * Salesforce DTO for SObject Task
+ */
+@XStreamAlias("Task")
+public class Task extends AbstractSObjectBase {
+
+    private ZonedDateTime ActivityDate;
+
+    private String Description;
+
+    @JsonProperty("ActivityDate")
+    public ZonedDateTime getActivityDate() {
+        return ActivityDate;
+    }
+
+    @JsonProperty("Description")
+    public String getDescription() {
+        return Description;
+    }
+
+    @JsonProperty("ActivityDate")
+    public void setActivityDate(final ZonedDateTime given) {
+        ActivityDate = given;
+    }
+
+    @JsonProperty("Description")
+    public void setDescription(final String description) {
+        Description = description;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Tasks__c.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Tasks__c.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Tasks__c.java
deleted file mode 100644
index 709db668..0000000
--- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/generated/Tasks__c.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Salesforce DTO generated by camel-salesforce-maven-plugin
- * Generated on: Wed Jun 29 21:11:17 CEST 2016
- */
-package org.apache.camel.component.salesforce.dto.generated;
-
-import java.time.ZonedDateTime;
-
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Salesforce DTO for SObject Tasks__c
- */
-@XStreamAlias("Tasks__c")
-public class Tasks__c extends AbstractSObjectBase {
-
-    // Start__c
-    private ZonedDateTime Start__c;
-
-    @JsonProperty("Start__c")
-    public ZonedDateTime getStart__c() {
-        return this.Start__c;
-    }
-
-    @JsonProperty("Start__c")
-    public void setStart__c(ZonedDateTime Start__c) {
-        this.Start__c = Start__c;
-    }
-
-    // Planned__c
-    private ZonedDateTime Planned__c;
-
-    @JsonProperty("Planned__c")
-    public ZonedDateTime getPlanned__c() {
-        return this.Planned__c;
-    }
-
-    @JsonProperty("Planned__c")
-    public void setPlanned__c(ZonedDateTime Planned__c) {
-        this.Planned__c = Planned__c;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml b/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml
index 1108309..76b6745 100644
--- a/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml
@@ -32,6 +32,10 @@
   <name>Camel :: Salesforce :: Maven plugin</name>
   <description>Camel Salesforce Maven plugin</description>
 
+  <properties>
+    <salesforce.component.root>..</salesforce.component.root>
+  </properties>
+
   <dependencyManagement>
     <dependencies>
       <dependency>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/migration-tool/.gitignore
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/migration-tool/.gitignore b/components/camel-salesforce/it/resources/migration-tool/.gitignore
new file mode 100644
index 0000000..62a9cd6
--- /dev/null
+++ b/components/camel-salesforce/it/resources/migration-tool/.gitignore
@@ -0,0 +1 @@
+/ant-salesforce.jar

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/applications/Warehouse.app
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/applications/Warehouse.app b/components/camel-salesforce/it/resources/salesforce/applications/Warehouse.app
new file mode 100644
index 0000000..bb76152
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/applications/Warehouse.app
@@ -0,0 +1,27 @@
+<?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.
+-->
+<CustomApplication xmlns="http://soap.sforce.com/2006/04/metadata">
+    <defaultLandingTab>Merchandise__c</defaultLandingTab>
+    <label>Warehouse</label>
+    <tab>standard-Chatter</tab>
+    <tab>standard-File</tab>
+    <tab>standard-report</tab>
+    <tab>standard-Dashboard</tab>
+    <tab>Invoice__c</tab>
+    <tab>Merchandise__c</tab>
+</CustomApplication>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/approvalProcesses/Account.Test_Account_Process.approvalProcess
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/approvalProcesses/Account.Test_Account_Process.approvalProcess b/components/camel-salesforce/it/resources/salesforce/approvalProcesses/Account.Test_Account_Process.approvalProcess
new file mode 100644
index 0000000..48359a5
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/approvalProcesses/Account.Test_Account_Process.approvalProcess
@@ -0,0 +1,44 @@
+<?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.
+-->
+<ApprovalProcess xmlns="http://soap.sforce.com/2006/04/metadata">
+    <active>true</active>
+    <allowRecall>false</allowRecall>
+    <allowedSubmitters>
+        <type>owner</type>
+    </allowedSubmitters>
+    <approvalPageFields>
+        <field>Name</field>
+        <field>Owner</field>
+    </approvalPageFields>
+    <approvalStep>
+        <allowDelegate>false</allowDelegate>
+        <assignedApprover>
+            <approver>
+                <type>adhoc</type>
+            </approver>
+        </assignedApprover>
+        <label>Step 1</label>
+        <name>Step_1</name>
+    </approvalStep>
+    <enableMobileDeviceAccess>false</enableMobileDeviceAccess>
+    <finalApprovalRecordLock>true</finalApprovalRecordLock>
+    <finalRejectionRecordLock>false</finalRejectionRecordLock>
+    <label>Test Account Process</label>
+    <recordEditability>AdminOnly</recordEditability>
+    <showApprovalHistory>true</showApprovalHistory>
+</ApprovalProcess>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls
new file mode 100644
index 0000000..6267a17
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls
@@ -0,0 +1,60 @@
+/**
+ * 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.
+ */
+@RestResource(urlMapping='/Merchandise/*')
+global with sharing class MerchandiseRestResource {
+
+    @HttpGet
+    global static Merchandise__c doGet() {
+        RestRequest req = RestContext.request;
+        String merchandiseId = null;
+        if (!req.requestURI.endsWith('/')) {
+            merchandiseId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
+        } else if (req.params.get('id') != null) {
+            merchandiseId = req.params.get('id');
+        }
+        if (merchandiseId != null) {
+            Merchandise__c merchandise = [SELECT Id, Name, Description__c, Price__c, Total_Inventory__c FROM Merchandise__c WHERE Id = :merchandiseId];
+            return merchandise;
+        } else {
+            throw new InvalidParamException('Missing merchandise id in URL and query params');
+        }
+    }
+  
+    @HttpPatch
+    global static Merchandise__c doPatch(Merchandise__c merchandise) {
+        // lookup merchandise
+        Merchandise__c current = [SELECT Id, Name, Description__c, Price__c, Total_Inventory__c FROM Merchandise__c WHERE Id = :merchandise.Id];
+        if (current == null) {
+            throw new InvalidParamException('Missing merchandise for id ' + merchandise.Id);
+        }
+        if (merchandise.Description__c != null) {
+            current.Description__c = merchandise.Description__c;
+        }
+        if (merchandise.Price__c != null) {
+            current.Price__c = merchandise.Price__c;
+        }
+        if (merchandise.Total_Inventory__c != null) {
+            current.Total_Inventory__c = merchandise.Total_Inventory__c;
+        }
+
+        update current;
+        return current;
+    }
+
+    // Invalid Merchandise Id exception
+    public class InvalidParamException extends Exception {}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls-meta.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls-meta.xml b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls-meta.xml
new file mode 100644
index 0000000..d378756
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResource.cls-meta.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
+    <apiVersion>38.0</apiVersion>
+    <status>Active</status>
+</ApexClass>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls
new file mode 100644
index 0000000..080f0db
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls
@@ -0,0 +1,73 @@
+/**
+ * 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.
+ */
+@isTest
+public class MerchandiseRestResourceTests {
+
+    static testmethod void testDoGetWithPathParameter() {
+        Merchandise__c merchandise = new Merchandise__c();
+        merchandise.Price__c = 1;
+        merchandise.Total_Inventory__c = 2;
+
+        insert merchandise;
+
+        RestRequest request = new RestRequest();
+        request.requestURI = 'Merchandise/' + merchandise.Id;
+
+        RestContext.request = request;
+
+        MerchandiseRestResource.doGet();
+    }
+
+    static testmethod void testDoGetWithRequestParameter() {
+        Merchandise__c merchandise = new Merchandise__c();
+        merchandise.Price__c = 1;
+        merchandise.Total_Inventory__c = 2;
+
+        insert merchandise;
+
+        RestRequest request = new RestRequest();
+        request.requestURI = 'Merchandise/';
+        request.params.put('id', merchandise.Id);        
+
+        RestContext.request = request;
+
+        MerchandiseRestResource.doGet();
+    }
+
+    static testmethod void testDoGetWithoutParameter() {
+        RestRequest request = new RestRequest();
+        request.requestURI = 'Merchandise/';
+
+        RestContext.request = request;
+
+        try {
+            MerchandiseRestResource.doGet();
+        } catch (Exception e) {
+            // expected
+        }
+    }
+
+    static testmethod void testDoPatch() {
+        Merchandise__c merchandise = new Merchandise__c();
+        merchandise.Price__c = 1;
+        merchandise.Total_Inventory__c = 2;
+
+        insert merchandise;
+
+        MerchandiseRestResource.doPatch(merchandise);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls-meta.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls-meta.xml b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls-meta.xml
new file mode 100644
index 0000000..d378756
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/classes/MerchandiseRestResourceTests.cls-meta.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
+    <apiVersion>38.0</apiVersion>
+    <status>Active</status>
+</ApexClass>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls b/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls
new file mode 100644
index 0000000..1b12942
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls
@@ -0,0 +1,37 @@
+/**
+ * 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.
+ */
+@RestResource(urlMapping='/UpdateProfile/*')
+global without sharing class UpdateProfile {
+
+    @HttpPatch
+    global static String perform() {
+        String username = RestContext.request.params.get('username');
+
+        PermissionSet bulkDelete = [SELECT Id FROM PermissionSet WHERE Name = 'Hard_Delete_Permission_Set' LIMIT 1];
+        User user = [SELECT Id FROM USER WHERE Username = :username LIMIT 1];
+
+        List<PermissionSetAssignment> previous = [SELECT Id FROM PermissionSetAssignment WHERE PermissionSetId = :bulkDelete.Id AND AssigneeId = :user.Id];
+
+        if (previous.isEmpty()) {
+            PermissionSetAssignment assignment = new PermissionSetAssignment(PermissionSetId = bulkDelete.Id, AssigneeId = user.Id);
+            upsert assignment;
+        }
+
+        return 'success';
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls-meta.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls-meta.xml b/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls-meta.xml
new file mode 100644
index 0000000..d378756
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/classes/UpdateProfile.cls-meta.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
+    <apiVersion>38.0</apiVersion>
+    <status>Active</status>
+</ApexClass>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/connectedApps/CamelSalesforceIntegrationTests.connectedApp
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/connectedApps/CamelSalesforceIntegrationTests.connectedApp b/components/camel-salesforce/it/resources/salesforce/connectedApps/CamelSalesforceIntegrationTests.connectedApp
new file mode 100644
index 0000000..4afdf5d
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/connectedApps/CamelSalesforceIntegrationTests.connectedApp
@@ -0,0 +1,27 @@
+<?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.
+-->
+<ConnectedApp xmlns="http://soap.sforce.com/2006/04/metadata">
+    <contactEmail>cbactrianus@gmail.com</contactEmail>
+    <label>CamelSalesforceIntegrationTests</label>
+    <oauthConfig>
+        <callbackUrl>https://login.salesforce.com/services/oauth2/success</callbackUrl>
+        <consumerKey>3MVG9HxRZv05HarQ5D2to.ylPaUg7uaFqGqE2wN6_RRaHM9PTa3SWT8UwKJzXAyCujHrfObkUy7oZqiBUyfGl</consumerKey>
+        <scopes>Api</scopes>
+        <scopes>RefreshToken</scopes>
+    </oauthConfig>
+</ConnectedApp>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents-meta.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents-meta.xml b/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents-meta.xml
new file mode 100644
index 0000000..b9b5bea
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents-meta.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<DocumentFolder xmlns="http://soap.sforce.com/2006/04/metadata">
+    <accessType>Public</accessType>
+    <name>Test Documents</name>
+    <publicFolderAccess>ReadOnly</publicFolderAccess>
+</DocumentFolder>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt b/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt
new file mode 100644
index 0000000..ed9a482
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+hi
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt-meta.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt-meta.xml b/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt-meta.xml
new file mode 100644
index 0000000..92d4c64
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/documents/Test_Documents/Test_Document.txt-meta.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<Document xmlns="http://soap.sforce.com/2006/04/metadata">
+    <internalUseOnly>false</internalUseOnly>
+    <name>Test Document</name>
+    <public>false</public>
+</Document>

http://git-wip-us.apache.org/repos/asf/camel/blob/ee55a3bc/components/camel-salesforce/it/resources/salesforce/layouts/Invoice__c-Invoice Layout.layout
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/it/resources/salesforce/layouts/Invoice__c-Invoice Layout.layout b/components/camel-salesforce/it/resources/salesforce/layouts/Invoice__c-Invoice Layout.layout
new file mode 100644
index 0000000..d89f76b
--- /dev/null
+++ b/components/camel-salesforce/it/resources/salesforce/layouts/Invoice__c-Invoice Layout.layout	
@@ -0,0 +1,86 @@
+<?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.
+-->
+<Layout xmlns="http://soap.sforce.com/2006/04/metadata">
+    <excludeButtons>Submit</excludeButtons>
+    <layoutSections>
+        <customLabel>false</customLabel>
+        <detailHeading>false</detailHeading>
+        <editHeading>true</editHeading>
+        <label>Information</label>
+        <layoutColumns>
+            <layoutItems>
+                <behavior>Readonly</behavior>
+                <field>Name</field>
+            </layoutItems>
+            <layoutItems>
+                <behavior>Edit</behavior>
+                <field>OwnerId</field>
+            </layoutItems>
+            <layoutItems>
+                <behavior>Edit</behavior>
+                <field>Status__c</field>
+            </layoutItems>
+        </layoutColumns>
+        <style>OneColumn</style>
+    </layoutSections>
+    <layoutSections>
+        <customLabel>false</customLabel>
+        <detailHeading>false</detailHeading>
+        <editHeading>true</editHeading>
+        <label>System Information</label>
+        <layoutColumns>
+            <layoutItems>
+                <behavior>Readonly</behavior>
+                <field>CreatedById</field>
+            </layoutItems>
+        </layoutColumns>
+        <layoutColumns>
+            <layoutItems>
+                <behavior>Readonly</behavior>
+                <field>LastModifiedById</field>
+            </layoutItems>
+        </layoutColumns>
+        <style>TwoColumnsTopToBottom</style>
+    </layoutSections>
+    <layoutSections>
+        <customLabel>true</customLabel>
+        <detailHeading>false</detailHeading>
+        <editHeading>true</editHeading>
+        <label>Custom Links</label>
+        <layoutColumns/>
+        <layoutColumns/>
+        <layoutColumns/>
+        <style>CustomLinks</style>
+    </layoutSections>
+    <miniLayout>
+        <fields>Name</fields>
+        <fields>OwnerId</fields>
+        <fields>Status__c</fields>
+    </miniLayout>
+    <showEmailCheckbox>false</showEmailCheckbox>
+    <showHighlightsPanel>true</showHighlightsPanel>
+    <showInteractionLogPanel>true</showInteractionLogPanel>
+    <showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
+    <showSubmitAndAttachButton>false</showSubmitAndAttachButton>
+    <summaryLayout>
+        <masterLabel>00hi0000000hrDC</masterLabel>
+        <sizeX>4</sizeX>
+        <sizeY>0</sizeY>
+        <summaryLayoutStyle>Default</summaryLayoutStyle>
+    </summaryLayout>
+</Layout>