You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2013/06/05 17:12:55 UTC

[11/11] git commit: CAMEL-6428: camel-salesforce component. Thanks to Dhiraj Bokde for the contribution.

CAMEL-6428: camel-salesforce component. Thanks to Dhiraj Bokde for the contribution.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/0c401b9f
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/0c401b9f
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/0c401b9f

Branch: refs/heads/master
Commit: 0c401b9fba53837bdea3dc486880dd0e0c6e2d6f
Parents: 469e0e3
Author: Claus Ibsen <da...@apache.org>
Authored: Wed Jun 5 16:55:52 2013 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Wed Jun 5 17:12:31 2013 +0200

----------------------------------------------------------------------
 .gitignore                                         |    1 +
 apache-camel/pom.xml                               |    4 +
 apache-camel/src/main/descriptors/common-bin.xml   |    1 +
 .../camel-salesforce-component/README.md           |   68 ++
 .../camel-salesforce-component/pom.xml             |  121 +++
 .../component/salesforce/SalesforceComponent.java  |  233 +++++
 .../component/salesforce/SalesforceConsumer.java   |  215 +++++
 .../component/salesforce/SalesforceEndpoint.java   |   96 ++
 .../salesforce/SalesforceEndpointConfig.java       |  292 +++++++
 .../salesforce/SalesforceLoginConfig.java          |   99 +++
 .../component/salesforce/SalesforceProducer.java   |  102 +++
 .../salesforce/api/JodaTimeConverter.java          |   64 ++
 .../salesforce/api/PicklistEnumConverter.java      |   80 ++
 .../salesforce/api/SalesforceException.java        |  104 +++
 .../salesforce/api/dto/AbstractDTOBase.java        |   40 +
 .../api/dto/AbstractQueryRecordsBase.java          |   72 ++
 .../salesforce/api/dto/AbstractSObjectBase.java    |  172 ++++
 .../component/salesforce/api/dto/Attributes.java   |   38 +
 .../salesforce/api/dto/ChildRelationShip.java      |   74 ++
 .../salesforce/api/dto/CreateSObjectResult.java    |   57 ++
 .../salesforce/api/dto/GlobalObjects.java          |   57 ++
 .../salesforce/api/dto/PickListValue.java          |   70 ++
 .../component/salesforce/api/dto/RecentItem.java   |   56 ++
 .../salesforce/api/dto/RecordTypeInfo.java         |   56 ++
 .../component/salesforce/api/dto/RestError.java    |   68 ++
 .../salesforce/api/dto/RestResources.java          |  100 +++
 .../component/salesforce/api/dto/SObject.java      |  237 +++++
 .../salesforce/api/dto/SObjectBasicInfo.java       |   46 +
 .../salesforce/api/dto/SObjectDescription.java     |   67 ++
 .../salesforce/api/dto/SObjectDescriptionUrls.java |   47 +
 .../component/salesforce/api/dto/SObjectField.java |  414 +++++++++
 .../component/salesforce/api/dto/SObjectUrls.java  |   56 ++
 .../component/salesforce/api/dto/SearchResult.java |   49 ++
 .../salesforce/api/dto/SearchResults.java          |   41 +
 .../component/salesforce/api/dto/Version.java      |   52 ++
 .../component/salesforce/api/dto/Versions.java     |   40 +
 .../salesforce/api/dto/bulk/BatchInfo.java         |  342 ++++++++
 .../salesforce/api/dto/bulk/BatchInfoList.java     |   82 ++
 .../salesforce/api/dto/bulk/BatchResult.java       |   82 ++
 .../salesforce/api/dto/bulk/BatchStateEnum.java    |   75 ++
 .../api/dto/bulk/ConcurrencyModeEnum.java          |   66 ++
 .../salesforce/api/dto/bulk/ContentType.java       |   57 ++
 .../component/salesforce/api/dto/bulk/Error.java   |  105 +++
 .../component/salesforce/api/dto/bulk/JobInfo.java |  673 +++++++++++++++
 .../salesforce/api/dto/bulk/JobStateEnum.java      |   72 ++
 .../salesforce/api/dto/bulk/ObjectFactory.java     |  200 +++++
 .../salesforce/api/dto/bulk/OperationEnum.java     |   78 ++
 .../salesforce/api/dto/bulk/QueryResult.java       |   84 ++
 .../salesforce/api/dto/bulk/QueryResultList.java   |   82 ++
 .../component/salesforce/api/dto/bulk/Result.java  |  147 ++++
 .../salesforce/api/dto/bulk/ResultError.java       |  140 +++
 .../component/salesforce/api/dto/bulk/SObject.java |  138 +++
 .../salesforce/api/dto/bulk/StatusCode.java        |  395 +++++++++
 .../salesforce/api/dto/bulk/package-info.java      |   18 +
 .../salesforce/internal/OperationName.java         |   71 ++
 .../salesforce/internal/PayloadFormat.java         |   22 +
 .../salesforce/internal/SalesforceSession.java     |  336 +++++++
 .../internal/client/AbstractClientBase.java        |  196 +++++
 .../salesforce/internal/client/BulkApiClient.java  |   92 ++
 .../internal/client/DefaultBulkApiClient.java      |  481 +++++++++++
 .../internal/client/DefaultRestClient.java         |  364 ++++++++
 .../salesforce/internal/client/RestClient.java     |  177 ++++
 .../internal/client/SalesforceExchange.java        |   36 +
 .../client/SalesforceSecurityListener.java         |  162 ++++
 .../internal/client/SyncResponseCallback.java      |   57 ++
 .../salesforce/internal/dto/LoginError.java        |   47 +
 .../salesforce/internal/dto/LoginToken.java        |   81 ++
 .../internal/dto/NotifyForFieldsEnum.java          |   53 ++
 .../internal/dto/NotifyForOperationsEnum.java      |   52 ++
 .../salesforce/internal/dto/PushTopic.java         |  100 +++
 .../internal/dto/QueryRecordsPushTopic.java        |   38 +
 .../salesforce/internal/dto/RestErrors.java        |   41 +
 .../internal/processor/AbstractRestProcessor.java  |  538 ++++++++++++
 .../processor/AbstractSalesforceProcessor.java     |   84 ++
 .../internal/processor/BulkApiProcessor.java       |  377 ++++++++
 .../internal/processor/JsonRestProcessor.java      |  180 ++++
 .../internal/processor/SalesforceProcessor.java    |   27 +
 .../internal/processor/XmlRestProcessor.java       |  240 +++++
 .../internal/streaming/PushTopicHelper.java        |  222 +++++
 .../internal/streaming/SubscriptionHelper.java     |  372 ++++++++
 .../services/org/apache/camel/component/salesforce |    1 +
 .../salesforce/AbstractBulkApiTestBase.java        |  105 +++
 .../salesforce/AbstractSalesforceTestBase.java     |   60 ++
 .../salesforce/BulkApiBatchIntegrationTest.java    |  109 +++
 .../salesforce/BulkApiJobIntegrationTest.java      |  111 +++
 .../salesforce/BulkApiQueryIntegrationTest.java    |   85 ++
 .../component/salesforce/LoginConfigHelper.java    |   59 ++
 .../salesforce/RestApiIntegrationTest.java         |  408 +++++++++
 .../salesforce/StreamingApiIntegrationTest.java    |  109 +++
 .../camel/component/salesforce/dto/Document.java   |  201 +++++
 .../component/salesforce/dto/Line_Item__c.java     |   74 ++
 .../component/salesforce/dto/Merchandise__c.java   |   61 ++
 .../salesforce/dto/QueryRecordsLine_Item__c.java   |   35 +
 .../internal/SessionIntegrationTest.java           |   79 ++
 .../src/test/resources/log4j.properties            |   18 +
 .../src/test/resources/test-request.csv            |    3 +
 .../src/test/resources/test-request.xml            |   15 +
 .../camel-salesforce-maven-plugin/README.md        |   26 +
 .../camel-salesforce-maven-plugin/pom.xml          |   78 ++
 .../apache/camel/maven/CamelSalesforceMojo.java    |  570 ++++++++++++
 .../src/main/resources/sobject-picklist.vm         |   52 ++
 .../src/main/resources/sobject-pojo.vm             |   60 ++
 .../src/main/resources/sobject-query-records.vm    |   29 +
 .../maven/CamelSalesforceMojoIntegrationTest.java  |   85 ++
 .../src/test/resources/log4j.properties            |   19 +
 components/camel-salesforce/pom.xml                |   38 +
 components/pom.xml                                 |    1 +
 parent/pom.xml                                     |    6 +
 .../karaf/features/src/main/resources/features.xml |   16 +
 109 files changed, 13184 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 394c174..b74e107 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,5 @@ target
 .settings
 .checkstyle
 *.log
