CRUD-Based Metadata Development
Metadata calls are different from the core, synchronous API calls in these ways.
- Metadata API calls are available in a separate WSDL. To download the WSDL, log into Salesforce, from Setup, enter API in the Quick Find box, then select API and click the Download Metadata WSDL link.
- After logging in, you must send Metadata API calls to the Metadata API endpoint, which has a different URL than SOAP API. Retrieve the metadataServerUrl from the LoginResult returned by your SOAP API login() call. For more information about SOAP API, see the SOAP API Developer Guide.
- Metadata calls are either synchronous or asynchronous. CRUD calls are synchronous in API version 30.0 and later, and similar to the API core calls the results are returned in a single call. In earlier API versions, create, update, and delete are only asynchronous, which means that the results aren’t immediately returned in one call.
- There are synchronous metadata calls that map to the corresponding core SOAP API
synchronous calls.
- createMetadata() maps to the create() SOAP API call.
- updateMetadata() maps to the update() SOAP API call.
- deleteMetadata() maps to the delete() SOAP API call.
Java Sample for CRUD-Based Development with Synchronous Calls
This section guides you through a sample Java client application that uses CRUD-based calls. This sample application performs the following main tasks.
- Uses the MetadataLoginUtil.java class to create a Metadata connection. For more information, see Step 3: Walk Through the Java Sample Code.
- Calls createMetadata() to create a custom object. This call returns the result in one call.
- Inspects the returned SaveResult object to check if the operation succeeded, and if it didn’t, writes the component name, error message, and status code to the output.
import com.sforce.soap.metadata.*;
/**
* Sample that logs in and creates a custom object through the metadata API
*/
public class CRUDSampleCreate {
private MetadataConnection metadataConnection;
// one second in milliseconds
private static final long ONE_SECOND = 1000;
public CRUDSampleCreate() {
}
public static void main(String[] args) throws Exception {
CRUDSampleCreate crudSample = new CRUDSampleCreate();
crudSample.runCreate();
}
/**
* Create a custom object. This method demonstrates usage of the
* create() and checkStatus() calls.
*
* @param uniqueName Custom object name should be unique.
*/
private void createCustomObjectSync(final String uniqueName) throws Exception {
final String label = "My Custom Object";
CustomObject co = new CustomObject();
co.setFullName(uniqueName);
co.setDeploymentStatus(DeploymentStatus.Deployed);
co.setDescription("Created by the Metadata API Sample");
co.setEnableActivities(true);
co.setLabel(label);
co.setPluralLabel(label + "s");
co.setSharingModel(SharingModel.ReadWrite);
// The name field appears in page layouts, related lists, and elsewhere.
CustomField nf = new CustomField();
nf.setType(FieldType.Text);
nf.setDescription("The custom object identifier on page layouts, related lists etc");
nf.setLabel(label);
nf.setFullName(uniqueName);
customObject.setNameField(nf);
SaveResult[] results = metadataConnection
.createMetadata(new Metadata[] { co });
for (SaveResult r : results) {
if (r.isSuccess()) {
System.out.println("Created component: " + r.getFullName());
} else {
System.out
.println("Errors were encountered while creating "
+ r.getFullName());
for (Error e : r.getErrors()) {
System.out.println("Error message: " + e.getMessage());
System.out.println("Status code: " + e.getStatusCode());
}
}
}
}
private void runCreate() throws Exception {
metadataConnection = MetadataLoginUtil.login();
// Custom objects and fields must have __c suffix in the full name.
final String uniqueObjectName = "MyCustomObject__c";
createCustomObjectSync(uniqueObjectName);
}
}
Java Sample for CRUD-Based Development with Asynchronous Calls
This section guides you through a sample Java client application that uses asynchronous CRUD-based calls. This sample application performs the following main tasks:
- Uses the MetadataLoginUtil.java class to create a Metadata connection. For more information, see Step 3: Walk Through the Java Sample Code.
- Calls create() to create a
custom object.
Salesforce returns an AsyncResult object for each component you tried to create. The AsyncResult object is updated with status information as the operation moves from a queue to completed or error state.
- Calls checkStatus() in a loop until the status value in AsyncResult indicates that the create operation is completed.
Note the error handling code that follows each API call.
import com.sforce.soap.metadata.*;
/**
* Sample that logs in and creates a custom object through the metadata api
*/
public class CRUDSample {
private MetadataConnection metadataConnection;
// one second in milliseconds
private static final long ONE_SECOND = 1000;
public CRUDSample() {
}
public static void main(String[] args) throws Exception {
CRUDSample crudSample = new CRUDSample();
crudSample.runCreate();
}
/**
* Create a custom object. This method demonstrates usage of the
* create() and checkStatus() calls.
*
* @param uniqueName Custom object name should be unique.
*/
private void createCustomObject(final String uniqueName) throws Exception {
final String label = "My Custom Object";
CustomObject customObject = new CustomObject();
customObject.setFullName(uniqueName);
customObject.setDeploymentStatus(DeploymentStatus.Deployed);
customObject.setDescription("Created by the Metadata API Sample");
customObject.setLabel(label);
customObject.setPluralLabel(label + "s");
customObject.setSharingModel(SharingModel.ReadWrite);
// The name field appears in page layouts, related lists, and elsewhere.
CustomField nf = new CustomField();
nf.setType(FieldType.Text);
nf.setDescription("The custom object identifier on page layouts, related lists etc");
nf.setLabel(label);
nf.setFullName(uniqueName);
customObject.setNameField(nf);
AsyncResult[] asyncResults = metadataConnection.create(
new CustomObject[]{customObject});
if (asyncResults == null) {
System.out.println("The object was not created successfully");
return;
}
long waitTimeMilliSecs = ONE_SECOND;
// After the create() call completes, we must poll the results of the checkStatus()
// call until it indicates that the create operation has completed.
do {
printAsyncResultStatus(asyncResults);
waitTimeMilliSecs *= 2;
Thread.sleep(waitTimeMilliSecs);
asyncResults = metadataConnection.checkStatus(new String[]{asyncResults[0].getId()});
} while (!asyncResults[0].isDone());
printAsyncResultStatus(asyncResults);
}
private void printAsyncResultStatus(AsyncResult[] asyncResults) throws Exception {
if (asyncResults == null || asyncResults.length == 0 || asyncResults[0] == null) {
throw new Exception("The object status cannot be retrieved");
}
AsyncResult asyncResult = asyncResults[0]; //we are creating only 1 metadata object
if (asyncResult.getStatusCode() != null) {
System.out.println("Error status code: " +
asyncResult.getStatusCode());
System.out.println("Error message: " + asyncResult.getMessage());
}
System.out.println("Object with id:" + asyncResult.getId() + " is " +
asyncResult.getState());
}
private void runCreate() throws Exception {
metadataConnection = MetadataLoginUtil.login();
// Custom objects and fields must have __c suffix in the full name.
final String uniqueObjectName = "MyCustomObject__c";
createCustomObject(uniqueObjectName);
}
}