Newer Version Available

This content describes an older version of this product. View Latest

Using Tooling SOAP API

Use SOAP API if you’re using a strongly typed language like Java that generates Web service client code. Tooling SOAP API is used just like the Salesforce SOAP API. For details on usage, syntax, and authentication, see the SOAP API Developer's Guide.

To access the Tooling API WSDL, from Setup, click Develop | API and click Generate Tooling WSDL.

Like the Salesforce SOAP API, Tooling API uses the following calls.

Call Description
create() Adds one or more new records to your organization’s data.
delete() Deletes one or more records from your organization’s data.
describeGlobal() Lists the available Tooling API objects and their metadata.
describeSObjects() Describes metadata (field list and object properties) for the specified object or array of objects.

Call describeGlobal() to retrieve a list of all Tooling API objects for your organization, then iterate through the list and use describeSObjects() to obtain metadata about individual objects.

executeanonymous(string apexcode) Executes the specified block of Apex anonymously and returns the result.
query() Executes a query against a Tooling API object and returns data that matches the specified criteria.
retrieve() Retrieves one or more records based on the specified IDs.
runTests() and runTestsAsynchronous() Executes test methods in the specified classes. Running tests asynchronously allows methods to process in parallel, cutting down your test run times. For example code, see ApexTestQueueItem.
update() Updates one or more existing records in your organization’s data.
upsert() Creates new records and updates existing records; uses a custom field to determine the presence of existing records.

Examples

These examples use Java, but you can use Tooling SOAP API in any language that supports Web services.

Use create() to compile Apex classes or triggers in Developer Edition or sandbox organizations. The first example below uses ApexClass to compile a simple class with a single method called SayHello.
1swfobject.registerObject("clippy.codeblock-0", "9");   String classBody = "public class Messages {\n"
2      + "public string SayHello() {\n"
3      + " return 'Hello';\n" + "}\n"
4      + "}";
5
6   // create a new ApexClass object and set the body 
7   ApexClass apexClass = new ApexClass();
8   apexClass.Body = classBody;
9   ApexClass[] classes = { apexClass };
10
11   // call create() to add the class
12   SaveResult[] saveResults = sforce.create(classes);
13   for (int i = 0; i < saveResults.Length; i++)
14      {
15      if (saveResults[i].success)
16         {
17           Console.WriteLine("Successfully created Class: " +
18            saveResults[i].id);
19         }
20      else
21         {
22            Console.WriteLine("Error: could not create Class ");
23            Console.WriteLine("   The error reported was: " +
24            saveResults[i].errors[0].message + "\n");
25         }
26      }
27

Use the IsCheckOnly parameter on ContainerAsyncRequest to indicate whether an asynchronous request should compile code without making any changes to the organization (true) or compile and save the code (false).

The example below expands upon the first by modifying the SayHello() method to accept a person’s first and last name. This example uses MetadataContainer with ApexClassMember to retrieve and update the class, and ContainerAsyncRequest to compile and deploy the changes to the server. You can use the same method with ApexTriggerMember, ApexComponentMember, and ApexPageMember.
To test your code, modify the IsCheckOnly parameter in the code below, and log in to your org after a successful execution to verify the results.
  • When IsCheckOnly = true, the SayHello() method should remain the same. (ApexClassMember contains the compiled results, but the class on the server remains the same.)
  • When IsCheckOnly = false, the SayHello() method should show the change to accept a person’s first and last name.

Note

1swfobject.registerObject("clippy.codeblock-1", "9");   String updatedClassBody = "public class Messages {\n"
2      + "public string SayHello(string fName, string lName) {\n"
3      + " return 'Hello ' + fName + ' ' + lName;\n" + "}\n"
4      + "}";
5
6   //create the metadata container object
7   MetadataContainer Container = new MetadataContainer();
8   Container.Name = "SampleContainer";
9
10   MetadataContainer[] Containers = { Container };
11   SaveResult[] containerResults = sforce.create(Containers);
12   if (containerResults[0].success)
13   {
14      String containerId = containerResults[0].id;
15
16      //create the ApexClassMember object
17      ApexClassMember classMember = new ApexClassMember();
18      //pass in the class ID from the first example
19      classMember.ContentEntityId = classId;
20      classMember.Body = updatedClassBody;
21      //pass the ID of the container created in the first step
22      classMember.MetadataContainerId = containerId;
23      ApexClassMember[] classMembers = { classMember };
24
25      SaveResult[] MembersResults = sforce.create(classMembers);
26      if (MembersResults[0].success)
27      {
28         //create the ContainerAsyncRequest object
29         ContainerAsyncRequest request = new ContainerAsyncRequest();
30         //if the code compiled successfully, save the updated class to the server
31         //change to IsCheckOnly = true to compile without saving 
32         request.IsCheckOnly = false;
33         request.MetadataContainerId = containerId;
34         ContainerAsyncRequest[] requests = { request };
35         SaveResult[] RequestResults = sforce.create(requests);
36         if (RequestResults[0].success)
37         {
38            string requestId = RequestResults[0].id;
39
40            //poll the server until the process completes
41            QueryResult queryResult = null;
42            String soql = "SELECT Id, State, CompilerErrors, ErrorMsg FROM ContainerAsyncRequest where id = '" + requestId + "'";
43            queryResult = sforce.query(soql);
44            if (queryResult.size > 0)
45            {
46               ContainerAsyncRequest _request = (ContainerAsyncRequest)queryResult.records[0];
47               while (_request.State.ToLower() == "queued")
48               {
49                  //pause the process for 2 seconds
50                  Thread.Sleep(2000);
51
52                  //poll the server again for completion
53                  queryResult = sforce.query(soql);
54                  _request = (ContainerAsyncRequest)queryResult.records[0];
55               }
56
57               //now process the result
58               switch (_request.State)
59               {
60                  case "Invalidated":
61                     break;
62
63                  case "Completed":
64                  //class compiled successfully
65                  //see the next example on how to process the SymbolTable 
66                     break;
67
68                  case "Failed":
69               . .   break;
70
71                  case "Error":
72                     break;
73
74                  case "Aborted":
75                     break;
76
77                  }
78               }
79               else
80               {
81                  //no rows returned
82               }
83            }
84            else
85            {
86               Console.WriteLine("Error: could not create ContainerAsyncRequest object");
87               Console.WriteLine("   The error reported was: " +
88               RequestResults[0].errors[0].message + "\n");
89            }
90         }
91         else
92         {
93            Console.WriteLine("Error: could not create Class Member ");
94            Console.WriteLine("   The error reported was: " +
95            MembersResults[0].errors[0].message + "\n");
96         }
97      }
98      else
99      {
100      .. Console.WriteLine("Error: could not create MetadataContainer ");
101         Console.WriteLine("   The error reported was: " +
102         containerResults[0].errors[0].message + "\n");
103      }
104   }
105

Use a SymbolTable to access Apex class and trigger data in a structured format.

The example below queries the ApexClassMember object created in the previous example to obtain the SymbolTable of the modified class.

The SOQL statement used depends on when the data is retrieved.

  • To execute the query from within the example above, use the ID of the ContainerAsyncRequest. For example, SELECT Body, ContentEntityId, SymbolTable FROM ApexClassMember where MetadataContainerId = '" + requestId + "'"
  • Otherwise, use the ID of the modified class as shown below. For example, SELECT ContentEntityId, SymbolTable FROM ApexClassMember where ContentEntityId = '" + classId + "'"

Note

1swfobject.registerObject("clippy.codeblock-2", "9");   //use the ID of the class from the previous step
2   string classId = "01pA00000036itIIAQ";
3   QueryResult queryResult = null;
4   String soql = "SELECT ContentEntityId, SymbolTable FROM ApexClassMember where ContentEntityId = '" + classId + "'";
5
6   queryResult = sforce.query(soql);
7   if (queryResult.size > 0)
8   {
9      ApexClassMember apexClass = (ApexClassMember)queryResult.records[0];
10      SymbolTable symbolTable = apexClass.SymbolTable;
11
12      foreach (Method _method in symbolTable.methods)
13      {
14         //here's the SayHello method
15         String _methodName = _method.name;
16
17         //is the method Global, Public or Private?
18         String _methodVisibility = _method.visibility.ToString();
19
20         //get the method's return type
21         string _methodReturnType = _method.returnType;
22
23         //get the fName & lName parameters
24         foreach (Parameter _parameter in _method.parameters)
25         {
26            string _paramName = _parameter.name;
27            string _parmType = _parameter.type;
28         }
29      }
30   }
31   else
32   {
33      //unable to locate class
34   } 
35

Use ApexExecutionOverlayAction to add checkpoints to your code for debugging.

This example adds a checkpoint to the class from the previous examples:
1swfobject.registerObject("clippy.codeblock-3", "9");   //use the ID of the class from the first example.
2   string classId = "01pA00000036itIIAQ";
3
4   ApexExecutionOverlayAction action = new ApexExecutionOverlayAction();
5   action.ExecutableEntityId = classId;
6   action.Line = 3;
7   action.LineSpecified = true;
8   action.Iteration = 1;
9   action.IterationSpecified = true;
10   ApexExecutionOverlayAction[] actions = { action };
11
12   SaveResult[] actionResults = sforce.create(actions);
13   if (actionResults[0].success)
14   {
15      // checkpoint created successfully
16   }
17   else
18   {
19      Console.WriteLine("Error: could not create Checkpoint ");
20      Console.WriteLine("   The error reported was: " +
21      actionResults[0].errors[0].message + "\n");
22   }
23