Newer Version Available

This content describes an older version of this product. View Latest

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:

  1. Include the WSC JAR, its dependencies, and the JAR files you generated from the WSDLs.

    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.

    Note

  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.

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}