+test-salesforce-login.properties
 dependency-reduced-pom.xml

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/apache-camel/pom.xml
----------------------------------------------------------------------
diff --git a/apache-camel/pom.xml b/apache-camel/pom.xml
index 0aeafe4..af957dd 100644
--- a/apache-camel/pom.xml
+++ b/apache-camel/pom.xml
@@ -436,6 +436,10 @@
     </dependency>
     <dependency>
       <groupId>org.apache.camel</groupId>
+      <artifactId>camel-salesforce</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
       <artifactId>camel-sap-netweaver</artifactId>
     </dependency>
     <dependency>

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/apache-camel/src/main/descriptors/common-bin.xml
----------------------------------------------------------------------
diff --git a/apache-camel/src/main/descriptors/common-bin.xml b/apache-camel/src/main/descriptors/common-bin.xml
index 3d08587..8e28551 100644
--- a/apache-camel/src/main/descriptors/common-bin.xml
+++ b/apache-camel/src/main/descriptors/common-bin.xml
@@ -120,6 +120,7 @@
         <include>org.apache.camel:camel-rss</include>
         <include>org.apache.camel:camel-ruby</include>
         <include>org.apache.camel:camel-rx</include>
+        <include>org.apache.camel:camel-salesforce</include>
         <include>org.apache.camel:camel-sap-netweaver</include>
         <include>org.apache.camel:camel-saxon</include>
         <include>org.apache.camel:camel-scala</include>

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/README.md
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/README.md b/components/camel-salesforce/camel-salesforce-component/README.md
new file mode 100644
index 0000000..9bf9845
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/README.md
@@ -0,0 +1,68 @@
+# Camel Salesforce component #
+
+This component supports producer and consumer endpoints to communicate with Salesforce using Java DTOs. 
+There is a companion maven plugin [camel-salesforce-plugin](https://github.com/dhirajsb/camel-salesforce-maven-plugin) that generates these DTOs. 
+
+The component supports the following Salesforce APIs
+
+## REST API ##
+
+Producer endpoints can use the following APIs. Most of the APIs process one record at a time, the Query API can retrieve multiple Records. 
+
+* getVersions - Gets supported Salesforce REST API versions
+* getResources - Gets available Salesforce REST Resource endpoints
+* getGlobalObjects - Gets metadata for all available SObject types
+* getBasicInfo - Gets basic metadata for a specific SObject type
+* getDescription - Gets comprehensive metadata for a specific SObject type
+* getSObject - Gets an SObject using its Salesforce Id
+* createSObject - Creates an SObject
+* updateSObject - Updates an SObject using Id
+* deleteSObject - Deletes an SObject using Id
+* getSObjectWithId - Gets an SObject using an external (user defined) id field
+* upsertSObject - Updates or inserts an SObject using an external id
+* deleteSObjectWithId - Deletes an SObject using an external id
+* query - Runs a Salesforce SOQL query
+* queryMore - Retrieves more results (in case of large number of results) using result link returned from the 'query' API
+* search - Runs a Salesforce SOSL query
+
+For example, the following producer endpoint uses the upsertSObject API, with the sObjectIdName parameter specifying 'Name' as the external id field. 
+The request message body should be an SObject DTO generated using the maven plugin. 
+The response message will either be NULL if an existing record was updated, or [CreateSObjectResult] with an id of the new record, or a list of errors while creating the new object.
+
+	...to("force:upsertSObject?sObjectIdName=Name")...
+
+## Bulk API ##
+
+Producer endpoints can use the following APIs. All Job data formats, i.e. xml, csv, zip/xml, and zip/csv are supported. 
+The request and response have to be marshalled/unmarshalled by the route. Usually the request will be some stream source like a CSV file, 
+and the response may also be saved to a file to be correlated with the request. 
+
+* createJob - Creates a Salesforce Bulk Job
+* getJob - Gets a Job using its Salesforce Id
+* closeJob - Closes a Job
+* abortJob - Aborts a Job
+* createBatch - Submits a Batch within a Bulk Job
+* getBatch - Gets a Batch using Id
+* getAllBatches - Gets all Batches for a Bulk Job Id
+* getRequest - Gets Request data (XML/CSV) for a Batch
+* getResults - Gets the results of the Batch when its complete
+* createBatchQuery - Creates a Batch from an SOQL query
+* getQueryResultIds - Gets a list of Result Ids for a Batch Query
+* getQueryResult - Gets results for a Result Id
+
+For example, the following producer endpoint uses the createBatch API to create a Job Batch. 
+The in message must contain a body that can be converted into an InputStream (usually UTF-8 CSV or XML content from a file, etc.) and header fields 'jobId' for the Job and 'contentType' for the Job content type, which can be XML, CSV, ZIP\_XML or ZIP\_CSV. The put message body will contain [BatchInfo] on success, or throw a [SalesforceException] on error.
+
+	...to("force:createBatchJob")..
+
+## Streaming API ##
+
+Consumer endpoints can use the following sytax for streaming endpoints to receive Salesforce notifications on create/update. 
+
+To create and subscribe to a topic
+
+	from("force:CamelTestTopic?notifyForFields=ALL&notifyForOperations=ALL&sObjectName=Merchandise__c&updateTopic=true&sObjectQuery=SELECT Id, Name FROM Merchandise__c")...
+
+To subscribe to an existing topic
+
+	from("force:CamelTestTopic&sObjectName=Merchandise__c")...

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/pom.xml b/components/camel-salesforce/camel-salesforce-component/pom.xml
new file mode 100644
index 0000000..03db485
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/pom.xml
@@ -0,0 +1,121 @@
+<?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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-salesforce-parent</artifactId>
+    <version>2.12-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-salesforce</artifactId>
+  <packaging>bundle</packaging>
+  <name>Camel :: Salesforce</name>
+  <description>Camel Salesforce support</description>
+
+  <properties>
+    <camel.osgi.export.pkg/>
+    <camel.osgi.export>
+      org.apache.camel.component.salesforce;version=${project.version},
+      org.apache.camel.component.salesforce.api.*;version=${project.version}
+    </camel.osgi.export>
+    <camel.osgi.private.pkg>org.apache.camel.component.salesforce.internal.*</camel.osgi.private.pkg>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-client</artifactId>
+      <version>${jetty-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-util</artifactId>
+      <version>${jetty-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-io</artifactId>
+      <version>${jetty-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>${jackson-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.thoughtworks.xstream</groupId>
+      <artifactId>xstream</artifactId>
+      <version>${xstream-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.cometd.java</groupId>
+      <artifactId>cometd-java-client</artifactId>
+      <version>${cometd-java-client-version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-util</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-io</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>joda-time</groupId>
+      <artifactId>joda-time</artifactId>
+      <version>${jodatime2-bundle-version}</version>
+    </dependency>
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j-api-version}</version>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>${slf4j-api-version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>${log4j-version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
new file mode 100644
index 0000000..a48502a
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
@@ -0,0 +1,233 @@
+/**
+ * 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;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.impl.DefaultComponent;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.RedirectListener;
+import org.apache.camel.component.salesforce.api.SalesforceException;
+import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase;
+import org.apache.camel.component.salesforce.internal.OperationName;
+import org.apache.camel.component.salesforce.internal.SalesforceSession;
+import org.apache.camel.component.salesforce.internal.streaming.SubscriptionHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents the component that manages {@link SalesforceEndpoint}.
+ */
+public class SalesforceComponent extends DefaultComponent {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SalesforceComponent.class);
+
+    private static final int MAX_CONNECTIONS_PER_ADDRESS = 20;
+    private static final int CONNECTION_TIMEOUT = 60000;
+    private static final int RESPONSE_TIMEOUT = 60000;
+
+    private SalesforceLoginConfig loginConfig;
+    private SalesforceEndpointConfig config;
+    private String[] packages;
+
+    // component state
+    private HttpClient httpClient;
+    private SalesforceSession session;
+    private Map<String, Class<?>> classMap;
+
+    // Lazily created helper for consumer endpoints
+    private SubscriptionHelper subscriptionHelper;
+
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+        // get Operation from remaining URI
+        OperationName operationName = null;
+        String topicName = null;
+        try {
+            LOG.debug("Creating endpoint for ", remaining);
+            operationName = OperationName.fromValue(remaining);
+        } catch (IllegalArgumentException ex) {
+            // if its not an operation name, treat is as topic name for consumer endpoints
+            topicName = remaining;
+        }
+
+        // create endpoint config
+        if (config == null) {
+            config = new SalesforceEndpointConfig();
+        }
+        if (config.getHttpClient() == null) {
+            // set the component's httpClient as default
+            config.setHttpClient(httpClient);
+        }
+
+        // create a deep copy and map parameters
+        final SalesforceEndpointConfig copy = config.copy();
+        setProperties(copy, parameters);
+
+        final SalesforceEndpoint endpoint = new SalesforceEndpoint(uri, this, copy,
+            operationName, topicName);
+
+        // map remaining parameters to endpoint (specifically, synchronous)
+        setProperties(endpoint, parameters);
+
+        return endpoint;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        // validate properties
+        ObjectHelper.notNull(loginConfig, "loginConfig");
+
+        // create a Jetty HttpClient if not already set
+        if (null == httpClient) {
+            if (config != null && config.getHttpClient() != null) {
+                httpClient = config.getHttpClient();
+            } else {
+                httpClient = new HttpClient();
+                httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
+                httpClient.setMaxConnectionsPerAddress(MAX_CONNECTIONS_PER_ADDRESS);
+                httpClient.setConnectTimeout(CONNECTION_TIMEOUT);
+                httpClient.setTimeout(RESPONSE_TIMEOUT);
+            }
+        }
+
+        // add redirect listener to handle Salesforce redirects
+        // this is ok to do since the RedirectListener is in the same classloader as Jetty client
+        String listenerClass = RedirectListener.class.getName();
+        if (httpClient.getRegisteredListeners() == null ||
+            !httpClient.getRegisteredListeners().contains(listenerClass)) {
+            httpClient.registerListener(listenerClass);
+        }
+        // SalesforceSecurityListener can't be registered the same way
+        // since Jetty HttpClient's Class.forName() can't see it
+
+        // start the Jetty client to initialize thread pool, etc.
+        httpClient.start();
+
+        // support restarts
+        if (null == this.session) {
+            this.session = new SalesforceSession(httpClient, loginConfig);
+        }
+
+        // login at startup if lazyLogin is disabled
+        if (!loginConfig.isLazyLogin()) {
+            ServiceHelper.startService(session);
+        }
+
+        if (packages != null && packages.length > 0) {
+            // parse the packages to create SObject name to class map
+            classMap = parsePackages();
+        } else {
+            // use an empty map to avoid NPEs later
+            LOG.warn("Missing property packages, getSObject* operations will NOT work");
+            classMap = Collections.unmodifiableMap(new HashMap<String, Class<?>>());
+        }
+
+        if (subscriptionHelper != null) {
+            ServiceHelper.startService(subscriptionHelper);
+        }
+    }
+
+    private Map<String, Class<?>> parsePackages() {
+        Map<String, Class<?>> result = new HashMap<String, Class<?>>();
+        Set<Class<?>> classes = getCamelContext().getPackageScanClassResolver().findImplementations(AbstractSObjectBase.class, packages);
+        for (Class<?> aClass : classes) {
+            // findImplementations also returns AbstractSObjectBase for some reason!!!
+            if (AbstractSObjectBase.class != aClass) {
+                result.put(aClass.getSimpleName(), aClass);
+            }
+        }
+
+        return Collections.unmodifiableMap(result);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+
+        try {
+            if (subscriptionHelper != null) {
+                // shutdown all streaming connections
+                // note that this is done in the component, and not in consumer
+                ServiceHelper.stopService(subscriptionHelper);
+            }
+            if (session != null && session.getAccessToken() != null) {
+                try {
+                    // logout of Salesforce
+                    ServiceHelper.stopService(session);
+                } catch (SalesforceException ignored) {
+                }
+            }
+        } finally {
+            if (httpClient != null) {
+                // shutdown http client connections
+                httpClient.stop();
+            }
+        }
+    }
+
+    public SubscriptionHelper getSubscriptionHelper() throws Exception {
+        if (subscriptionHelper == null) {
+            // lazily create subscription helper
+            subscriptionHelper = new SubscriptionHelper(this);
+
+            // also start the helper to connect to Salesforce
+            ServiceHelper.startService(subscriptionHelper);
+        }
+        return subscriptionHelper;
+    }
+
+    public SalesforceLoginConfig getLoginConfig() {
+        return loginConfig;
+    }
+
+    public void setLoginConfig(SalesforceLoginConfig loginConfig) {
+        this.loginConfig = loginConfig;
+    }
+
+    public SalesforceEndpointConfig getConfig() {
+        return config;
+    }
+
+    public void setConfig(SalesforceEndpointConfig config) {
+        this.config = config;
+    }
+
+    public String[] getPackages() {
+        return packages;
+    }
+
+    public void setPackages(String[] packages) {
+        this.packages = packages;
+    }
+
+    public SalesforceSession getSession() {
+        return session;
+    }
+
+    public Map<String, Class<?>> getClassMap() {
+        return classMap;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceConsumer.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceConsumer.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceConsumer.java
new file mode 100644
index 0000000..9fa1b91
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceConsumer.java
@@ -0,0 +1,215 @@
+/**
+ * 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;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.impl.DefaultConsumer;
+import org.apache.camel.util.ServiceHelper;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.cometd.bayeux.Message;
+import org.cometd.bayeux.client.ClientSessionChannel;
+import org.apache.camel.component.salesforce.internal.client.DefaultRestClient;
+import org.apache.camel.component.salesforce.internal.streaming.PushTopicHelper;
+import org.apache.camel.component.salesforce.internal.client.RestClient;
+import org.apache.camel.component.salesforce.internal.streaming.SubscriptionHelper;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The Salesforce consumer.
+ */
+public class SalesforceConsumer extends DefaultConsumer {
+
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+    private static final String EVENT_PROPERTY = "event";
+    private static final String TYPE_PROPERTY = "type";
+    private static final String CREATED_DATE_PROPERTY = "createdDate";
+    private static final String SOBJECT_PROPERTY = "sobject";
+    private static final double MINIMUM_VERSION = 24.0;
+
+    private final SalesforceEndpoint endpoint;
+    public final SubscriptionHelper subscriptionHelper;
+
+    private final String topicName;
+    private final Class<?> sObjectClass;
+    private boolean subscribed;
+
+    public SalesforceConsumer(SalesforceEndpoint endpoint, Processor processor, SubscriptionHelper helper) {
+        super(endpoint, processor);
+        this.endpoint = endpoint;
+
+        // check minimum supported API version
+        if (Double.valueOf(endpoint.getConfiguration().getApiVersion()) < MINIMUM_VERSION) {
+            throw new IllegalArgumentException("Minimum supported API version for consumer endpoints is " + 24.0);
+        }
+
+        this.topicName = endpoint.getTopicName();
+        this.subscriptionHelper = helper;
+
+        // get sObjectClass to convert to
+        final String sObjectName = endpoint.getConfiguration().getSObjectName();
+        if (sObjectName != null) {
+            sObjectClass = endpoint.getComponent().getClassMap().get(sObjectName);
+            if (sObjectClass == null) {
+                throw new IllegalArgumentException(String.format("SObject Class not found for %s", sObjectName));
+            }
+        } else {
+            final String className = endpoint.getConfiguration().getSObjectClass();
+            if (className != null) {
+                sObjectClass = endpoint.getComponent().getCamelContext().getClassResolver().resolveClass(className);
+                if (sObjectClass == null) {
+                    throw new IllegalArgumentException(String.format("SObject Class not found %s", className));
+                }
+            } else {
+                log.warn("Property sObjectName or sObjectClass NOT set, messages will be of type java.lang.Map");
+                sObjectClass = null;
+            }
+        }
+
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        final SalesforceEndpointConfig config = endpoint.getConfiguration();
+
+        // is a query configured in the endpoint?
+        if (config.getSObjectQuery() != null) {
+            // Note that we don't lookup topic if the query is not specified
+            // create REST client for PushTopic operations
+            SalesforceComponent component = endpoint.getComponent();
+            RestClient restClient = new DefaultRestClient(component.getConfig().getHttpClient(),
+                endpoint.getConfiguration().getApiVersion(), "json", component.getSession());
+            // don't forget to start the client
+            ServiceHelper.startService(restClient);
+
+            try {
+                PushTopicHelper helper = new PushTopicHelper(config, topicName, restClient);
+                helper.createOrUpdateTopic();
+            } finally {
+                // don't forget to stop the client
+                ServiceHelper.stopService(restClient);
+            }
+        }
+
+        // subscribe to topic
+        subscriptionHelper.subscribe(topicName, this);
+        subscribed = true;
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+
+        if (subscribed) {
+            subscribed = false;
+            // unsubscribe from topic
+            subscriptionHelper.unsubscribe(topicName, this);
+        }
+    }
+
+    public void processMessage(ClientSessionChannel channel, Message message) {
+        final Exchange exchange = endpoint.createExchange();
+        org.apache.camel.Message in = exchange.getIn();
+        setHeaders(in, message);
+
+        // get event data
+        // TODO do we need to add NPE checks for message/data.get***???
+        Map<String, Object> data = message.getDataAsMap();
+
+        @SuppressWarnings("unchecked") final
+        Map<String, Object> event = (Map<String, Object>) data.get(EVENT_PROPERTY);
+        final Object eventType = event.get(TYPE_PROPERTY);
+        Object createdDate = event.get(CREATED_DATE_PROPERTY);
+        if (log.isDebugEnabled()) {
+            log.debug(String.format("Received event %s on channel %s created on %s",
+                eventType, channel.getChannelId(), createdDate));
+        }
+
+        in.setHeader("CamelSalesforceEventType", eventType);
+        in.setHeader("CamelSalesforceCreatedDate", createdDate);
+
+        // get SObject
+        @SuppressWarnings("unchecked")
+        final Map<String, Object> sObject = (Map<String, Object>) data.get(SOBJECT_PROPERTY);
+        try {
+
+            final String sObjectString = objectMapper.writeValueAsString(sObject);
+            log.debug("Received SObject: {}", sObjectString);
+
+            if (sObjectClass == null) {
+                // return sobject map as exchange body
+                in.setBody(sObject);
+            } else {
+                // create the expected SObject
+                in.setBody(objectMapper.readValue(
+                    new StringReader(sObjectString), sObjectClass));
+            }
+        } catch (IOException e) {
+            final String msg = String.format("Error parsing message [%s] from Topic %s: %s",
+                message, topicName, e.getMessage());
+            handleException(msg, new RuntimeCamelException(msg, e));
+        }
+
+        try {
+            getAsyncProcessor().process(exchange, new AsyncCallback() {
+                public void done(boolean doneSync) {
+                    // noop
+                    if (log.isTraceEnabled()) {
+                        log.trace("Done processing event: {} {}", eventType.toString(),
+                            doneSync ? "synchronously" : "asynchronously");
+                    }
+                }
+            });
+        } catch (Exception e) {
+            handleException(String.format("Error processing %s: %s", exchange, e.getMessage()), e);
+        } finally {
+            Exception ex = exchange.getException();
+            if (ex != null) {
+                handleException(String.format("Unhandled exception: %s", ex.getMessage()), ex);
+            }
+        }
+    }
+
+    private void setHeaders(org.apache.camel.Message in, Message message) {
+        Map<String, Object> headers = new HashMap<String, Object>();
+        // set topic name
+        headers.put("CamelSalesforceTopicName", topicName);
+        // set message properties as headers
+        headers.put("CamelSalesforceChannel", message.getChannel());
+        headers.put("CamelSalesforceClientId", message.getClientId());
+
+        in.setHeaders(headers);
+    }
+
+    @Override
+    public void handleException(String message, Throwable t) {
+        super.handleException(message, t);
+    }
+
+    public String getTopicName() {
+        return topicName;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpoint.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpoint.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpoint.java
new file mode 100644
index 0000000..24874bb
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpoint.java
@@ -0,0 +1,96 @@
+/**
+ * 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;
+
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.impl.DefaultEndpoint;
+import org.apache.camel.impl.SynchronousDelegateProducer;
+import org.apache.camel.component.salesforce.internal.OperationName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Represents a Salesforce endpoint.
+ */
+public class SalesforceEndpoint extends DefaultEndpoint {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SalesforceEndpoint.class);
+
+    private final SalesforceEndpointConfig config;
+    private final OperationName operationName;
+    private final String topicName;
+
+    public SalesforceEndpoint(String uri, SalesforceComponent salesforceComponent,
+                              SalesforceEndpointConfig config, OperationName operationName, String topicName) {
+        super(uri, salesforceComponent);
+
+        this.config = config;
+        this.operationName = operationName;
+        this.topicName = topicName;
+    }
+
+    public Producer createProducer() throws Exception {
+        // producer requires an operation, topicName must be the invalid operation name
+        if (operationName == null) {
+            throw new IllegalArgumentException(String.format("Invalid Operation %s", topicName));
+        }
+
+        SalesforceProducer producer = new SalesforceProducer(this);
+        if (isSynchronous()) {
+            return new SynchronousDelegateProducer(producer);
+        } else {
+            return producer;
+        }
+    }
+
+    public Consumer createConsumer(Processor processor) throws Exception {
+        // consumer requires a topicName, operation name must be the invalid topic name
+        if (topicName == null) {
+            throw new IllegalArgumentException(String.format("Invalid topic name %s, matches a producer operation name",
+                operationName.value()));
+        }
+
+        return new SalesforceConsumer(this, processor,
+            getComponent().getSubscriptionHelper());
+    }
+
+    @Override
+    public SalesforceComponent getComponent() {
+        return (SalesforceComponent) super.getComponent();
+    }
+
+    public boolean isSingleton() {
+        // re-use endpoint instance across multiple threads
+        // the description of this method is a little confusing
+        return true;
+    }
+
+    public SalesforceEndpointConfig getConfiguration() {
+        return config;
+    }
+
+    public OperationName getOperationName() {
+        return operationName;
+    }
+
+    public String getTopicName() {
+        return topicName;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..817871b
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java
@@ -0,0 +1,292 @@
+/**
+ * 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;
+
+import org.apache.camel.RuntimeCamelException;
+import org.eclipse.jetty.client.HttpClient;
+import org.apache.camel.component.salesforce.api.dto.bulk.ContentType;
+import org.apache.camel.component.salesforce.api.dto.bulk.OperationEnum;
+import org.apache.camel.component.salesforce.internal.PayloadFormat;
+import org.apache.camel.component.salesforce.internal.dto.NotifyForFieldsEnum;
+import org.apache.camel.component.salesforce.internal.dto.NotifyForOperationsEnum;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SalesforceEndpointConfig implements Cloneable {
+
+    // default API version
+    static final String DEFAULT_VERSION = "27.0";
+
+    // general parameter
+    public static final String API_VERSION = "apiVersion";
+
+    // parameters for Rest API
+    public static final String FORMAT = "format";
+    public static final String SOBJECT_NAME = "sObjectName";
+    public static final String SOBJECT_ID = "sObjectId";
+    public static final String SOBJECT_FIELDS = "sObjectFields";
+    public static final String SOBJECT_EXT_ID_NAME = "sObjectIdName";
+    public static final String SOBJECT_EXT_ID_VALUE = "sObjectIdValue";
+    public static final String SOBJECT_BLOB_FIELD_NAME = "sObjectBlobFieldName";
+    public static final String SOBJECT_CLASS = "sObjectClass";
+    public static final String SOBJECT_QUERY = "sObjectQuery";
+    public static final String SOBJECT_SEARCH = "sObjectSearch";
+
+    // parameters for Bulk API
+    public static final String BULK_OPERATION = "bulkOperation";
+    public static final String CONTENT_TYPE = "contentType";
+    public static final String JOB_ID = "jobId";
+    public static final String BATCH_ID = "batchId";
+    public static final String RESULT_ID = "resultId";
+
+    // parameters for Streaming API
+    public static final String UPDATE_TOPIC = "updateTopic";
+
+    // general properties
+    private String apiVersion = DEFAULT_VERSION;
+
+    // Rest API properties
+    private PayloadFormat format = PayloadFormat.JSON;
+    private String sObjectName;
+    private String sObjectId;
+    private String sObjectFields;
+    private String sObjectIdName;
+    private String sObjectIdValue;
+    private String sObjectBlobFieldName;
+    private String sObjectClass;
+    private String sObjectQuery;
+    private String sObjectSearch;
+
+    // Bulk API properties
+    private OperationEnum bulkOperation;
+    private ContentType contentType;
+    private String jobId;
+    private String batchId;
+    private String resultId;
+
+    // Streaming API properties
+    private boolean updateTopic;
+    private NotifyForFieldsEnum notifyForFields;
+    private NotifyForOperationsEnum notifyForOperations;
+
+    // Jetty HttpClient, set using reference
+    private HttpClient httpClient;
+
+    public SalesforceEndpointConfig copy() {
+        try {
+            final SalesforceEndpointConfig copy = (SalesforceEndpointConfig) super.clone();
+            // nothing to deep copy
+            return copy;
+        } catch (CloneNotSupportedException ex) {
+            throw new RuntimeCamelException(ex);
+        }
+    }
+
+    public PayloadFormat getPayloadFormat() {
+        return format;
+    }
+
+    public void setFormat(String format) {
+        this.format = PayloadFormat.valueOf(format.toUpperCase());
+    }
+
+    public String getApiVersion() {
+        return apiVersion;
+    }
+
+    public void setApiVersion(String apiVersion) {
+        this.apiVersion = apiVersion;
+    }
+
+    public String getSObjectName() {
+        return sObjectName;
+    }
+
+    public void setSObjectName(String sObjectName) {
+        this.sObjectName = sObjectName;
+    }
+
+    public String getSObjectId() {
+        return sObjectId;
+    }
+
+    public void setSObjectId(String sObjectId) {
+        this.sObjectId = sObjectId;
+    }
+
+    public String getSObjectFields() {
+        return sObjectFields;
+    }
+
+    public void setSObjectFields(String sObjectFields) {
+        this.sObjectFields = sObjectFields;
+    }
+
+    public String getSObjectIdName() {
+        return sObjectIdName;
+    }
+
+    public void setSObjectIdName(String sObjectIdName) {
+        this.sObjectIdName = sObjectIdName;
+    }
+
+    public String getSObjectIdValue() {
+        return sObjectIdValue;
+    }
+
+    public void setSObjectIdValue(String sObjectIdValue) {
+        this.sObjectIdValue = sObjectIdValue;
+    }
+
+    public String getSObjectBlobFieldName() {
+        return sObjectBlobFieldName;
+    }
+
+    public void setSObjectBlobFieldName(String sObjectBlobFieldName) {
+        this.sObjectBlobFieldName = sObjectBlobFieldName;
+    }
+
+    public String getSObjectClass() {
+        return sObjectClass;
+    }
+
+    public void setSObjectClass(String sObjectClass) {
+        this.sObjectClass = sObjectClass;
+    }
+
+    public String getSObjectQuery() {
+        return sObjectQuery;
+    }
+
+    public void setSObjectQuery(String sObjectQuery) {
+        this.sObjectQuery = sObjectQuery;
+    }
+
+    public String getSObjectSearch() {
+        return sObjectSearch;
+    }
+
+    public void setSObjectSearch(String sObjectSearch) {
+        this.sObjectSearch = sObjectSearch;
+    }
+
+    public OperationEnum getBulkOperation() {
+        return bulkOperation;
+    }
+
+    public void setBulkOperation(OperationEnum bulkOperation) {
+        this.bulkOperation = bulkOperation;
+    }
+
+    public ContentType getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(ContentType contentType) {
+        this.contentType = contentType;
+    }
+
+    public String getJobId() {
+        return jobId;
+    }
+
+    public void setJobId(String jobId) {
+        this.jobId = jobId;
+    }
+
+    public String getBatchId() {
+        return batchId;
+    }
+
+    public void setBatchId(String batchId) {
+        this.batchId = batchId;
+    }
+
+    public String getResultId() {
+        return resultId;
+    }
+
+    public void setResultId(String resultId) {
+        this.resultId = resultId;
+    }
+
+    public boolean isUpdateTopic() {
+        return updateTopic;
+    }
+
+    public void setUpdateTopic(boolean updateTopic) {
+        this.updateTopic = updateTopic;
+    }
+
+    public NotifyForFieldsEnum getNotifyForFields() {
+        return notifyForFields;
+    }
+
+    public void setNotifyForFields(NotifyForFieldsEnum notifyForFields) {
+        this.notifyForFields = notifyForFields;
+    }
+
+    public NotifyForOperationsEnum getNotifyForOperations() {
+        return notifyForOperations;
+    }
+
+    public void setNotifyForOperations(NotifyForOperationsEnum notifyForOperations) {
+        this.notifyForOperations = notifyForOperations;
+    }
+
+    public void setHttpClient(HttpClient httpClient) {
+        this.httpClient = httpClient;
+    }
+
+    public HttpClient getHttpClient() {
+        return httpClient;
+    }
+
+    public Map<String, String> toValueMap() {
+
+        final Map<String, String> valueMap = new HashMap<String, String>();
+        valueMap.put(FORMAT, format.toString().toLowerCase());
+        valueMap.put(API_VERSION, apiVersion);
+
+        valueMap.put(SOBJECT_NAME, sObjectName);
+        valueMap.put(SOBJECT_ID, sObjectId);
+        valueMap.put(SOBJECT_FIELDS, sObjectFields);
+        valueMap.put(SOBJECT_EXT_ID_NAME, sObjectIdName);
+        valueMap.put(SOBJECT_BLOB_FIELD_NAME, sObjectBlobFieldName);
+        valueMap.put(SOBJECT_EXT_ID_VALUE, sObjectIdValue);
+        valueMap.put(SOBJECT_CLASS, sObjectClass);
+        valueMap.put(SOBJECT_QUERY, sObjectQuery);
+        valueMap.put(SOBJECT_SEARCH, sObjectSearch);
+
+        // add bulk API properties
+        if (bulkOperation != null) {
+            valueMap.put(BULK_OPERATION, bulkOperation.value());
+        }
+        if (contentType != null) {
+            valueMap.put(CONTENT_TYPE, contentType.value());
+        }
+        valueMap.put(JOB_ID, jobId);
+        valueMap.put(BATCH_ID, batchId);
+        valueMap.put(RESULT_ID, resultId);
+
+        valueMap.put(UPDATE_TOPIC, String.valueOf(updateTopic));
+
+        return Collections.unmodifiableMap(valueMap);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java
new file mode 100644
index 0000000..a6a097f
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceLoginConfig.java
@@ -0,0 +1,99 @@
+/**
+ * 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;
+
+/**
+ * Configuration object for Salesforce login properties
+ */
+public class SalesforceLoginConfig {
+
+    public static final String DEFAULT_LOGIN_URL = "https://login.salesforce.com";
+
+    private String loginUrl;
+    private String clientId;
+    private String clientSecret;
+    private String userName;
+    private String password;
+    // allow lazy login into Salesforce
+    // note that login issues may not surface until a message needs to be processed
+    private boolean lazyLogin;
+
+    public SalesforceLoginConfig() {
+        loginUrl = DEFAULT_LOGIN_URL;
+        lazyLogin = false;
+    }
+
+    public SalesforceLoginConfig(String loginUrl,
+                                 String clientId, String clientSecret,
+                                 String userName, String password, boolean lazyLogin) {
+        this.loginUrl = loginUrl;
+        this.clientId = clientId;
+        this.clientSecret = clientSecret;
+        this.userName = userName;
+        this.password = password;
+        this.lazyLogin = lazyLogin;
+    }
+
+    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 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 isLazyLogin() {
+        return lazyLogin;
+    }
+
+    public void setLazyLogin(boolean lazyLogin) {
+        this.lazyLogin = lazyLogin;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceProducer.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceProducer.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceProducer.java
new file mode 100644
index 0000000..5cc4547
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceProducer.java
@@ -0,0 +1,102 @@
+/**
+ * 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;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.impl.DefaultAsyncProducer;
+import org.apache.camel.util.ServiceHelper;
+import org.apache.camel.component.salesforce.api.SalesforceException;
+import org.apache.camel.component.salesforce.internal.OperationName;
+import org.apache.camel.component.salesforce.internal.PayloadFormat;
+import org.apache.camel.component.salesforce.internal.processor.BulkApiProcessor;
+import org.apache.camel.component.salesforce.internal.processor.JsonRestProcessor;
+import org.apache.camel.component.salesforce.internal.processor.SalesforceProcessor;
+import org.apache.camel.component.salesforce.internal.processor.XmlRestProcessor;
+
+/**
+ * The Salesforce producer.
+ */
+public class SalesforceProducer extends DefaultAsyncProducer {
+
+    private final SalesforceProcessor processor;
+
+    public SalesforceProducer(SalesforceEndpoint endpoint) throws SalesforceException {
+        super(endpoint);
+
+        final SalesforceEndpointConfig endpointConfig = endpoint.getConfiguration();
+        final PayloadFormat payloadFormat = endpointConfig.getPayloadFormat();
+
+        // check if its a Bulk Operation
+        if (isBulkOperation(endpoint.getOperationName())) {
+            processor = new BulkApiProcessor(endpoint);
+        } else {
+            // create an appropriate processor
+            if (payloadFormat == PayloadFormat.JSON) {
+                // create a JSON exchange processor
+                processor = new JsonRestProcessor(endpoint);
+            } else {
+                processor = new XmlRestProcessor(endpoint);
+            }
+        }
+    }
+
+    private boolean isBulkOperation(OperationName operationName) {
+        switch (operationName) {
+            case CREATE_JOB:
+            case GET_JOB:
+            case CLOSE_JOB:
+            case ABORT_JOB:
+            case CREATE_BATCH:
+            case GET_BATCH:
+            case GET_ALL_BATCHES:
+            case GET_REQUEST:
+            case GET_RESULTS:
+            case CREATE_BATCH_QUERY:
+            case GET_QUERY_RESULT_IDS:
+            case GET_QUERY_RESULT:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean process(Exchange exchange, AsyncCallback callback) {
+        log.debug("Processing {}",
+            ((SalesforceEndpoint) getEndpoint()).getOperationName());
+        return processor.process(exchange, callback);
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        // start Salesforce processor
+        ServiceHelper.startService(processor);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        // stop Salesforce processor
+        ServiceHelper.stopService(processor);
+
+        super.doStop();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/JodaTimeConverter.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/JodaTimeConverter.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/JodaTimeConverter.java
new file mode 100644
index 0000000..372e7a4
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/JodaTimeConverter.java
@@ -0,0 +1,64 @@
+/**
+ * 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.api;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Constructor;
+
+public class JodaTimeConverter implements Converter {
+    private static final Logger LOG = LoggerFactory.getLogger(JodaTimeConverter.class);
+    private final DateTimeFormatter formatter = ISODateTimeFormat.dateTime();
+
+    @Override
+    public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext context) {
+        DateTime dateTime = (DateTime) o;
+        writer.setValue(formatter.print(dateTime));
+    }
+
+    @Override
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        String dateTimeStr = reader.getValue();
+        Class<?> requiredType = context.getRequiredType();
+        try {
+            Constructor<?> constructor = requiredType.getConstructor(Object.class, DateTimeZone.class);
+            // normalize date time to UTC
+            return constructor.newInstance(dateTimeStr, DateTimeZone.UTC);
+        } catch (Exception e) {
+            throw new IllegalArgumentException(
+                String.format("Error reading Joda DateTime from value %s: %s",
+                    dateTimeStr, e.getMessage()),
+                e);
+        }
+    }
+
+    @Override
+    public boolean canConvert(Class aClass) {
+        return DateTime.class.isAssignableFrom(aClass);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/PicklistEnumConverter.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/PicklistEnumConverter.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/PicklistEnumConverter.java
new file mode 100644
index 0000000..06d0ca5
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/PicklistEnumConverter.java
@@ -0,0 +1,80 @@
+/**
+ * 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.api;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+
+public class PicklistEnumConverter implements Converter {
+    private static final Logger LOG = LoggerFactory.getLogger(PicklistEnumConverter.class);
+    private static final String FACTORY_METHOD = "fromValue";
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext context) {
+        Class<?> aClass = o.getClass();
+        try {
+            Method getterMethod = aClass.getMethod("value");
+            writer.setValue((String) getterMethod.invoke(o));
+        } catch (ReflectiveOperationException e) {
+            throw new IllegalArgumentException(
+                String.format("Exception writing pick list value %s of type %s: %s",
+                    o, o.getClass().getName(), e.getMessage()),
+                e);
+        }
+    }
+
+    @Override
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        String value = reader.getValue();
+        Class<?> requiredType = context.getRequiredType();
+        try {
+            Method factoryMethod = requiredType.getMethod(FACTORY_METHOD, String.class);
+            // use factory method to create object
+            return factoryMethod.invoke(null, value);
+        } catch (ReflectiveOperationException e) {
+            throw new IllegalArgumentException(
+                String.format("Exception reading pick list value %s of type %s: %s",
+                    value, context.getRequiredType().getName(), e.getMessage()),
+                e);
+        } catch (SecurityException e) {
+            throw new IllegalArgumentException(
+                String.format("Security Exception reading pick list value %s of type %s: %s",
+                    value, context.getRequiredType().getName(), e.getMessage()),
+                e);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public boolean canConvert(Class aClass) {
+        try {
+            return Enum.class.isAssignableFrom(aClass) &&
+                aClass.getMethod(FACTORY_METHOD, String.class) != null;
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java
new file mode 100644
index 0000000..760eebe
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java
@@ -0,0 +1,104 @@
+/**
+ * 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.api;
+
+import org.apache.camel.CamelException;
+import org.apache.camel.component.salesforce.api.dto.RestError;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class SalesforceException extends CamelException {
+
+    private List<RestError> errors;
+    private int statusCode;
+
+    public SalesforceException(List<RestError> errors, int statusCode) {
+        this(toErrorMessage(errors, statusCode), statusCode);
+
+        this.errors = errors;
+    }
+
+    public SalesforceException(String message, int statusCode) {
+        super(message);
+
+        this.statusCode = statusCode;
+    }
+
+    public SalesforceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public SalesforceException(Throwable cause) {
+        super(cause);
+    }
+
+    public List<RestError> getErrors() {
+        return Collections.unmodifiableList(errors);
+    }
+
+    public void setErrors(List<RestError> errors) {
+        if (this.errors != null) {
+            this.errors.clear();
+        } else {
+            this.errors = new ArrayList<RestError>();
+        }
+        this.errors.addAll(errors);
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    public void setStatusCode(int statusCode) {
+        this.statusCode = statusCode;
+    }
+
+    @Override
+    public String toString() {
+        if (errors != null) {
+            return toErrorMessage(errors, statusCode);
+        } else {
+            // make sure we include the custom message
+            final StringBuilder builder = new StringBuilder("{ ");
+            builder.append(getMessage());
+            builder.append(", statusCode: ");
+            builder.append(statusCode);
+            builder.append("}");
+
+            return builder.toString();
+        }
+    }
+
+    private static String toErrorMessage(List<RestError> errors, int statusCode) {
+        StringBuilder builder = new StringBuilder("{ ");
+        if (errors != null) {
+            builder.append(" errors: [");
+            for (RestError error : errors) {
+                builder.append(error.toString());
+            }
+            builder.append("], ");
+        }
+        builder.append("statusCode: ");
+        builder.append(statusCode);
+        builder.append("}");
+
+        return builder.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractDTOBase.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractDTOBase.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractDTOBase.java
new file mode 100644
index 0000000..e4723ae
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractDTOBase.java
@@ -0,0 +1,40 @@
+/**
+ * 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.api.dto;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+// disable null values in json output
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+public abstract class AbstractDTOBase {
+    private final static ObjectMapper mapper = new ObjectMapper();
+
+    @Override
+    public String toString() {
+        try {
+            StringWriter writer = new StringWriter();
+            mapper.writeValue(writer, this);
+            return writer.toString();
+        } catch (IOException e) {
+            return "Error in toString " + e.getMessage();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractQueryRecordsBase.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractQueryRecordsBase.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractQueryRecordsBase.java
new file mode 100644
index 0000000..ddfa6bf
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractQueryRecordsBase.java
@@ -0,0 +1,72 @@
+/**
+ * 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.api.dto;
+
+/**
+ * Abstract base DTO for Salesforce SOQL Query records.
+ * <p>
+ * Derived classes must follow the template below:
+ * </p>
+ * <pre>
+ * {@code
+ * public class QueryResultMySObject extends AbstractQueryRecordsBase {
+ *     @XStreamImplicit
+ *     private List<MySObject> records;
+ *
+ *     public List<MySObject> getRecords() {
+ *         return records;
+ *     }
+ *
+ *     public void setRecords(List<MySObject> records) {
+ *         this.records = records;
+ *     }
+ *
+ * }
+ * }
+ * </pre>
+ */
+public abstract class AbstractQueryRecordsBase extends AbstractDTOBase {
+
+    private Boolean done;
+    private int totalSize;
+    private String nextRecordsUrl;
+
+    public Boolean getDone() {
+        return done;
+    }
+
+    public void setDone(Boolean done) {
+        this.done = done;
+    }
+
+    public int getTotalSize() {
+        return totalSize;
+    }
+
+    public void setTotalSize(int totalSize) {
+        this.totalSize = totalSize;
+    }
+
+    public String getNextRecordsUrl() {
+        return nextRecordsUrl;
+    }
+
+    public void setNextRecordsUrl(String nextRecordsUrl) {
+        this.nextRecordsUrl = nextRecordsUrl;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractSObjectBase.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractSObjectBase.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractSObjectBase.java
new file mode 100644
index 0000000..c236872
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/AbstractSObjectBase.java
@@ -0,0 +1,172 @@
+/**
+ * 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.api.dto;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.joda.time.DateTime;
+
+public class AbstractSObjectBase extends AbstractDTOBase {
+
+    private Attributes attributes;
+
+    private String Id;
+
+    private String OwnerId;
+
+    private Boolean IsDeleted;
+
+    private String Name;
+
+    private DateTime CreatedDate;
+
+    private String CreatedById;
+
+    private DateTime LastModifiedDate;
+
+    private String LastModifiedById;
+
+    private DateTime SystemModstamp;
+
+    private String LastActivityDate;
+
+    /**
+     * Utility method to clear all {@link AbstractSObjectBase} fields.
+     * <p>Used when reusing a DTO for a new record.</p>
+     */
+    public final void clearBaseFields() {
+        attributes = null;
+        Id = null;
+        OwnerId = null;
+        IsDeleted = null;
+        Name = null;
+        CreatedDate = null;
+        CreatedById = null;
+        LastModifiedDate = null;
+        LastModifiedById = null;
+        SystemModstamp = null;
+        LastActivityDate = null;
+    }
+
+    public Attributes getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Attributes attributes) {
+        this.attributes = attributes;
+    }
+
+    @JsonProperty("Id")
+    public String getId() {
+        return Id;
+    }
+
+    @JsonProperty("Id")
+    public void setId(String id) {
+        Id = id;
+    }
+
+    @JsonProperty("OwnerId")
+    public String getOwnerId() {
+        return OwnerId;
+    }
+
+    @JsonProperty("OwnerId")
+    public void setOwnerId(String ownerId) {
+        OwnerId = ownerId;
+    }
+
+    @JsonProperty("IsDeleted")
+    public Boolean isIsDeleted() {
+        return IsDeleted;
+    }
+
+    @JsonProperty("IsDeleted")
+    public void setIsDeleted(Boolean isDeleted) {
+        IsDeleted = isDeleted;
+    }
+
+    @JsonProperty("Name")
+    public String getName() {
+        return Name;
+    }
+
+    @JsonProperty("Name")
+    public void setName(String name) {
+        Name = name;
+    }
+
+    @JsonProperty("CreatedDate")
+    public DateTime getCreatedDate() {
+        return CreatedDate;
+    }
+
+    @JsonProperty("CreatedDate")
+    public void setCreatedDate(DateTime createdDate) {
+        CreatedDate = createdDate;
+    }
+
+    @JsonProperty("CreatedById")
+    public String getCreatedById() {
+        return CreatedById;
+    }
+
+    @JsonProperty("CreatedById")
+    public void setCreatedById(String createdById) {
+        CreatedById = createdById;
+    }
+
+    @JsonProperty("LastModifiedDate")
+    public DateTime getLastModifiedDate() {
+        return LastModifiedDate;
+    }
+
+    @JsonProperty("LastModifiedDate")
+    public void setLastModifiedDate(DateTime lastModifiedDate) {
+        LastModifiedDate = lastModifiedDate;
+    }
+
+    @JsonProperty("LastModifiedById")
+    public String getLastModifiedById() {
+        return LastModifiedById;
+    }
+
+    @JsonProperty("LastModifiedById")
+    public void setLastModifiedById(String lastModifiedById) {
+        LastModifiedById = lastModifiedById;
+    }
+
+    @JsonProperty("SystemModstamp")
+    public DateTime getSystemModstamp() {
+        return SystemModstamp;
+    }
+
+    @JsonProperty("SystemModstamp")
+    public void setSystemModstamp(DateTime systemModstamp) {
+        SystemModstamp = systemModstamp;
+    }
+
+    @JsonProperty("LastActivityDate")
+    public String getLastActivityDate() {
+        return LastActivityDate;
+    }
+
+    @JsonProperty("LastActivityDate")
+    public void setLastActivityDate(String lastActivityDate) {
+        LastActivityDate = lastActivityDate;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/Attributes.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/Attributes.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/Attributes.java
new file mode 100644
index 0000000..439f4c3
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/Attributes.java
@@ -0,0 +1,38 @@
+/**
+ * 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.api.dto;
+
+public class Attributes extends AbstractDTOBase {
+    private String type;
+    private String url;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/ChildRelationShip.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/ChildRelationShip.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/ChildRelationShip.java
new file mode 100644
index 0000000..4c2d58e
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/dto/ChildRelationShip.java
@@ -0,0 +1,74 @@
+/**
+ * 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.api.dto;
+
+public class ChildRelationShip extends AbstractDTOBase {
+    private String field;
+    private Boolean deprecatedAndHidden;
+    private String relationshipName;
+    private Boolean cascadeDelete;
+    private Boolean restrictedDelete;
+    private String childSObject;
+
+    public String getField() {
+        return field;
+    }
+
+    public void setField(String field) {
+        this.field = field;
+    }
+
+    public Boolean isDeprecatedAndHidden() {
+        return deprecatedAndHidden;
+    }
+
+    public void setDeprecatedAndHidden(Boolean deprecatedAndHidden) {
+        this.deprecatedAndHidden = deprecatedAndHidden;
+    }
+
+    public String getRelationshipName() {
+        return relationshipName;
+    }
+
+    public void setRelationshipName(String relationshipName) {
+        this.relationshipName = relationshipName;
+    }
+
+    public Boolean isCascadeDelete() {
+        return cascadeDelete;
+    }
+
+    public void setCascadeDelete(Boolean cascadeDelete) {
+        this.cascadeDelete = cascadeDelete;
+    }
+
+    public Boolean isRestrictedDelete() {
+        return restrictedDelete;
+    }
+
+    public void setRestrictedDelete(Boolean restrictedDelete) {
+        this.restrictedDelete = restrictedDelete;
+    }
+
+    public String getChildSObject() {
+        return childSObject;
+    }
+
+    public void setChildSObject(String childSObject) {
+        this.childSObject = childSObject;
+    }
+}