ステップ 3: Java サンプルコードの説明
WSDL ファイルをインポートすると、メタデータ API を使用するクライアントアプリケーションの構築を開始できます。このサンプルは、独自のコードを記述するための出発点として適しています。
サンプルを実行する前に、プロジェクトとコードを次のように変更します。
- WSC JAR、その連動関係、および WSDL から生成した JAR ファイルを含めます。
- 自分のユーザ名とパスワードを使って、MetadataLoginUtil.login() メソッドの USERNAME 変数と PASSWORD 変数を更新します。現在の IP アドレスが組織の信頼済み IP 範囲内にない場合は、セキュリティトークンをパスワードに追加する必要があります。
- Sandbox を使用している場合は、必ずログイン URL を変更してください。
ログインユーティリティ
Java ユーザは、ConnectorConfig を使用して、Enterprise API、Partner API、および Metadata SOAP API に接続できます。MetadataLoginUtil は ConnectorConfig オブジェクトを作成し、Enterprise WSDL の login メソッドを使用してログインします。次に、sessionId と metadataServerUrl を取得して ConnectorConfig を作成し、メタデータ API のエンドポイントに接続します。ConnectorConfig は WSC で定義されています。
MetadataLoginUtil クラスは、サンプルの他の部分からログインコードを抽象化するため、Salesforce API ごとに変更を行わずにこのコードの一部を再利用できます。
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 のサンプルコード
サンプルコードは、ログインユーティリティを使用してログインします。次に、取得、リリース、および終了のメニューを表示します。
retrieve() コールおよび deploy() コールは両方とも components.zip という名前の .zip ファイルを処理します。retrieve() コールは組織のコンポーネントを components.zip に取得し、deploy() コールは components.zip のコンポーネントを組織にリリースします。コンピュータにサンプルを保存して実行する場合は、後でリリースできる components.zip ファイルを含めることができるように、まず取得オプションを実行します。retrieve コールの後、サンプルは、操作が完了するまで checkRetrieveStatus() コールをループします。同様に、deploy コールの後、サンプルは、操作が完了するまで checkDeployStatus() チェックをループします。
retrieve() コールは、マニフェストファイルを使用して組織から取得するコンポーネントを決定します。package.xml マニフェストファイルのサンプルは次のとおりです。マニフェストファイルの構造についての詳細は、「Zip ファイルの使用」を参照してください。このサンプルでは、マニフェストファイルはすべてのカスタムオブジェクト、カスタムタブ、およびページレイアウトを取得します。
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>API コールに続く、エラー処理コードに注意してください。
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}