[SalesForce] execute anonymous apex via ANT script

I am looking to execute anonymous apex code thru ANT scripts.

But couldn't get started..

Appreciate if somebody can outline the process of doing this.

Thanks for your help.

Best Answer

  1. download the salesforce ant tool and extract it to /lib/salesforce_ant_34.0/

  2. then download the missing link http library and extract it to /lib/ml-ant-http-1.1.3/

  3. scaffold out your build.xml as follows, and look at the deploy target for example usage

     <project default="usage" basedir="." xmlns:sf="antlib:com.salesforce" xmlns:ml="org.missinglink.ant.task.http.HttpClientTask">
    
         <!-- Download from https://login.salesforce.com/dwnld/SfdcAnt/salesforce_ant_32.0.zip -->
         <taskdef uri="antlib:com.salesforce" classpath="lib/salesforce_ant_32.0/ant-salesforce.jar" />
    
         <!-- Download from https://code.google.com/p/missing-link/ -->
         <taskdef name="http" uri="org.missinglink.ant.task.http.HttpClientTask" classname="org.missinglink.ant.task.http.HttpClientTask" classpath="lib/ml-ant-http-1.1.3/ml-ant-http-1.1.3.jar"/>
    
         <property file="build.properties"/>
         <property environment="env"/>
    
         <target name="deploy" description="Deploys all package components to dev org">
             <!-- stop scheduled job -->
             <executeAnonymous username="${de.username}" password="${de.password}" serverurl="${de.serverurl}"><![CDATA[
                 for (SObject j : [SELECT Id FROM CronTrigger]) System.abortJob(j.Id);
             ]]></executeAnonymous>
    
             <!-- upload code -->
             <sf:deploy
                 username="${de.username}"
                 password="${de.password}"
                 serverurl="https://login.salesforce.com"
                 deployRoot="src"
             />
    
             <!-- restart scheduled job -->
             <executeAnonymous username="${de.username}" password="${de.password}" serverurl="${de.serverurl}"><![CDATA[
                 System.schedule('New Job', '0 0 * * * ?', new Job());
             ]]></executeAnonymous>
         </target>
    
         <macrodef name="executeAnonymous" description="Invoke ExecuteAnonymous using Salesforce Tooling API">
             <attribute name="username" description="Salesforce user name."/>
             <attribute name="password" description="Salesforce password."/>
             <attribute name="serverurl" description="Salesforce endpoint."/>
             <text name="apexcode"/>
             <sequential>
                 <!-- Login to populate serverUrl / sessionId / instance -->
                 <login username="@{username}" password="@{password}" serverurl="@{serverurl}" />
    
                 <!-- Execute using HTTP GET -->
                 <ml:http url="${instance}/services/data/v34.0/tooling/executeAnonymous" method="GET" outfile="lib/executeAnonymousResult.json"  statusProperty="loginResponseStatus">
                     <headers>
                         <header name="Authorization" value="Bearer ${sessionId}"/>
                     </headers>
                     <query>
                         <parameter name="anonymousBody" value="@{apexcode}"/>
                     </query>
                 </ml:http>
    
                 <!-- Fail on error -->
                 <loadfile property="executeAnonymousResult" srcFile="lib/executeAnonymousResult.json" />
                 <fail message="Execute Anonymous failed: ${executeAnonymousResult}">
                     <condition>
                         <resourcecontains resource="lib/executeAnonymousResult.json" substring="&quot;success&quot;:false" />
                     </condition>
                 </fail>
    
                 <!-- Show output -->
                 <echo message="${executeAnonymousResult}" />
             </sequential>
         </macrodef>
    
         <macrodef name="login" description="Login to salesforce and populate serverUrl / sessionId / instance">
             <attribute name="username" description="Salesforce user name."/>
             <attribute name="password" description="Salesforce password."/>    
             <attribute name="serverurl" description="Salesforce endpoint."/>    
             <sequential>
                 <!-- Obtain Session Id via Login SOAP service -->
                 <ml:http url="@{serverurl}/services/Soap/c/34.0" method="POST" failonunexpected="false" outfile="lib/loginResult.xml" statusProperty="loginResponseStatus">
                     <headers>
                         <header name="Content-Type" value="text/xml"/>
                         <header name="SOAPAction" value="login"/>
                     </headers>
                     <entity><![CDATA[
                         <env:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>
                             <env:Body>
                                 <sf:login xmlns:sf='urn:enterprise.soap.sforce.com'>
                                     <sf:username>@{username}</sf:username>
                                     <sf:password>@{password}</sf:password>
                                 </sf:login>
                             </env:Body>
                         </env:Envelope>]]>
                     </entity>
                 </ml:http>
    
                 <!-- Fail on error -->
                 <loadfile property="loginResult" srcFile="lib/loginResult.xml" />
                 <fail message="Login failed: ${loginResult}">
                     <condition>
                         <resourcecontains resource="lib/loginResult.xml" substring="faultcode" />
                     </condition>
                 </fail>
    
                 <!-- Parse response into properties -->
                 <xmlproperty file="lib/loginResult.xml" />
                 <echo file="lib/serverUrl.txt" message="${soapenv:Envelope.soapenv:Body.loginResponse.result.serverUrl}" />
                 <echo file="lib/sessionId.txt" message="${soapenv:Envelope.soapenv:Body.loginResponse.result.sessionId}" />
                 <echo file="lib/instance.txt" message="${soapenv:Envelope.soapenv:Body.loginResponse.result.serverUrl}" />
                 <replaceregexp file="lib/instance.txt" match="(https://[^/]+)/.*" replace="\1" />
                 <loadfile property="sessionId" srcFile="lib/sessionId.txt" />
                 <loadfile property="instance" srcFile="lib/instance.txt" />
             </sequential>
         </macrodef>
    
     </project>