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:49 UTC

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

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/Merchandise__c.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/Merchandise__c.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/Merchandise__c.java
new file mode 100644
index 0000000..3e3d36a
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/Merchandise__c.java
@@ -0,0 +1,61 @@
+/**
+ * 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;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase;
+
+@XStreamAlias("Merchandise__c")
+public class Merchandise__c extends AbstractSObjectBase {
+
+    private String Description__c;
+
+    private Double Price__c;
+
+    private Double Total_Inventory__c;
+
+    @JsonProperty("Description__c")
+    public String getDescription__c() {
+        return Description__c;
+    }
+
+    @JsonProperty("Description__c")
+    public void setDescription__c(String description__c) {
+        Description__c = description__c;
+    }
+
+    @JsonProperty("Price__c")
+    public Double getPrice__c() {
+        return Price__c;
+    }
+
+    @JsonProperty("Price__c")
+    public void setPrice__c(Double price__c) {
+        Price__c = price__c;
+    }
+
+    @JsonProperty("Total_Inventory__c")
+    public Double getTotal_Inventory__c() {
+        return Total_Inventory__c;
+    }
+
+    @JsonProperty("Total_Inventory__c")
+    public void setTotal_Inventory__c(Double total_Inventory__c) {
+        Total_Inventory__c = total_Inventory__c;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/QueryRecordsLine_Item__c.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/QueryRecordsLine_Item__c.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/QueryRecordsLine_Item__c.java
new file mode 100644
index 0000000..a2d00cb
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/dto/QueryRecordsLine_Item__c.java
@@ -0,0 +1,35 @@
+/**
+ * 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;
+
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+import org.apache.camel.component.salesforce.api.dto.AbstractQueryRecordsBase;
+
+import java.util.List;
+
+public class QueryRecordsLine_Item__c extends AbstractQueryRecordsBase {
+    @XStreamImplicit
+    private List<Line_Item__c> records;
+
+    public List<Line_Item__c> getRecords() {
+        return records;
+    }
+
+    public void setRecords(List<Line_Item__c> records) {
+        this.records = records;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/SessionIntegrationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/SessionIntegrationTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/SessionIntegrationTest.java
new file mode 100644
index 0000000..5e5ab5f
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/SessionIntegrationTest.java
@@ -0,0 +1,79 @@
+/**
+ * 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.internal;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.RedirectListener;
+import org.apache.camel.component.salesforce.LoginConfigHelper;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author dbokde
+ */
+public class SessionIntegrationTest extends Assert implements SalesforceSession.SalesforceSessionListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SessionIntegrationTest.class);
+    private static final int TIMEOUT = 60000;
+    private boolean onLoginTriggered;
+    private boolean onLogoutTriggered;
+
+    @Test
+    public void testLogin() throws Exception {
+
+        final HttpClient httpClient = new HttpClient();
+        httpClient.setConnectTimeout(TIMEOUT);
+        httpClient.setTimeout(TIMEOUT);
+        httpClient.registerListener(RedirectListener.class.getName());
+        httpClient.start();
+
+        final SalesforceSession session = new SalesforceSession(
+            httpClient, LoginConfigHelper.getLoginConfig());
+        session.addListener(this);
+
+        try {
+            String loginToken = session.login(session.getAccessToken());
+            LOG.info("First token " + loginToken);
+
+            assertTrue("SalesforceSessionListener onLogin NOT called", onLoginTriggered);
+            onLoginTriggered = false;
+
+            // refresh token, also causes logout
+            loginToken = session.login(loginToken);
+            LOG.info("Refreshed token " + loginToken);
+
+            assertTrue("SalesforceSessionListener onLogout NOT called", onLogoutTriggered);
+            assertTrue("SalesforceSessionListener onLogin NOT called", onLoginTriggered);
+
+        } finally {
+            // logout finally
+            session.logout();
+        }
+    }
+
+    @Override
+    public void onLogin(String accessToken, String instanceUrl) {
+        onLoginTriggered = true;
+    }
+
+    @Override
+    public void onLogout() {
+        onLogoutTriggered = true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/resources/log4j.properties b/components/camel-salesforce/camel-salesforce-component/src/test/resources/log4j.properties
new file mode 100644
index 0000000..9bbf19e
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/resources/log4j.properties
@@ -0,0 +1,18 @@
+#
+# The logging properties used
+#
+log4j.rootLogger=INFO, out
+
+# uncomment the following line to turn on Camel debugging
+#log4j.logger.org.apache.camel=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=[%30.30t] %-30.30c{1} %-5p %m%n
+#log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
+
+#log4j.logger.org.apache.http=DEBUG
+#log4j.logger.org.apache.camel.component.salesforce=TRACE

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.csv
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.csv b/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.csv
new file mode 100644
index 0000000..880c4c4
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.csv
@@ -0,0 +1,3 @@
+"Description__c","Name","Price__c","Total_Inventory__c"
+"Created from Bulk API","[Bulk API] Merchandise 0 (batch 0)","15.0","30.0"
+"Created from Bulk API","[Bulk API] Merchandise 1 (batch 0)","30.0","60.0"
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.xml b/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.xml
new file mode 100644
index 0000000..bdfa10e
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/resources/test-request.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<sObjects xmlns="http://www.force.com/2009/06/asyncapi/dataload">
+  <sObject>
+    <Description__c>Created from Bulk API</Description__c>
+    <Price__c>15.0</Price__c>
+    <Total_Inventory__c>30.0</Total_Inventory__c>
+    <Name>[Bulk API] Merchandise 0 (batch 0)</Name>
+  </sObject>
+  <sObject>
+    <Description__c>Created from Bulk API</Description__c>
+    <Price__c>30.0</Price__c>
+    <Total_Inventory__c>60.0</Total_Inventory__c>
+    <Name>[Bulk API] Merchandise 1 (batch 0)</Name>
+  </sObject>
+</sObjects>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/README.md b/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
new file mode 100644
index 0000000..5bfd808
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
@@ -0,0 +1,26 @@
+# Maven plugin for camel-salesforce component #
+
+This plugin generates DTOs for the [Camel Salesforce Component](https://github.com/dhirajsb/camel-salesforce). 
+
+## Usage ##
+
+The plugin configuration has the following properties.
+
+* clientId - Salesforce client Id for Remote API access
+* clientSecret - Salesforce client secret for Remote API access
+* userName - Salesforce account user name
+* password - Salesforce account password (including secret token)
+* version - Salesforce Rest API version, defaults to 25.0
+* outputDirectory - Directory where to place generated DTOs, defaults to ${project.build.directory}/generated-sources/camel-salesforce
+* includes - List of SObject types to include
+* excludes - List of SObject types to exclude
+* includePattern - Java RegEx for SObject types to include
+* excludePattern - Java RegEx for SObject types to exclude
+* packageName - Java package name for generated DTOs, defaults to org.apache.camel.salesforce.dto.
+
+Fro obvious security reasons it is recommended that the clientId, clientSecret, userName and password fields be not set in the pom.xml. 
+The plugin should be configured for the rest of the properties, and can be executed using the following command:
+
+	mvn camel-salesforce:generate -DclientId=<clientid> -DclientSecret=<clientsecret> -DuserName=<username> -Dpassword=<password>
+
+The generated DTOs use Jackson and XStream annotations. All Salesforce field types are supported. Date and time fields are mapped to Joda DateTime, and picklist fields are mapped to generated Java Enumerations. 

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/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
new file mode 100644
index 0000000..a5bfdf7
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml
@@ -0,0 +1,78 @@
+<?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>
+
+  <groupId>org.apache.camel.maven</groupId>
+  <artifactId>camel-salesforce-maven-plugin</artifactId>
+  <packaging>maven-plugin</packaging>
+  <name>Camel :: Salesforce :: Maven plugin</name>
+  <description>Camel Salesforce Maven plugin</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-salesforce</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity</artifactId>
+      <version>${velocity-version}</version>
+    </dependency>
+
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j-api-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>${log4j-version}</version>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>${slf4j-api-version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit-version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/CamelSalesforceMojo.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/CamelSalesforceMojo.java b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/CamelSalesforceMojo.java
new file mode 100644
index 0000000..70942a4
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/CamelSalesforceMojo.java
@@ -0,0 +1,570 @@
+/**
+ * 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.maven;
+
+import org.apache.log4j.Logger;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.log.Log4JLogChute;
+import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.RedirectListener;
+import org.apache.camel.component.salesforce.SalesforceLoginConfig;
+import org.apache.camel.component.salesforce.api.SalesforceException;
+import org.apache.camel.component.salesforce.api.dto.*;
+import org.apache.camel.component.salesforce.internal.SalesforceSession;
+import org.apache.camel.component.salesforce.internal.client.DefaultRestClient;
+import org.apache.camel.component.salesforce.internal.client.RestClient;
+import org.apache.camel.component.salesforce.internal.client.SyncResponseCallback;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+/**
+ * Goal which generates POJOs for Salesforce SObjects
+ *
+ * @goal generate
+ * 
+ * @phase generate-sources
+ *
+ */
+public class CamelSalesforceMojo extends AbstractMojo
+{
+    private static final String JAVA_EXT = ".java";
+    private static final String PACKAGE_NAME_PATTERN = "^[a-z]+(\\.[a-z][a-z0-9]*)*$";
+    private static final String SOBJECT_POJO_VM = "/sobject-pojo.vm";
+    private static final String SOBJECT_QUERY_RECORDS_VM = "/sobject-query-records.vm";
+    private static final String SOBJECT_PICKLIST_VM = "/sobject-picklist.vm";
+
+    // used for velocity logging, to avoid creating velocity.log
+    private static final Logger LOG = Logger.getLogger(CamelSalesforceMojo.class.getName());
+    private static final int TIMEOUT = 60000;
+
+    /**
+     * Salesforce client id
+     * @parameter property="${clientId}"
+     * @required
+     */
+    protected String clientId;
+
+    /**
+     * Salesforce client secret
+     * @parameter property="${clientSecret}"
+     * @required
+     */
+    protected String clientSecret;
+
+    /**
+     * Salesforce user name
+     * @parameter property="${userName}"
+     * @required
+     */
+    protected String userName;
+
+    /**
+     * Salesforce password
+     * @parameter property="${password}"
+     * @required
+     */
+    protected String password;
+
+    /**
+     * Salesforce version
+     * @parameter property="${version}" default-value="25.0"
+     */
+    protected String version;
+
+    /**
+     * Location of the file.
+     * @parameter property="${outputDirectory}" default-value="${project.build.directory}/generated-sources/camel-salesforce"
+     * @required
+     */
+    protected File outputDirectory;
+
+    /**
+     * Names of Salesforce SObject for which POJOs must be generated
+     * @parameter
+     */
+    protected String[] includes;
+
+    /**
+     * Do NOT generate POJOs for these Salesforce SObjects
+     * @parameter
+     */
+    protected String[] excludes;
+
+    /**
+     * Include Salesforce SObjects that match pattern
+     * @parameter property="${includePattern}"
+     */
+    protected String includePattern;
+
+    /**
+     * Exclude Salesforce SObjects that match pattern
+     * @parameter property="${excludePattern}"
+     */
+    protected String excludePattern;
+
+    /**
+     * Java package name for generated POJOs
+     * @parameter property="${packageName}" default-value="org.apache.camel.salesforce.dto"
+     */
+    protected String packageName;
+
+    private VelocityEngine engine;
+
+    /**
+     * Execute the mojo to generate SObject POJOs
+     * @throws MojoExecutionException
+     */
+    public void execute()
+        throws MojoExecutionException
+    {
+        // initialize velocity to load resources from class loader and use Log4J
+        Properties velocityProperties = new Properties();
+        velocityProperties.setProperty(RuntimeConstants.RESOURCE_LOADER, "cloader");
+        velocityProperties.setProperty("cloader.resource.loader.class", ClasspathResourceLoader.class.getName());
+        velocityProperties.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, Log4JLogChute.class.getName());
+        velocityProperties.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM + ".log4j.logger", LOG.getName());
+        engine = new VelocityEngine(velocityProperties);
+        engine.init();
+
+        // make sure we can load both templates
+        if (!engine.resourceExists(SOBJECT_POJO_VM) ||
+            !engine.resourceExists(SOBJECT_QUERY_RECORDS_VM)) {
+            throw new MojoExecutionException("Velocity templates not found");
+        }
+
+        // connect to Salesforce
+        final HttpClient httpClient = new HttpClient();
+        httpClient.registerListener(RedirectListener.class.getName());
+        httpClient.setConnectTimeout(TIMEOUT);
+        httpClient.setTimeout(TIMEOUT);
+        try {
+            httpClient.start();
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error creating HTTP client: " + e.getMessage(), e);
+        }
+
+        final SalesforceSession session = new SalesforceSession(httpClient,
+            new SalesforceLoginConfig(SalesforceLoginConfig.DEFAULT_LOGIN_URL,
+            clientId, clientSecret, userName, password, false));
+
+        getLog().info("Salesforce login...");
+        try {
+            session.login(null);
+        } catch (SalesforceException e) {
+            String msg = "Salesforce login error " + e.getMessage();
+            throw new MojoExecutionException(msg, e);
+        }
+        getLog().info("Salesforce login successful");
+
+        // create rest client
+        RestClient restClient = null;
+        try {
+            restClient = new DefaultRestClient(httpClient,
+                version, "json", session);
+            // remember to start the active client object
+            ((DefaultRestClient)restClient).start();
+        } catch (Exception e) {
+            final String msg = "Unexpected exception creating Rest client: " + e.getMessage();
+            throw new MojoExecutionException(msg, e);
+        }
+
+        try {
+            // use Jackson json
+            final ObjectMapper mapper = new ObjectMapper();
+
+            // call getGlobalObjects to get all SObjects
+            final Set<String> objectNames = new HashSet<String>();
+            final SyncResponseCallback callback = new SyncResponseCallback();
+            try {
+                getLog().info("Getting Salesforce Objects...");
+                restClient.getGlobalObjects(callback);
+                if (!callback.await(TIMEOUT, TimeUnit.MILLISECONDS)) {
+                    throw new MojoExecutionException("Timeout waiting for getGlobalObjects!");
+                }
+                final SalesforceException ex = callback.getException();
+                if (ex != null) {
+                    throw ex;
+                }
+                final GlobalObjects globalObjects = mapper.readValue(callback.getResponse(),
+                    GlobalObjects.class);
+
+                // create a list of object names
+                for (SObject sObject : globalObjects.getSobjects()) {
+                    objectNames.add(sObject.getName());
+                }
+            } catch (Exception e) {
+                String msg = "Error getting global Objects " + e.getMessage();
+                throw new MojoExecutionException(msg, e);
+            }
+
+            // check if we are generating POJOs for all objects or not
+            if ((includes != null && includes.length > 0) ||
+                (excludes != null && excludes.length > 0) ||
+                (includePattern != null && !includePattern.trim().isEmpty()) ||
+                (excludePattern != null && !excludePattern.trim().isEmpty())) {
+
+                getLog().info("Looking for matching Object names...");
+                // create a list of accepted names
+                final Set<String> includedNames = new HashSet<String>();
+                if (includes != null && includes.length > 0) {
+                    for (String name : includes) {
+                        name = name.trim();
+                        if (name.isEmpty()) {
+                            throw new MojoExecutionException("Invalid empty name in includes");
+                        }
+                        includedNames.add(name);
+                    }
+                }
+
+                final Set<String> excludedNames = new HashSet<String>();
+                if (excludes != null && excludes.length > 0) {
+                    for (String name : excludes) {
+                        name = name.trim();
+                        if (name.isEmpty()) {
+                            throw new MojoExecutionException("Invalid empty name in excludes");
+                        }
+                        excludedNames.add(name);
+                    }
+                }
+
+                // check whether a pattern is in effect
+                Pattern incPattern;
+                if (includePattern != null && !includePattern.trim().isEmpty()) {
+                    incPattern = Pattern.compile(includePattern.trim());
+                } else if (includedNames.isEmpty()) {
+                    // include everything by default if no include names are set
+                    incPattern = Pattern.compile(".*");
+                } else {
+                    // include nothing by default if include names are set
+                    incPattern = Pattern.compile("^$");
+                }
+
+                // check whether a pattern is in effect
+                Pattern excPattern;
+                if (excludePattern != null && !excludePattern.trim().isEmpty()) {
+                    excPattern = Pattern.compile(excludePattern.trim());
+                } else {
+                    // exclude nothing by default
+                    excPattern = Pattern.compile("^$");
+                }
+
+                final Set<String> acceptedNames = new HashSet<String>();
+                for (String name : objectNames) {
+                    // name is included, or matches include pattern
+                    // and is not excluded and does not match exclude pattern
+                    if ((includedNames.contains(name) || incPattern.matcher(name).matches()) &&
+                        !excludedNames.contains(name) &&
+                        !excPattern.matcher(name).matches()) {
+                        acceptedNames.add(name);
+                    }
+                }
+                objectNames.clear();
+                objectNames.addAll(acceptedNames);
+
+                getLog().info(String.format("Found %s matching Objects", objectNames.size()));
+
+            } else {
+                getLog().warn(String.format("Generating Java classes for all %s Objects, this may take a while...",
+                    objectNames.size()));
+            }
+
+            // for every accepted name, get SObject description
+            final Set<SObjectDescription> descriptions =
+                new HashSet<SObjectDescription>();
+
+            try {
+                getLog().info("Retrieving Object descriptions...");
+                for (String name : objectNames) {
+                    callback.reset();
+                    restClient.getDescription(name, callback);
+                    if (!callback.await(TIMEOUT, TimeUnit.MILLISECONDS)) {
+                        throw new MojoExecutionException(
+                            "Timeout waiting for getDescription for sObject " + name);
+                    }
+                    final SalesforceException ex = callback.getException();
+                    if (ex != null) {
+                        throw ex;
+                    }
+                    descriptions.add(mapper.readValue(callback.getResponse(),
+                            SObjectDescription.class));
+                }
+            } catch (Exception e) {
+                String msg = "Error getting SObject description " + e.getMessage();
+                throw new MojoExecutionException(msg, e);
+            }
+
+            // create package directory
+            // validate package name
+            if (!packageName.matches(PACKAGE_NAME_PATTERN)) {
+                throw new MojoExecutionException("Invalid package name " + packageName);
+            }
+            final File pkgDir = new File(outputDirectory, packageName.trim().replace('.', File.separatorChar));
+            if (!pkgDir.exists()) {
+                if (!pkgDir.mkdirs()) {
+                    throw new MojoExecutionException("Unable to create " + pkgDir);
+                }
+            }
+
+            getLog().info("Generating Java Classes...");
+            // generate POJOs for every object description
+            final GeneratorUtility utility = new GeneratorUtility();
+            // should we provide a flag to control timestamp generation?
+            final String generatedDate = new Date().toString();
+            for (SObjectDescription description : descriptions) {
+                processDescription(pkgDir, description, utility, generatedDate);
+            }
+
+            getLog().info(String.format("Successfully generated %s Java Classes", (descriptions.size() * 2)));
+
+        } finally {
+            // remember to stop the client
+            try {
+                ((DefaultRestClient)restClient).stop();
+            } catch (Exception ignore) {}
+
+            // Salesforce session stop
+            try {
+                session.stop();
+            } catch (Exception ignore) {}
+
+            // release HttpConnections
+            try {
+                httpClient.stop();
+            } catch (Exception ignore) {}
+        }
+    }
+
+    private void processDescription(File pkgDir, SObjectDescription description, GeneratorUtility utility, String generatedDate) throws MojoExecutionException {
+        // generate a source file for SObject
+        String fileName = description.getName() + JAVA_EXT;
+        BufferedWriter writer = null;
+        try {
+            final File pojoFile = new File(pkgDir, fileName);
+            writer = new BufferedWriter(new FileWriter(pojoFile));
+
+            VelocityContext context = new VelocityContext();
+            context.put("packageName", packageName);
+            context.put("utility", utility);
+            context.put("desc", description);
+            context.put("generatedDate", generatedDate);
+
+            Template pojoTemplate = engine.getTemplate(SOBJECT_POJO_VM);
+            pojoTemplate.merge(context, writer);
+            // close pojoFile
+            writer.close();
+
+            // write required Enumerations for any picklists
+            for (SObjectField field : description.getFields()) {
+                if (utility.isPicklist(field)) {
+                    fileName = utility.enumTypeName(field.getName()) + JAVA_EXT;
+                    File enumFile = new File(pkgDir, fileName);
+                    writer = new BufferedWriter(new FileWriter(enumFile));
+
+                    context = new VelocityContext();
+                    context.put("packageName", packageName);
+                    context.put("utility", utility);
+                    context.put("field", field);
+                    context.put("generatedDate", generatedDate);
+
+                    Template queryTemplate = engine.getTemplate(SOBJECT_PICKLIST_VM);
+                    queryTemplate.merge(context, writer);
+
+                    // close Enum file
+                    writer.close();
+                }
+            }
+
+            // write the QueryRecords class
+            fileName = "QueryRecords" + description.getName() + JAVA_EXT;
+            File queryFile = new File(pkgDir, fileName);
+            writer = new BufferedWriter(new FileWriter(queryFile));
+
+            context = new VelocityContext();
+            context.put("packageName", packageName);
+            context.put("desc", description);
+            context.put("generatedDate", generatedDate);
+
+            Template queryTemplate = engine.getTemplate(SOBJECT_QUERY_RECORDS_VM);
+            queryTemplate.merge(context, writer);
+
+            // close QueryRecords file
+            writer.close();
+
+        } catch (Exception e) {
+            String msg = "Error creating " + fileName + ": " + e.getMessage();
+            throw new MojoExecutionException(msg, e);
+        } finally {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (IOException ignore) {}
+            }
+        }
+    }
+
+    public static class GeneratorUtility {
+
+        private static final Set<String> baseFields;
+        private static final Map<String, String> lookupMap;
+
+        static {
+            baseFields = new HashSet<String>();
+            for (Field field : AbstractSObjectBase.class.getDeclaredFields()) {
+                baseFields.add(field.getName());
+            }
+
+            // create a type map
+            // using JAXB mapping, for the most part
+            // uses Joda time instead of XmlGregorianCalendar
+            // TODO do we need support for commented types???
+            final String[][] typeMap = new String[][] {
+                {"ID", "String"}, // mapping for tns:ID SOAP type
+                {"string", "String"},
+                {"integer", "java.math.BigInteger"},
+                {"int", "Integer"},
+                {"long", "Long"},
+                {"short", "Short"},
+                {"decimal", "java.math.BigDecimal"},
+                {"float", "Float"},
+                {"double", "Double"},
+                {"boolean", "Boolean"},
+                {"byte", "Byte"},
+//                {"QName", "javax.xml.namespace.QName"},
+
+//                {"dateTime", "javax.xml.datatype.XMLGregorianCalendar"},
+                {"dateTime", "org.joda.time.DateTime"},
+
+                // the blob base64Binary type is mapped to String URL for retrieving the blob
+                {"base64Binary", "String"},
+//                {"hexBinary", "byte[]"},
+
+                {"unsignedInt", "Long"},
+                {"unsignedShort", "Integer"},
+                {"unsignedByte", "Short"},
+
+//                {"time", "javax.xml.datatype.XMLGregorianCalendar"},
+                {"time", "org.joda.time.DateTime"},
+//                {"date", "javax.xml.datatype.XMLGregorianCalendar"},
+                {"date", "org.joda.time.DateTime"},
+//                {"g", "javax.xml.datatype.XMLGregorianCalendar"},
+                {"g", "org.joda.time.DateTime"},
+
+                // Salesforce maps any types like string, picklist, reference, etc. to string
+                {"anyType", "String"},
+/*
+                {"anySimpleType", "java.lang.Object"},
+                {"anySimpleType", "java.lang.String"},
+                {"duration", "javax.xml.datatype.Duration"},
+                {"NOTATION", "javax.xml.namespace.QName"}
+*/
+            };
+            lookupMap = new HashMap<String, String>();
+            for (String[] entry : typeMap) {
+                lookupMap.put(entry[0], entry[1]);
+            }
+        }
+
+        private static final String BASE64BINARY = "base64Binary";
+
+        public boolean isBlobField(SObjectField field) {
+            final String soapType = field.getSoapType();
+            return BASE64BINARY.equals(soapType.substring(soapType.indexOf(':')+1));
+        }
+
+        public boolean notBaseField(String name) {
+            return !baseFields.contains(name);
+        }
+
+        public String getFieldType(SObjectField field) throws MojoExecutionException {
+            // check if this is a picklist
+            if (isPicklist(field)) {
+                // use a pick list enum, which will be created after generating the SObject class
+                return enumTypeName(field.getName());
+            } else {
+                // map field to Java type
+                final String soapType = field.getSoapType();
+                final String type = lookupMap.get(soapType.substring(soapType.indexOf(':')+1));
+                if (type == null) {
+                    throw new MojoExecutionException(
+                        String.format("Unsupported type %s for field %s", soapType, field.getName()));
+                }
+                return type;
+            }
+        }
+
+        public boolean hasPicklists(SObjectDescription desc) {
+            for (SObjectField field : desc.getFields()) {
+                if (isPicklist(field)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public PickListValue getLastEntry(SObjectField field) {
+            final List<PickListValue> values = field.getPicklistValues();
+            return values.get(values.size() - 1);
+        }
+
+        public boolean isPicklist(SObjectField field) {
+            return field.getPicklistValues() != null && !field.getPicklistValues().isEmpty();
+        }
+
+        public String enumTypeName(String name) {
+            name = name.endsWith("__c") ? name.substring(0, name.length() - 3) : name;
+            return name + "Enum";
+        }
+
+        public String getEnumConstant(String value) {
+
+            // TODO add support for supplementary characters
+            final StringBuilder result = new StringBuilder();
+            boolean changed = false;
+            if (!Character.isJavaIdentifierStart(value.charAt(0))) {
+                result.append("_");
+                changed = true;
+            }
+            for (char c : value.toCharArray()) {
+                if (Character.isJavaIdentifierPart(c)) {
+                    result.append(c);
+                } else {
+                    // replace non Java identifier character with '_'
+                    result.append('_');
+                    changed = true;
+                }
+            }
+
+            return changed ? result.toString().toUpperCase() : value.toUpperCase();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-picklist.vm
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-picklist.vm b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-picklist.vm
new file mode 100644
index 0000000..0aed2a7
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-picklist.vm
@@ -0,0 +1,52 @@
+## sobject-picklist.vm
+/*
+ * Salesforce DTO generated by camel-salesforce-maven-plugin
+ * Generated on: $generatedDate
+ */
+package $packageName;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonValue;
+
+#set ( $enumName = $utility.enumTypeName($field.Name) )
+/**
+ * Salesforce Enumeration DTO for picklist $field.Name
+ */
+public enum $enumName {
+## find the last entry
+#set ( $lastEntry = $utility.getLastEntry($field) )
+
+#foreach ( $entry in $field.PicklistValues)
+#set ( $value = $entry.Value )
+#if ( $entry == $lastEntry )
+#set ( $delim = ";" )
+#else
+#set ( $delim = ",")
+#end
+    // $value
+    $utility.getEnumConstant($value)("$value")$delim
+#end
+
+    final String value;
+
+    private $enumName(String value) {
+        this.value = value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static $enumName fromValue(String value) {
+#set ( $allValues = ".values()" )
+        for ($enumName e : $enumName$allValues) {
+            if (e.value.equals(value)) {
+                return e;
+            }
+        }
+        throw new IllegalArgumentException(value);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-pojo.vm
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-pojo.vm b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-pojo.vm
new file mode 100644
index 0000000..b240d46
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-pojo.vm
@@ -0,0 +1,60 @@
+## sobject-pojo.vm
+/*
+ * Salesforce DTO generated by camel-salesforce-maven-plugin
+ * Generated on: $generatedDate
+ */
+package $packageName;
+
+## add imports for XStreamConverter and PicklistEnumConverter if needed
+#set ( $hasPicklists = $utility.hasPicklists($desc) )
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+#if ( $hasPicklists )
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+#end
+import org.codehaus.jackson.annotate.JsonProperty;
+#if ( $hasPicklists )
+import org.apache.camel.component.salesforce.api.PicklistEnumConverter;
+#end
+import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase;
+
+/**
+ * Salesforce DTO for SObject $desc.Name
+ */
+@XStreamAlias("$desc.Name")
+public class $desc.Name extends AbstractSObjectBase {
+
+#foreach ( $field in $desc.Fields )
+#if ( $utility.notBaseField($field.Name) )
+#set ( $fieldName = $field.Name )
+#set ( $fieldType = $utility.getFieldType($field) )
+    // $fieldName
+#if ( $utility.isBlobField($field) )
+#set ( $propertyName = $fieldName + "Url" )
+#else
+#set ( $propertyName = $fieldName )
+#end
+## add a converter annotation if needed
+#if ( $utility.isPicklist($field) )
+    @XStreamConverter(PicklistEnumConverter.class)
+#else
+## add an alias for blob field url if needed
+#if ( $propertyName != $fieldName )
+    // blob field url, use getBlobField to get the content
+    @XStreamAlias("$fieldName")
+#end
+#end
+    private $fieldType $propertyName;
+
+    @JsonProperty("$fieldName")
+    public $fieldType get$propertyName() {
+        return this.$propertyName;
+    }
+
+    @JsonProperty("$fieldName")
+    public void set$propertyName($fieldType $propertyName) {
+        this.$propertyName = $propertyName;
+    }
+
+#end
+#end
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-query-records.vm
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-query-records.vm b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-query-records.vm
new file mode 100644
index 0000000..40f1ac1
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/resources/sobject-query-records.vm
@@ -0,0 +1,29 @@
+## sobject-query-records.vm
+/*
+ * Salesforce Query DTO generated by camel-salesforce-maven-plugin
+ * Generated on: $generatedDate
+ */
+package $packageName;
+
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+import org.apache.camel.component.salesforce.api.dto.AbstractQueryRecordsBase;
+
+import java.util.List;
+
+/**
+ * Salesforce QueryRecords DTO for type $desc.Name
+ */
+#set( $descName = $desc.Name )
+public class QueryRecords$descName extends AbstractQueryRecordsBase {
+
+    @XStreamImplicit
+    private List<$descName> records;
+
+    public List<$descName> getRecords() {
+        return records;
+    }
+
+    public void setRecords(List<$descName> records) {
+        this.records = records;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoIntegrationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoIntegrationTest.java b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoIntegrationTest.java
new file mode 100644
index 0000000..781c592
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoIntegrationTest.java
@@ -0,0 +1,85 @@
+/**
+ * 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.maven;
+
+import org.apache.maven.plugin.logging.SystemStreamLog;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+public class CamelSalesforceMojoIntegrationTest {
+
+    private static final String TEST_LOGIN_PROPERTIES = "test-salesforce-login.properties";
+
+    @Test
+    public void testExecute() throws Exception {
+        CamelSalesforceMojo mojo = new CamelSalesforceMojo();
+
+        mojo.setLog(new SystemStreamLog());
+
+        // set login properties
+        setLoginProperties(mojo);
+
+        // set defaults
+        mojo.version = "27.0";
+        mojo.outputDirectory = new File("target/generated-sources/camel-salesforce");
+        mojo.packageName = "org.apache.camel.salesforce.dto";
+
+        // set code generation properties
+        mojo.includePattern = "(.*__c)|(PushTopic)";
+
+        // remove generated code directory
+        if (mojo.outputDirectory.exists()) {
+            // remove old files
+            for (File file : mojo.outputDirectory.listFiles()) {
+                file.delete();
+            }
+            mojo.outputDirectory.delete();
+        }
+
+        // generate code
+        mojo.execute();
+
+        // validate generated code
+        // check that it was generated
+        Assert.assertTrue("Output directory was not created", mojo.outputDirectory.exists());
+
+        // TODO check that the generated code compiles
+    }
+
+    private void setLoginProperties(CamelSalesforceMojo mojo) throws IllegalAccessException, IOException {
+        // load test-salesforce-login properties
+        Properties properties = new Properties();
+        InputStream stream = new FileInputStream(TEST_LOGIN_PROPERTIES);
+        if (null == stream) {
+            throw new IllegalAccessException("Create a properties file named " +
+                TEST_LOGIN_PROPERTIES + " with clientId, clientSecret, userName, password and a testId" +
+                " for a Salesforce account with the Merchandise object from Salesforce Guides.");
+        }
+        properties.load(stream);
+        mojo.clientId= properties.getProperty("clientId");
+        mojo.clientSecret= properties.getProperty("clientSecret");
+        mojo.userName= properties.getProperty("userName");
+        mojo.password= properties.getProperty("password");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/resources/log4j.properties b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/resources/log4j.properties
new file mode 100644
index 0000000..6c5150e
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#
+# The logging properties used
+#
+log4j.rootLogger=INFO, out
+
+# uncomment the following line to turn on Camel debugging
+#log4j.logger.org.apache.camel=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=[%30.30t] %-30.30c{1} %-5p %m%n
+#log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
+
+#log4j.logger.org.apache.camel.maven=DEBUG
+#log4j.logger.org.apache.http=DEBUG
+#log4j.logger.org.apache.camel.component.salesforce=TRACE

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/camel-salesforce/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/pom.xml b/components/camel-salesforce/pom.xml
new file mode 100644
index 0000000..1a79dcd
--- /dev/null
+++ b/components/camel-salesforce/pom.xml
@@ -0,0 +1,38 @@
+<?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>components</artifactId>
+        <version>2.12-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-salesforce-parent</artifactId>
+    <packaging>pom</packaging>
+    <name>Camel :: Salesforce :: Parent</name>
+    <description>Camel Salesforce parent</description>
+
+    <modules>
+        <module>camel-salesforce-component</module>
+        <module>camel-salesforce-maven-plugin</module>
+    </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/components/pom.xml
----------------------------------------------------------------------
diff --git a/components/pom.xml b/components/pom.xml
index 8a4b7d4..e186ce3 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -143,6 +143,7 @@
     <module>camel-rx</module>
     <module>camel-sap-netweaver</module>
     <module>camel-saxon</module>
+    <module>camel-salesforce</module>
     <module>camel-script</module>
     <module>camel-servlet</module>
     <module>camel-servletlistener</module>

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 66940d7..d558694 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -72,6 +72,7 @@
     <cglib-version>2.2</cglib-version>
     <cmis-version>0.8.0</cmis-version>
     <cometd-bayeux-version>6.1.11</cometd-bayeux-version>
+    <cometd-java-client-version>2.4.3</cometd-java-client-version>
     <cometd-java-server-bundle-version>2.3.1_2</cometd-java-server-bundle-version>
     <cometd-java-server>2.3.1</cometd-java-server>
     <commons-beanutils-bundle-version>1.8.3_1</commons-beanutils-bundle-version>
@@ -963,6 +964,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-salesforce</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-saxon</artifactId>
         <version>${project.version}</version>
       </dependency>

http://git-wip-us.apache.org/repos/asf/camel/blob/0c401b9f/platforms/karaf/features/src/main/resources/features.xml
----------------------------------------------------------------------
diff --git a/platforms/karaf/features/src/main/resources/features.xml b/platforms/karaf/features/src/main/resources/features.xml
index 165f9bb..7a378e2 100644
--- a/platforms/karaf/features/src/main/resources/features.xml
+++ b/platforms/karaf/features/src/main/resources/features.xml
@@ -738,6 +738,22 @@
     <bundle dependency='true'>mvn:org.codehaus.jackson/jackson-mapper-asl/${jackson-version}</bundle>
     <bundle>mvn:org.apache.camel/camel-sap-netweaver/${project.version}</bundle>
   </feature>
+  <feature name='camel-salesforce' version='${project.version}' resolver='(obr)' start-level='50'>
+    <feature version='${project.version}'>camel-core</feature>
+    <bundle dependency='true'>mvn:org.codehaus.jackson/jackson-mapper-asl/${jackson-version}</bundle>
+    <bundle dependency='true'>mvn:org.codehaus.jackson/jackson-core-asl/${jackson-version}</bundle>
+    <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.xstream/${xstream-bundle-version}</bundle>
+    <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.xpp3/${xpp3-bundle-version}</bundle>
+    <bundle dependency='true'>mvn:org.cometd.java/cometd-java-client/${cometd-java-client-version}</bundle>
+    <bundle dependency='true'>mvn:org.cometd.java/bayeux-api/${cometd-java-client-version}</bundle>
+    <bundle dependency='true'>mvn:org.cometd.java/cometd-java-common/${cometd-java-client-version}</bundle>
+    <bundle dependency='true'>mvn:org.eclipse.jetty/jetty-util/${jetty-version}</bundle>
+    <bundle dependency='true'>mvn:org.eclipse.jetty/jetty-io/${jetty--version}</bundle>
+    <bundle dependency='true'>mvn:org.eclipse.jetty/jetty-http/${jetty-version}</bundle>
+    <bundle dependency='true'>mvn:org.eclipse.jetty/jetty-client/${jetty-version}</bundle>
+    <bundle dependency='true'>mvn:joda-time/joda-time/${jodatime2-bundle-version}</bundle>
+    <bundle>mvn:org.apache.camel/camel-salesforce/${project.version}</bundle>
+  </feature>
   <feature name='camel-saxon' version='${project.version}' resolver='(obr)' start-level='50'>
     <feature version='${project.version}'>camel-core</feature>
     <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.xmlresolver/${xmlresolver-bundle-version}</bundle>