I've been doing some work on communication between our company's learning management system and salesforce so that we can tie a client's training into their customer and partner management systems. I had done a little work with salesforce in the past, mostly on accessing data via their REST API, but this integration involved creating a new schema in the client's salesforce database as well as creating some Apex functions (salesforce's platform language) to run periodically on their system.

Background

Salesforce's force.com platform is an interesting system. It centers around a multitenant architecture which focuses on applications with web-based front ends and a relational database back end. It can create fairly complex permissions hierarchies, which can be useful for enterprise clients, and is fairly well documented. They encourage the idea that one can create applications on force.com without writing code (or at least very little code) and therefore focus on visual application design tools, including a visual schema editor for the relational database.

Anyone can get a free developer account to play around with the force.com platform.

The Problem

Deployment can be tricky depending on the client. Our client had an Enterprise Edition account, which meant that we could develop a solution within their sandbox and use a change set for deployment to production. That was the model the client typically used, and we thought it would be prudent to adapt to their deployment strategy. It also allowed the client to perform whatever testing a validation their business processes required.

However, it is often better for us to do work in our own developer edition instance prior to deploying to the client sandbox. This allows us to run experiments, we don't have to worry about permissions, and we don't have to worry about losing access to our work. It is possible to create a managed package of our application, but a developer edition can only have one managed package.

Another aspect of the development which I found troubling was the visual programming aspect. I wanted to be able to save our deployment as a script which we could easily tweak and put into our revision control system. The alternative for our visually programmed system was to place screen shots in a wiki so that we could reproduce the work later. This was not a good solution.

The Metadata API

Of course force.com has a way of representing this visual data internally, and they expose it programmatically using the metadata API. This seemed to be exactly what we needed to record and recreate the changes we were making, but it's a fairly complex API and it would have required us to write a tool to exploit the API. It turns out, however, that someone has already written that tool! It's the Force.com Migration Tool.

Using the Force.com Migration Tool

The Force.com migration tool integrates into Apache Ant through the installation of a salesforce specific ant jar. All I had to do under OS X was copy this jar into /usr/share/ant/lib and I was ready to go.

You need a build.properties file, which specifies your username, password, and the login url. An example is shown below:

# build.properties
#

# Specify the login credentials for the desired Salesforce organization
sf.username = USERNAME GOES HERE
sf.password = PASSWORD GOES HERE

# Use 'https://login.salesforce.com' for production or developer edition (the default if not specified).
# Use 'https://test.salesforce.com for sandbox.
sf.serverurl = https://login.salesforce.com

sf.maxPoll = 20

You also need to setup a build.xml file which is in the typical style of an ant build file. Be sure to include your build.properties file. I include a retrieve target to retrieve the data from my environment, and a deploy target to deploy the data. My build.xml file looks like this:

<project name="Backup salesforce changes" default="retrieve" basedir="." xmlns:sf="antlib:com.salesforce">

  <property file="build.properties"/>
  <property environment="env"/>

  <target name="retrieve">
    <mkdir dir="retrieveMetadata"/>
    <sf:retrieve username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" retrieveTarget="retrieveMetadata" unpackaged="package.xml"/>
  </target>

  <target name="deploy">
    <sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" deployRoot="deployMetadata"/>
  </target>
</project>

Another file, the package.xml file tells the migration tool what data to retrieve. This XML document contains a list of metadata types and the members to be retrieved from each type. In some cases you can use the * wildcard to retrieve all the metadata objects of that type. In the case of this application, my package.xml file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>User_Certificate__c</members>
        <name>CustomObject</name>
    </types>
    <types>
      <members>Contact</members>
      <name>CustomObject</name>
    </types>
    <types>
      <members>User</members>
      <name>CustomObject</name>
    </types>
    <types>
        <members>Contact-Contact Layout</members>
        <members>Contact-Partner Contact Layout</members>
        <name>Layout</name>
    </types>
    <types>
      <members>REST_Moodle_BatchScheduler</members>
      <name>ApexClass</name>
    </types>
    <version>29.0</version>
</Package>

Note that you can check the list of metadata types to see what they are named and if they accept wildcards.

Running the

ant retrieve

command put the relevant metadata into the retrieveMetadata directory. I could then copy it to the deployMetadata directory for deployment. I could also edit the metadata if need be. I could also put the entire thing under revision control so that we could track changes and make bug reports against the repository.

When we were ready to deploy to the sandbox, I just put the credentials and login of the sandbox into my build.properties file and ran the

ant deploy

command.

More information

For more information, please visit the following sites: