By now we’re well into the Spring ’13 pre-release cycle: the release preview webinar is on Wednesday, selected sandboxes and NA1 already have the new release, and the remaining instances will be updated over the next couple of weekends. Techie that I am, my favorite new feature is the Tooling API, generally available (GA) in Spring ’13.

The Tooling API (Developer’s Guide HTML | PDF) provides REST and SOAP interfaces that enable custom development tools for the Force.com platform. This first release allows you to

  • Create/read/update/delete code artifacts such as Apex Classes and Triggers, and Visualforce Pages and Components,
  • Manage debugging features such as trace flags, heap dump markers and Apex/SOQL overlays
  • List debugging artifacts such as debug logs and heap dumps

As a demo for the upcoming webinar, I wrote a simple Apex Class browser/editor as a Java web application, showing just how to work with the REST Tooling API. Since I’m a somewhat polyglot programmer, I tend to use REST, even from Java, since I can quickly hack things out from the command line with curl, then code it up using Simple JSON, but many Java and C# developers prefer the SOAP API. If you’re in that camp, then from your org, go to Your Name | Setup | Develop | API, download the Tooling API WSDL, generate typed proxies, and use the appropriate create(), retrieve() etc method calls.

In any case, my code is all on GitHub, and if you have a pre-release login, you can go try it out on Heroku. Here are a few snippets; they all use the ToolingApi class, a very thin layer over Apache HttpClient that creates an HTTP request with the correct endpoint, access token etc, sends it, and parses the JSON response. In particular, ToolingApi prepends the instance URL and Tooling API prefix, for example https://na1.salesforce.com/services/data/v27.0/tooling/, to the URL path. Note also, that, to keep things simple, I haven’t shown error handling here, but you can find it in the ApexClassesController class.

Query

Use SOQL SELECT to retrieve a list of ApexClass objects.

JSONObject queryResponse = ToolingApi.get("query/?q="
		+ URLEncoder.encode(query, "UTF-8"));
System.out.println("Got "
		+ ((JSONArray) queryResponse.get("records")).size()
		+ " classes");

Create

To create an Apex Class, post JSON containing the class name and body to sobjects/ApexClass.

String name = "Demo";
String body = "public class " + name + " {\n\n}";

JSONObject apexClassRequest = new JSONObject();
apexClassRequest.put("Name", name);
apexClassRequest.put("Body", body);
JSONObject apexClassResponse = ToolingApi.post(
		"sobjects/ApexClass", apexClassRequest);
System.out.println("ApexClass id: " + apexClassResponse.get("id"));

Read

Just GET sobjects/ApexClass/ID.

JSONObject apexClassResponse = ToolingApi.get("sobjects/ApexClass/" + id);
System.out.println("ApexClass body: " + apexClassResponse.get("body"));

Update

Since a change might involve several code artifacts, update is a little more involved. You will need to create a MetadataContainer to hold ApexClassMember, ApexTriggerMember, ApexPageMember and ApexComponentMember objects, then create an ContainerAsyncRequest to compile and deploy the new code. Here’s a simple example with a single ApexClassMember.

//First, the MetadataContainer
JSONObject metadataContainerRequest = new JSONObject();
metadataContainerRequest.put("Name", "SaveClass" + id); // Any unique name
JSONObject metadataContainerResponse = ToolingApi.post(
		"sobjects/MetadataContainer", metadataContainerRequest);
System.out.println("MetadataContainer id: "
		+ metadataContainerResponse.get("id"));

// Then an ApexClassMember with our updated code
JSONObject apexClassMemberRequest = new JSONObject();
apexClassMemberRequest.put("MetadataContainerId",
		metadataContainerResponse.get("id"));
apexClassMemberRequest.put("ContentEntityId", id);
apexClassMemberRequest.put("Body", body);
JSONObject apexClassMemberResponse = ToolingApi.post(
		"sobjects/ApexClassMember", apexClassMemberRequest);
System.out.println("ApexClassMember id: "
		+ apexClassMemberResponse.get("id"));

// Now we can create a containerAsyncRequest
JSONObject containerAsyncRequest = new JSONObject();
containerAsyncRequest.put("MetadataContainerId",
		metadataContainerResponse.get("id"));
containerAsyncRequest.put("isCheckOnly", false);
JSONObject containerAsyncResponse = ToolingApi.post(
		"sobjects/ContainerAsyncRequest", containerAsyncRequest);
System.out.println("ContainerAsyncRequest id: "
		+ containerAsyncResponse.get("id"));

// We need to wait until the request is done
JSONObject result = ToolingApi
		.get("sobjects/ContainerAsyncRequest/"
				+ containerAsyncResponse.get("id"));
String state = (String) result.get("State");
System.out.println("State: " + state);
int wait = 1;
while (state.equals("Queued")) {
	try {
		System.out.println("Sleeping for " + wait + " second(s)");
		Thread.sleep(wait * 1000);
	} catch (InterruptedException ex) {
		Thread.currentThread().interrupt();
	}

	wait *= 2;

	result = ToolingApi.get("sobjects/ContainerAsyncRequest/"
			+ containerAsyncResponse.get("id"));
	state = (String) result.get("State");
	System.out.println("State: " + state);
}

// A real development tool would likely reuse the MetadataContainer
// but we just delete it here to be tidy
ToolingApi.delete("sobjects/MetadataContainer/"
		+ metadataContainerResponse.get("id"));

System.out.println("Request done with state: " + state)

Delete

DELETE sobjects/ApexClass/ID.

JSONObject apexClassResponse = ToolingApi.delete("sobjects/ApexClass/" + id);

What’s Next

The next few releases will bring additional Tooling API functionality – access to more resources, such as debug logs and code coverage results, and more operations, such as execute anonymous blocks of code, and run tests. We will also rewrite the Force.com IDE (Eclipse plugin) to use the Tooling API, and open source it for you to use as a template for adding Force.com capability to your favorite development environment.

Join me and Samantha Ready for the Spring ’13 Release Developer Preview webinar, this Wednesday, Jan 30, and learn more about the Tooling API and other new features!

Bookmark the permalink. Trackbacks are closed, but you can post a comment.