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