Quick Start. salesforce Metadata API
Metadata API is a powerful tool for managing customizations in Salesforce organizations. It allows you to retrieve, deploy, create, update, or delete metadata components such as custom objects, page layouts, and Apex code.
Advertisement
Advertisement
CHAPTER 2
Quick Start
Use Metadata API to retrieve, deploy, create, update, or delete customizations for your org. The most common use is to migrate changes from a sandbox or testing org to your production environment. Metadata API is intended for managing customizations and for building tools that can manage the metadata model, not the data itself.
However, the underlying calls of Metadata API have been exposed for you to use directly, if you prefer to build your own client applications.
This quick start gives you all the information you need to start writing applications that directly use Metadata API to manage customizations for your organization. It shows you how to get started with File-Based Development. For an example of CRUD-Based Development, see
Java Sample for CRUD-Based Development with Synchronous Calls
.
Prerequisites
Make sure you complete these prerequisites before you start using Metadata API.
•
Create a development environment.
It is strongly recommended that you use a sandbox, which is an exact replica of your production organization. Enterprise, Unlimited, and Performance Editions come with free developer sandboxes. For more information, see http://www.salesforce.com/platform/cloud-infrastructure/sandbox.jsp
.
Alternatively, you can use a Developer Edition org, which provides access to all of the features available with Enterprise Edition, but is limited by the number of users and the amount of storage space. A Developer Edition org isn’t a copy of your production org, but it provides an environment where you can build and test your solutions without affecting your organization’s data. Developer Edition accounts are available for free at http://developer.salesforce.com/signup
.
•
Identify a user that has the “API Enabled” and “Modify All Data” permissions. These permissions are required to access Metadata API calls.
•
Install a SOAP client. Metadata API works with current SOAP development environments, including, but not limited to, Visual Studio
®
.NET and the Force.com Web Service Connector (WSC).
In this document, we provide Java examples based on WSC and JDK 6 (Java Platform Standard Edition Development Kit 6). To run the samples, first download the latest force-wsc JAR file and its dependencies (dependencies are listed on the page when you select a version) from mvnrepository.com/artifact/com.force.api/force-wsc/ .
Note:
Development platforms vary in their SOAP implementations. Implementation differences in certain development platforms might prevent access to some or all of the features in Metadata API.
Step 1: Generate or Obtain the Web Service WSDLs for Your
Organization
To access Metadata API calls, you need a Web Service Description Language (WSDL) file. The WSDL file defines the Web service that is available to you. Your development platform uses this WSDL to generate stub code to access the Web service it defines. You can either
4
Quick Start Step 2: Import the WSDL Files Into Your Development Platform obtain the WSDL file from your organization’s Salesforce administrator, or you can generate it yourself if you have access to the WSDL download page in the Salesforce user interface. For more information about WSDL, see http://www.w3.org/TR/wsdl
.
Before you can access Metadata API calls, you must authenticate to use the Web service using the login()
call, which is defined in the enterprise WSDL and the partner WSDL. Therefore, you must also obtain one of these WSDLs.
Any user with the “Modify All Data” permission can download the WSDL file to integrate and extend the Salesforce platform. (The System
Administrator profile has this permission.)
The sample code in
equally well.
To generate the metadata and enterprise WSDL files for your organization:
1. Log in to your Salesforce account. You must log in as an administrator or as a user who has the “Modify All Data” permission.
2. From Setup, enter
API
in the
Quick Find
box, then select API.
3. Click Generate Metadata WSDL and save the XML WSDL file to your file system.
4. Click Generate Enterprise WSDL and save the XML WSDL file to your file system.
Step 2: Import the WSDL Files Into Your Development Platform
Once you have the WSDL files, import them into your development platform so that your development environment can generate the necessary objects for use in building client Web service applications. This section provides sample instructions for WSC. For instructions about other development platforms, see your platform’s product documentation.
Note: The process for importing WSDL files is identical for the metadata and enterprise WSDL files.
Instructions for Java Environments (WSC)
Java environments access the API through Java objects that serve as proxies for their server-side counterparts. Before using the API, you must first generate these objects from your organization’s WSDL file.
Each SOAP client has its own tool for this process. For WSC, use the wsdlc
utility.
Note:
Before you run wsdlc
, you must have the WSC JAR file installed on your system and referenced in your classpath. You can download the latest force-wsc JAR file and its dependencies (dependencies are listed on the page when you select a version) from mvnrepository.com/artifact/com.force.api/force-wsc/ .
The basic syntax for wsdlc
is: java -classpath pathToWsc;pathToWscDependencies com.sforce.ws.tools.wsdlc
pathToWsdl/WsdlFilename pathToOutputJar/OutputJarFilename
For example, on Windows: java –classpath force-wsc-30.0.0.jar;ST4-4.0.7.jar;antlr-runtime-3.5.jar
com.sforce.ws.tools.wsdlc metadata.wsdl metadata.jar
On Mac OS X and Unix, use a colon instead of a semicolon in between items in the classpath: java –classpath force-wsc-30.0.0.jar:ST4-4.0.7.jar:antlr-runtime-3.5.jar
com.sforce.ws.tools.wsdlc metadata.wsdl metadata.jar
wsdlc
generates a JAR file and Java source code and bytecode files for use in creating client applications. Repeat this process for the enterprise WSDL to create an enterprise.JAR file.
5
Quick Start Step 3: Walk Through the Java Sample Code
Step 3: Walk Through the Java Sample Code
When you have imported the WSDL files, you can build client applications that use Metadata API. The sample is a good starting point for writing your own code.
Before you run the sample, modify your project and the code to:
1. Include the WSC JAR, its dependencies, and the JAR files you generated from the WSDLs.
Note: Although WSC has other dependencies, the following sample only requires Rhino ( js-1.7R2.jar
), which you can download from mvnrepository.com/artifact/rhino/js .
2. Update USERNAME and PASSWORD variables in the
MetadataLoginUtil.login()
method with your user name and password. If your current IP address isn’t in your organization's trusted IP range, you'll need to append a security token to the password.
3. If you are using a sandbox, be sure to change the login URL.
Login Utility
Java users can use
ConnectorConfig
to connect to Enterprise, Partner, and Metadata SOAP API.
MetadataLoginUtil
creates a
ConnectorConfig
object and logs in using the Enterprise WSDL login method. Then it retrieves sessionId
and metadataServerUrl
to create a
ConnectorConfig
and connects to Metadata API endpoint.
ConnectorConfig
is defined in WSC.
The
MetadataLoginUtil
class abstracts the login code from the other parts of the sample, allowing portions of this code to be reused without change across different Salesforce APIs.
import com.sforce.soap.enterprise.EnterpriseConnection; import com.sforce.soap.enterprise.LoginResult; import com.sforce.soap.metadata.MetadataConnection; import com.sforce.ws.ConnectionException; import com.sforce.ws.ConnectorConfig;
/**
* Login utility.
*/ public class MetadataLoginUtil { public static MetadataConnection login() throws ConnectionException { final String USERNAME = "[email protected]" ;
// This is only a sample. Hard coding passwords in source files is a bad practice.
final String PASSWORD = "password" ; final String URL = "https://login.salesforce.com/services/Soap/c/ 39.0
"; final LoginResult loginResult = loginToSalesforce(USERNAME, PASSWORD, URL); return createMetadataConnection(loginResult);
} private static MetadataConnection createMetadataConnection( final LoginResult loginResult) throws ConnectionException { final ConnectorConfig config = new ConnectorConfig(); config.setServiceEndpoint(loginResult.getMetadataServerUrl()); config.setSessionId(loginResult.getSessionId()); return new MetadataConnection(config);
}
6
Quick Start Step 3: Walk Through the Java Sample Code
} private static LoginResult loginToSalesforce( final String username, final String password, final String loginUrl) throws ConnectionException { final ConnectorConfig config = new ConnectorConfig(); config.setAuthEndpoint(loginUrl); config.setServiceEndpoint(loginUrl); config.setManualLogin( true ); return ( new EnterpriseConnection(config)).login(username, password);
}
Note:
This example uses user and password authentication to obtain a session ID, which is then used for making calls to Metadata
API. Alternatively, you can use OAuth authentication. After you athenticate with OAuth to Salesforce, pass the returned access token instead of the session ID. For example, pass the access token to the setSessionId()
call on
ConnectorConfig
.
To learn how to use OAuth authentication in Salesforce, see Authenticating Apps with OAuth in the Salesforce Help.
Java Sample Code for File-Based Development
The retrieve()
and deploy()
calls both operate on a .zip file named components.zip
. The retrieve()
call retrieves components from your organization into components.zip
, and the deploy()
call deploys the components in components.zip
to your organization. If you save the sample to your computer and execute it, run the retrieve option first so that you have a components.zip
file that you can subsequently deploy. After a retrieve call, the sample calls checkRetrieveStatus()
in a loop until the operation is completed. Similarly, after a deploy call, the sample checks checkDeployStatus()
in a loop until the operation is completed.
The retrieve()
call uses a manifest file to determine the components to retrieve from your organization. A sample package.xml
manifest file follows. For more details on the manifest file structure, see
Working with the Zip File . For this sample, the manifest file
retrieves all custom objects, custom tabs, and page layouts.
<?xml
version= "1.0" encoding= "UTF-8"?>
<Package xmlns= "http://soap.sforce.com/2006/04/metadata">
<types >
<members >* </members >
<name >CustomObject </name >
</types >
<types >
<members >* </members >
<name >CustomTab </name >
</types >
<types >
<members >* </members >
<name >Layout </name >
</types >
<version >39.0
</version >
</Package >
Note the error handling code that follows each API call.
7
Quick Start Step 3: Walk Through the Java Sample Code
Note: This sample requires API version 34.0 or later.
import java.io.*; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.rmi.RemoteException; import java.util.*; import javax.xml.parsers.*; import org.w3c.dom.*; import org.xml.sax.SAXException; import com.sforce.soap.metadata.*;
/**
* Sample that logs in and shows a menu of retrieve and deploy metadata options.
*/ public class FileBasedDeployAndRetrieve { private MetadataConnection metadataConnection; private static final String ZIP_FILE = "components.zip" ;
// manifest file that controls which components get retrieved private static final String MANIFEST_FILE = "package.xml" ; private static final double API_VERSION = 29.0;
// one second in milliseconds private static final long ONE_SECOND = 1000;
// maximum number of attempts to deploy the zip file private static final int MAX_NUM_POLL_REQUESTS = 50; private BufferedReader reader = new BufferedReader( new InputStreamReader(System.
in )); public static void main( String [] args) throws Exception {
FileBasedDeployAndRetrieve sample = new FileBasedDeployAndRetrieve(); sample.run();
} public FileBasedDeployAndRetrieve() {
} private void run() throws Exception { this .metadataConnection = MetadataLoginUtil.login();
// Show the options to retrieve or deploy until user exits
String choice = getUsersChoice(); while (choice != null && !choice.equals( "99" )) { if (choice.equals( "1" )) {
8
Quick Start Step 3: Walk Through the Java Sample Code retrieveZip();
} else if (choice.equals( "2" )) { deployZip();
} else { break ;
}
// show the options again choice = getUsersChoice();
}
}
/*
* Utility method to present options to retrieve or deploy.
*/ private String getUsersChoice() throws IOException {
System.out.println( " 1: Retrieve" );
System.out.println( " 2: Deploy" );
System.out.println( "99: Exit" );
System.out.println();
System.out.print( "Enter 1 to retrieve, 2 to deploy, or 99 to exit: " );
// wait for the user input.
String choice = reader.readLine(); return choice != null ? choice.trim() : "" ;
} private void deployZip() throws Exception { byte zipBytes[] = readZipFile();
DeployOptions deployOptions = new DeployOptions(); deployOptions.setPerformRetrieve( false ); deployOptions.setRollbackOnError( true );
AsyncResult asyncResult = metadataConnection.deploy(zipBytes, deployOptions);
DeployResult result = waitForDeployCompletion(asyncResult.getId()); if (!result.isSuccess()) { printErrors(result, "Final list of failures:\n" ); throw new Exception( "The files were not successfully deployed" );
}
System.out.println( "The file " + ZIP_FILE + " was successfully deployed\n" );
}
/*
* Read the zip file contents into a byte array.
*/ private byte[] readZipFile() throws Exception { byte[] result = null ;
// We assume here that you have a deploy.zip file.
// See the retrieve sample for how to retrieve a zip file.
File zipFile = new File(ZIP_FILE); if (!zipFile.exists() || !zipFile.isFile()) { throw new Exception( "Cannot find the zip file for deploy() on path:"
+ zipFile.getAbsolutePath());
}
FileInputStream fileInputStream = new FileInputStream(zipFile); try {
9
Quick Start Step 3: Walk Through the Java Sample Code
ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int bytesRead = 0; while (-1 != (bytesRead = fileInputStream.read(buffer))) { bos.write(buffer, 0, bytesRead);
} result = bos.toByteArray();
} finally { fileInputStream.close();
} return result;
}
/*
* Print out any errors, if any, related to the deploy.
* @param result - DeployResult
*/ private void printErrors(DeployResult result, String messageHeader) {
DeployDetails details = result.getDetails();
StringBuilder stringBuilder = new StringBuilder(); if (details != null ) {
DeployMessage[] componentFailures = details.getComponentFailures(); for (DeployMessage failure : componentFailures) {
String loc = "(" + failure.getLineNumber() + ", " + failure.getColumnNumber(); if (loc.length() == 0 &&
!failure.getFileName().equals(failure.getFullName()))
{ loc = "(" + failure.getFullName() + ")" ;
} stringBuilder.append(failure.getFileName() + loc + ":"
+ failure.getProblem()).append( '\n' );
}
RunTestsResult rtr = details.getRunTestResult(); if (rtr.getFailures() != null ) { for (RunTestFailure failure : rtr.getFailures()) {
String n = (failure.getNamespace() == null ?
"" :
(failure.getNamespace() + "." )) + failure.getName(); stringBuilder.append( "Test failure, method: " + n + "." + failure.getMethodName() + " -- " + failure.getMessage() +
" stack " + failure.getStackTrace() + "\n\n" );
}
} if (rtr.getCodeCoverageWarnings() != null ) { for (CodeCoverageWarning ccw : rtr.getCodeCoverageWarnings()) { stringBuilder.append( "Code coverage issue" ); if (ccw.getName() != null ) {
String n = (ccw.getNamespace() == null ?
"" :
(ccw.getNamespace() + "." )) + ccw.getName(); stringBuilder.append( ", class: " + n);
} stringBuilder.append( " -- " + ccw.getMessage() + "\n" );
}
10
Quick Start Step 3: Walk Through the Java Sample Code
}
}
} if (stringBuilder.length() > 0) { stringBuilder.
insert (0, messageHeader);
System.out.println(stringBuilder.toString());
} private void retrieveZip() throws Exception {
RetrieveRequest retrieveRequest = new RetrieveRequest();
// The version in package.xml overrides the version in RetrieveRequest retrieveRequest.setApiVersion(API_VERSION); setUnpackaged(retrieveRequest);
AsyncResult asyncResult = metadataConnection.retrieve(retrieveRequest);
RetrieveResult result = waitForRetrieveCompletion(asyncResult); if (result.getStatus() == RetrieveStatus.Failed) { throw new Exception(result.getErrorStatusCode() + " msg: " + result.getErrorMessage());
} else if (result.getStatus() == RetrieveStatus.Succeeded) {
// Print out any warning messages
StringBuilder stringBuilder = new StringBuilder(); if (result.getMessages() != null ) { for (RetrieveMessage rm : result.getMessages()) { stringBuilder.append(rm.getFileName() + " - " + rm.getProblem() + "\n" );
}
} if (stringBuilder.length() > 0) {
System.out.println( "Retrieve warnings:\n" + stringBuilder);
}
System.out.println( "Writing results to zip file" );
File resultsFile = new File(ZIP_FILE);
FileOutputStream os = new FileOutputStream(resultsFile);
}
} try { os.write(result.getZipFile());
} finally { os.close();
} private DeployResult waitForDeployCompletion( String asyncResultId) throws Exception { int poll = 0; long waitTimeMilliSecs = ONE_SECOND;
DeployResult deployResult; boolean fetchDetails; do {
Thread.sleep(waitTimeMilliSecs);
11
Quick Start Step 3: Walk Through the Java Sample Code
+
// double the wait time for the next iteration waitTimeMilliSecs *= 2; if (poll++ > MAX_NUM_POLL_REQUESTS) { throw new Exception(
"Request timed out. If this is a large set of metadata components, "
"ensure that MAX_NUM_POLL_REQUESTS is sufficient." );
}
// Fetch in-progress details once for every 3 polls fetchDetails = (poll % 3 == 0); deployResult = metadataConnection.checkDeployStatus(asyncResultId, fetchDetails);
System.out.println( "Status is: " + deployResult.getStatus()); if (!deployResult.isDone() && fetchDetails) { printErrors(deployResult, "Failures for deployment in progress:\n" );
}
} while (!deployResult.isDone()); if (!deployResult.isSuccess() && deployResult.getErrorStatusCode() != null ) { throw new Exception(deployResult.getErrorStatusCode() + " msg: " + deployResult.getErrorMessage());
} if (!fetchDetails) {
// Get the final result with details if we didn't do it in the last attempt.
deployResult = metadataConnection.checkDeployStatus(asyncResultId, true );
} return deployResult;
} private RetrieveResult waitForRetrieveCompletion(AsyncResult asyncResult) throws
Exception {
// Wait for the retrieve to complete int poll = 0; long waitTimeMilliSecs = ONE_SECOND;
String asyncResultId = asyncResult.getId();
RetrieveResult result = null ; do {
Thread.sleep(waitTimeMilliSecs);
// Double the wait time for the next iteration waitTimeMilliSecs *= 2; if (poll++ > MAX_NUM_POLL_REQUESTS) { throw new Exception( "Request timed out.
If this is a large set " +
"of metadata components, check that the time allowed " +
"by MAX_NUM_POLL_REQUESTS is sufficient." );
} result = metadataConnection.checkRetrieveStatus( asyncResultId, true );
System.out.println( "Retrieve Status: " + result.getStatus());
} while (!result.isDone());
12
Quick Start Step 3: Walk Through the Java Sample Code return result;
} private void setUnpackaged(RetrieveRequest request) throws Exception {
// Edit the path, if necessary, if your package.xml file is located elsewhere
File unpackedManifest = new File(MANIFEST_FILE);
System.out.println( "Manifest file: " + unpackedManifest.getAbsolutePath()); if (!unpackedManifest.exists() || !unpackedManifest.isFile()) { throw new Exception( "Should provide a valid retrieve manifest " +
"for unpackaged content. Looking for " + unpackedManifest.getAbsolutePath());
}
// Note that we use the fully quualified class name because
// of a collision with the java.lang.Package class com.sforce.soap.metadata.Package p = parsePackageManifest(unpackedManifest); request.setUnpackaged(p);
} private com.sforce.soap.metadata.Package parsePackageManifest(File file) throws ParserConfigurationException, IOException, SAXException { com.sforce.soap.metadata.Package packageManifest = null ;
List<PackageTypeMembers> listPackageTypes = new ArrayList<PackageTypeMembers>();
DocumentBuilder db =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream inputStream = new FileInputStream(file);
Element d = db.parse(inputStream).getDocumentElement(); for (Node c = d.getFirstChild(); c != null ; c = c.getNextSibling()) { if (c instanceof Element) {
Element ce = (Element) c;
NodeList nodeList = ce.getElementsByTagName( "name" ); if (nodeList.getLength() == 0) { continue ;
}
String name = nodeList.
item (0).getTextContent();
NodeList m = ce.getElementsByTagName( "members" );
List< String > members = new ArrayList< String >(); for (int i = 0; i < m.getLength(); i++) {
Node mm = m.
item (i); members.add(mm.getTextContent());
}
PackageTypeMembers packageTypes = new PackageTypeMembers(); packageTypes.setName(name); packageTypes.setMembers(members.toArray( new String [members.size()])); listPackageTypes.add(packageTypes);
}
} packageManifest = new com.sforce.soap.metadata.Package();
PackageTypeMembers[] packageTypesArray = new PackageTypeMembers[listPackageTypes.size()]; packageManifest.setTypes(listPackageTypes.toArray(packageTypesArray)); packageManifest.setVersion(API_VERSION + "" );
13
Quick Start
}
} return packageManifest;
Step 3: Walk Through the Java Sample Code
14
Download
Advertisement
Key features
Retrieve metadata from your org
Deploy metadata to your org
Create new metadata components
Update existing metadata components
Delete metadata components from your org
Supports both file-based and CRUD-based development
Complies with industry standards for web services
Provides comprehensive error handling
Frequently asked questions
Metadata API is supported in Enterprise, Unlimited, Performance, and Developer Editions. It is recommended to use a sandbox for testing changes before deploying to production.
File-based development involves deploying and retrieving metadata as a zip file containing XML files. CRUD-based development uses synchronous API calls similar to the enterprise WSDL.
You can use the Force.com IDE or Force.com Migration Tool, or directly interact with the API using SOAP calls. This documentation provides examples and guidance for both approaches.