Newer Version Available
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:
- Include the WSC JAR, its dependencies, and the JAR files you generated from the WSDLs.
- 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.
- 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.
1import com.sforce.soap.enterprise.EnterpriseConnection;
2import com.sforce.soap.enterprise.LoginResult;
3import com.sforce.soap.metadata.MetadataConnection;
4import com.sforce.ws.ConnectionException;
5import com.sforce.ws.ConnectorConfig;
6
7/**
8 * Login utility.
9 */
10public class MetadataLoginUtil {
11
12 public static MetadataConnection login() throws ConnectionException {
13 final String USERNAME = "user@company.com";
14 // This is only a sample. Hard coding passwords in source files is a bad practice.
15 final String PASSWORD = "password";
16 final String URL = "https://login.salesforce.com/services/Soap/c/50.0";
17 final LoginResult loginResult = loginToSalesforce(USERNAME, PASSWORD, URL);
18 return createMetadataConnection(loginResult);
19 }
20
21 private static MetadataConnection createMetadataConnection(
22 final LoginResult loginResult) throws ConnectionException {
23 final ConnectorConfig config = new ConnectorConfig();
24 config.setServiceEndpoint(loginResult.getMetadataServerUrl());
25 config.setSessionId(loginResult.getSessionId());
26 return new MetadataConnection(config);
27 }
28
29 private static LoginResult loginToSalesforce(
30 final String username,
31 final String password,
32 final String loginUrl) throws ConnectionException {
33 final ConnectorConfig config = new ConnectorConfig();
34 config.setAuthEndpoint(loginUrl);
35 config.setServiceEndpoint(loginUrl);
36 config.setManualLogin(true);
37 return (new EnterpriseConnection(config)).login(username, password);
38 }
39}Java Sample Code for File-Based Development
The sample code logs in using the login utility. Then it displays a menu with retrieve, deploy, and exit.
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 Deploying and Retrieving Metadata with the Zip File. For this sample, the manifest file retrieves all custom objects, custom tabs, and page layouts.
1<?xml version="1.0" encoding="UTF-8"?>
2<Package xmlns="http://soap.sforce.com/2006/04/metadata">
3 <types>
4 <members>*</members>
5 <name>CustomObject</name>
6 </types>
7 <types>
8 <members>*</members>
9 <name>CustomTab</name>
10 </types>
11 <types>
12 <members>*</members>
13 <name>Layout</name>
14 </types>
15 <version>50.0</version>
16</Package>Note the error handling code that follows each API call.
1import java.io.*;
2import java.nio.channels.Channels;
3import java.nio.channels.FileChannel;
4import java.nio.channels.ReadableByteChannel;
5import java.rmi.RemoteException;
6import java.util.*;
7
8import javax.xml.parsers.*;
9
10import org.w3c.dom.*;
11import org.xml.sax.SAXException;
12
13import com.sforce.soap.metadata.*;
14
15/**
16 * Sample that logs in and shows a menu of retrieve and deploy metadata options.
17 */
18public class FileBasedDeployAndRetrieve {
19
20 private MetadataConnection metadataConnection;
21
22 private static final String ZIP_FILE = "components.zip";
23
24 // manifest file that controls which components get retrieved
25 private static final String MANIFEST_FILE = "package.xml";
26
27 private static final double API_VERSION = 29.0;
28
29 // one second in milliseconds
30 private static final long ONE_SECOND = 1000;
31
32 // maximum number of attempts to deploy the zip file
33 private static final int MAX_NUM_POLL_REQUESTS = 50;
34
35 private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
36
37 public static void main(String[] args) throws Exception {
38 FileBasedDeployAndRetrieve sample = new FileBasedDeployAndRetrieve();
39 sample.run();
40 }
41
42 public FileBasedDeployAndRetrieve() {
43 }
44
45 private void run() throws Exception {
46 this.metadataConnection = MetadataLoginUtil.login();
47
48 // Show the options to retrieve or deploy until user exits
49 String choice = getUsersChoice();
50 while (choice != null && !choice.equals("99")) {
51 if (choice.equals("1")) {
52 retrieveZip();
53 } else if (choice.equals("2")) {
54 deployZip();
55 } else {
56 break;
57 }
58 // show the options again
59 choice = getUsersChoice();
60 }
61 }
62
63 /*
64 * Utility method to present options to retrieve or deploy.
65 */
66 private String getUsersChoice() throws IOException {
67 System.out.println(" 1: Retrieve");
68 System.out.println(" 2: Deploy");
69 System.out.println("99: Exit");
70 System.out.println();
71 System.out.print("Enter 1 to retrieve, 2 to deploy, or 99 to exit: ");
72 // wait for the user input.
73 String choice = reader.readLine();
74 return choice != null ? choice.trim() : "";
75 }
76
77 private void deployZip() throws Exception {
78 byte zipBytes[] = readZipFile();
79 DeployOptions deployOptions = new DeployOptions();
80 deployOptions.setPerformRetrieve(false);
81 deployOptions.setRollbackOnError(true);
82 AsyncResult asyncResult = metadataConnection.deploy(zipBytes, deployOptions);
83 DeployResult result = waitForDeployCompletion(asyncResult.getId());
84 if (!result.isSuccess()) {
85 printErrors(result, "Final list of failures:\n");
86 throw new Exception("The files were not successfully deployed");
87 }
88 System.out.println("The file " + ZIP_FILE + " was successfully deployed\n");
89 }
90
91 /*
92 * Read the zip file contents into a byte array.
93 */
94 private byte[] readZipFile() throws Exception {
95 byte[] result = null;
96 // We assume here that you have a deploy.zip file.
97 // See the retrieve sample for how to retrieve a zip file.
98 File zipFile = new File(ZIP_FILE);
99 if (!zipFile.exists() || !zipFile.isFile()) {
100 throw new Exception("Cannot find the zip file for deploy() on path:"
101 + zipFile.getAbsolutePath());
102 }
103
104 FileInputStream fileInputStream = new FileInputStream(zipFile);
105 try {
106 ByteArrayOutputStream bos = new ByteArrayOutputStream();
107 byte[] buffer = new byte[4096];
108 int bytesRead = 0;
109 while (-1 != (bytesRead = fileInputStream.read(buffer))) {
110 bos.write(buffer, 0, bytesRead);
111 }
112
113 result = bos.toByteArray();
114 } finally {
115 fileInputStream.close();
116 }
117 return result;
118 }
119
120 /*
121 * Print out any errors, if any, related to the deploy.
122 * @param result - DeployResult
123 */
124 private void printErrors(DeployResult result, String messageHeader) {
125 DeployDetails details = result.getDetails();
126 StringBuilder stringBuilder = new StringBuilder();
127 if (details != null) {
128 DeployMessage[] componentFailures = details.getComponentFailures();
129 for (DeployMessage failure : componentFailures) {
130 String loc = "(" + failure.getLineNumber() + ", " + failure.getColumnNumber();
131 if (loc.length() == 0 && !failure.getFileName().equals(failure.getFullName()))
132 {
133 loc = "(" + failure.getFullName() + ")";
134 }
135 stringBuilder.append(failure.getFileName() + loc + ":"
136 + failure.getProblem()).append('\n');
137 }
138 RunTestsResult rtr = details.getRunTestResult();
139 if (rtr.getFailures() != null) {
140 for (RunTestFailure failure : rtr.getFailures()) {
141 String n = (failure.getNamespace() == null ? "" :
142 (failure.getNamespace() + ".")) + failure.getName();
143 stringBuilder.append("Test failure, method: " + n + "." +
144 failure.getMethodName() + " -- " + failure.getMessage() +
145 " stack " + failure.getStackTrace() + "\n\n");
146 }
147 }
148 if (rtr.getCodeCoverageWarnings() != null) {
149 for (CodeCoverageWarning ccw : rtr.getCodeCoverageWarnings()) {
150 stringBuilder.append("Code coverage issue");
151 if (ccw.getName() != null) {
152 String n = (ccw.getNamespace() == null ? "" :
153 (ccw.getNamespace() + ".")) + ccw.getName();
154 stringBuilder.append(", class: " + n);
155 }
156 stringBuilder.append(" -- " + ccw.getMessage() + "\n");
157 }
158 }
159 }
160 if (stringBuilder.length() > 0) {
161 stringBuilder.insert(0, messageHeader);
162 System.out.println(stringBuilder.toString());
163 }
164 }
165
166
167 private void retrieveZip() throws Exception {
168 RetrieveRequest retrieveRequest = new RetrieveRequest();
169 // The version in package.xml overrides the version in RetrieveRequest
170 retrieveRequest.setApiVersion(API_VERSION);
171 setUnpackaged(retrieveRequest);
172
173 AsyncResult asyncResult = metadataConnection.retrieve(retrieveRequest);
174 RetrieveResult result = waitForRetrieveCompletion(asyncResult);
175
176 if (result.getStatus() == RetrieveStatus.Failed) {
177 throw new Exception(result.getErrorStatusCode() + " msg: " +
178 result.getErrorMessage());
179 } else if (result.getStatus() == RetrieveStatus.Succeeded) {
180 // Print out any warning messages
181 StringBuilder stringBuilder = new StringBuilder();
182 if (result.getMessages() != null) {
183 for (RetrieveMessage rm : result.getMessages()) {
184 stringBuilder.append(rm.getFileName() + " - " + rm.getProblem() + "\n");
185 }
186 }
187 if (stringBuilder.length() > 0) {
188 System.out.println("Retrieve warnings:\n" + stringBuilder);
189 }
190
191 System.out.println("Writing results to zip file");
192 File resultsFile = new File(ZIP_FILE);
193 FileOutputStream os = new FileOutputStream(resultsFile);
194
195 try {
196 os.write(result.getZipFile());
197 } finally {
198 os.close();
199 }
200 }
201 }
202
203 private DeployResult waitForDeployCompletion(String asyncResultId) throws Exception {
204 int poll = 0;
205 long waitTimeMilliSecs = ONE_SECOND;
206 DeployResult deployResult;
207 boolean fetchDetails;
208 do {
209 Thread.sleep(waitTimeMilliSecs);
210 // double the wait time for the next iteration
211
212 waitTimeMilliSecs *= 2;
213 if (poll++ > MAX_NUM_POLL_REQUESTS) {
214 throw new Exception(
215 "Request timed out. If this is a large set of metadata components, " +
216 "ensure that MAX_NUM_POLL_REQUESTS is sufficient.");
217 }
218 // Fetch in-progress details once for every 3 polls
219 fetchDetails = (poll % 3 == 0);
220
221 deployResult = metadataConnection.checkDeployStatus(asyncResultId, fetchDetails);
222 System.out.println("Status is: " + deployResult.getStatus());
223 if (!deployResult.isDone() && fetchDetails) {
224 printErrors(deployResult, "Failures for deployment in progress:\n");
225 }
226 }
227 while (!deployResult.isDone());
228
229 if (!deployResult.isSuccess() && deployResult.getErrorStatusCode() != null) {
230 throw new Exception(deployResult.getErrorStatusCode() + " msg: " +
231 deployResult.getErrorMessage());
232 }
233
234 if (!fetchDetails) {
235 // Get the final result with details if we didn't do it in the last attempt.
236 deployResult = metadataConnection.checkDeployStatus(asyncResultId, true);
237 }
238
239 return deployResult;
240 }
241
242 private RetrieveResult waitForRetrieveCompletion(AsyncResult asyncResult) throws Exception {
243 // Wait for the retrieve to complete
244 int poll = 0;
245 long waitTimeMilliSecs = ONE_SECOND;
246 String asyncResultId = asyncResult.getId();
247 RetrieveResult result = null;
248 do {
249 Thread.sleep(waitTimeMilliSecs);
250 // Double the wait time for the next iteration
251 waitTimeMilliSecs *= 2;
252 if (poll++ > MAX_NUM_POLL_REQUESTS) {
253 throw new Exception("Request timed out. If this is a large set " +
254 "of metadata components, check that the time allowed " +
255 "by MAX_NUM_POLL_REQUESTS is sufficient.");
256 }
257 result = metadataConnection.checkRetrieveStatus(
258 asyncResultId, true);
259 System.out.println("Retrieve Status: " + result.getStatus());
260 } while (!result.isDone());
261
262 return result;
263 }
264
265 private void setUnpackaged(RetrieveRequest request) throws Exception {
266 // Edit the path, if necessary, if your package.xml file is located elsewhere
267 File unpackedManifest = new File(MANIFEST_FILE);
268 System.out.println("Manifest file: " + unpackedManifest.getAbsolutePath());
269
270 if (!unpackedManifest.exists() || !unpackedManifest.isFile()) {
271 throw new Exception("Should provide a valid retrieve manifest " +
272 "for unpackaged content. Looking for " +
273 unpackedManifest.getAbsolutePath());
274 }
275
276 // Note that we use the fully quualified class name because
277 // of a collision with the java.lang.Package class
278 com.sforce.soap.metadata.Package p = parsePackageManifest(unpackedManifest);
279 request.setUnpackaged(p);
280 }
281
282 private com.sforce.soap.metadata.Package parsePackageManifest(File file)
283 throws ParserConfigurationException, IOException, SAXException {
284 com.sforce.soap.metadata.Package packageManifest = null;
285 List<PackageTypeMembers> listPackageTypes = new ArrayList<PackageTypeMembers>();
286 DocumentBuilder db =
287 DocumentBuilderFactory.newInstance().newDocumentBuilder();
288 InputStream inputStream = new FileInputStream(file);
289 Element d = db.parse(inputStream).getDocumentElement();
290 for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling()) {
291 if (c instanceof Element) {
292 Element ce = (Element) c;
293 NodeList nodeList = ce.getElementsByTagName("name");
294 if (nodeList.getLength() == 0) {
295 continue;
296 }
297 String name = nodeList.item(0).getTextContent();
298 NodeList m = ce.getElementsByTagName("members");
299 List<String> members = new ArrayList<String>();
300 for (int i = 0; i < m.getLength(); i++) {
301 Node mm = m.item(i);
302 members.add(mm.getTextContent());
303 }
304 PackageTypeMembers packageTypes = new PackageTypeMembers();
305 packageTypes.setName(name);
306 packageTypes.setMembers(members.toArray(new String[members.size()]));
307 listPackageTypes.add(packageTypes);
308 }
309 }
310 packageManifest = new com.sforce.soap.metadata.Package();
311 PackageTypeMembers[] packageTypesArray =
312 new PackageTypeMembers[listPackageTypes.size()];
313 packageManifest.setTypes(listPackageTypes.toArray(packageTypesArray));
314 packageManifest.setVersion(API_VERSION + "");
315 return packageManifest;
316 }
317}