ステップ 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;
18
19import com.sforce.soap.enterprise.LoginResult;
20
21import com.sforce.soap.metadata.MetadataConnection;
22
23import com.sforce.ws.ConnectionException;
24
25import com.sforce.ws.ConnectorConfig;
26
27
28
29/**
30
31 * Login utility.
32
33 */
34
35public class MetadataLoginUtil {
36
37
38
39 public static MetadataConnection login() throws ConnectionException {
40
41 final String USERNAME = "user@company.com";
42
43 // This is only a sample. Hard coding passwords in source files is a bad practice.
44
45 final String PASSWORD = "password";
46
47 final String URL = "https://login.salesforce.com/services/Soap/c/33.0";
48
49 final LoginResult loginResult = loginToSalesforce(USERNAME, PASSWORD, URL);
50
51 return createMetadataConnection(loginResult);
52
53 }
54
55
56
57 private static MetadataConnection createMetadataConnection(
58
59 final LoginResult loginResult) throws ConnectionException {
60
61 final ConnectorConfig config = new ConnectorConfig();
62
63 config.setServiceEndpoint(loginResult.getMetadataServerUrl());
64
65 config.setSessionId(loginResult.getSessionId());
66
67 return new MetadataConnection(config);
68
69 }
70
71
72
73 private static LoginResult loginToSalesforce(
74
75 final String username,
76
77 final String password,
78
79 final String loginUrl) throws ConnectionException {
80
81 final ConnectorConfig config = new ConnectorConfig();
82
83 config.setAuthEndpoint(loginUrl);
84
85 config.setServiceEndpoint(loginUrl);
86
87 config.setManualLogin(true);
88
89 return (new EnterpriseConnection(config)).login(username, password);
90
91 }
92
93}ファイルベース開発用の 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
3<Package xmlns="http://soap.sforce.com/2006/04/metadata">
4
5 <types>
6
7 <members>*</members>
8
9 <name>CustomObject</name>
10
11 </types>
12
13 <types>
14
15 <members>*</members>
16
17 <name>CustomTab</name>
18
19 </types>
20
21 <types>
22
23 <members>*</members>
24
25 <name>Layout</name>
26
27 </types>
28
29 <version>33.0</version>
30
31</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.*;
18
19import java.nio.channels.Channels;
20
21import java.nio.channels.FileChannel;
22
23import java.nio.channels.ReadableByteChannel;
24
25import java.rmi.RemoteException;
26
27import java.util.*;
28
29
30
31import javax.xml.parsers.*;
32
33
34
35import org.w3c.dom.*;
36
37import org.xml.sax.SAXException;
38
39
40
41import com.sforce.soap.metadata.*;
42
43
44
45/**
46
47 * Sample that logs in and shows a menu of retrieve and deploy metadata options.
48
49 */
50
51public class FileBasedDeployAndRetrieve {
52
53
54
55 private MetadataConnection metadataConnection;
56
57
58
59 private static final String ZIP_FILE = "components.zip";
60
61
62
63 // manifest file that controls which components get retrieved
64
65 private static final String MANIFEST_FILE = "package.xml";
66
67
68
69 private static final double API_VERSION = 29.0;
70
71
72
73 // one second in milliseconds
74
75 private static final long ONE_SECOND = 1000;
76
77
78
79 // maximum number of attempts to deploy the zip file
80
81 private static final int MAX_NUM_POLL_REQUESTS = 50;
82
83
84
85 private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
86
87
88
89 public static void main(String[] args) throws Exception {
90
91 FileBasedDeployAndRetrieve sample = new FileBasedDeployAndRetrieve();
92
93 sample.run();
94
95 }
96
97
98
99 public FileBasedDeployAndRetrieve() {
100
101 }
102
103
104
105 private void run() throws Exception {
106
107 this.metadataConnection = MetadataLoginUtil.login();
108
109
110
111 // Show the options to retrieve or deploy until user exits
112
113 String choice = getUsersChoice();
114
115 while (choice != null && !choice.equals("99")) {
116
117 if (choice.equals("1")) {
118
119 retrieveZip();
120
121 } else if (choice.equals("2")) {
122
123 deployZip();
124
125 } else {
126
127 break;
128
129 }
130
131 // show the options again
132
133 choice = getUsersChoice();
134
135 }
136
137 }
138
139
140
141 /*
142
143 * Utility method to present options to retrieve or deploy.
144
145 */
146
147 private String getUsersChoice() throws IOException {
148
149 System.out.println(" 1: Retrieve");
150
151 System.out.println(" 2: Deploy");
152
153 System.out.println("99: Exit");
154
155 System.out.println();
156
157 System.out.print("Enter 1 to retrieve, 2 to deploy, or 99 to exit: ");
158
159 // wait for the user input.
160
161 String choice = reader.readLine();
162
163 return choice != null ? choice.trim() : "";
164
165 }
166
167
168
169 private void deployZip() throws Exception {
170
171 byte zipBytes[] = readZipFile();
172
173 DeployOptions deployOptions = new DeployOptions();
174
175 deployOptions.setPerformRetrieve(false);
176
177 deployOptions.setRollbackOnError(true);
178
179 AsyncResult asyncResult = metadataConnection.deploy(zipBytes, deployOptions);
180
181 DeployResult result = waitForDeployCompletion(asyncResult.getId());
182
183 if (!result.isSuccess()) {
184
185 printErrors(result, "Final list of failures:\n");
186
187 throw new Exception("The files were not successfully deployed");
188
189 }
190
191 System.out.println("The file " + ZIP_FILE + " was successfully deployed\n");
192
193 }
194
195
196
197 /*
198
199 * Read the zip file contents into a byte array.
200
201 */
202
203 private byte[] readZipFile() throws Exception {
204
205 byte[] result = null;
206
207 // We assume here that you have a deploy.zip file.
208
209 // See the retrieve sample for how to retrieve a zip file.
210
211 File zipFile = new File(ZIP_FILE);
212
213 if (!zipFile.exists() || !zipFile.isFile()) {
214
215 throw new Exception("Cannot find the zip file for deploy() on path:"
216
217 + zipFile.getAbsolutePath());
218
219 }
220
221
222
223 FileInputStream fileInputStream = new FileInputStream(zipFile);
224
225 try {
226
227 ByteArrayOutputStream bos = new ByteArrayOutputStream();
228
229 byte[] buffer = new byte[4096];
230
231 int bytesRead = 0;
232
233 while (-1 != (bytesRead = fileInputStream.read(buffer))) {
234
235 bos.write(buffer, 0, bytesRead);
236
237 }
238
239
240
241 result = bos.toByteArray();
242
243 } finally {
244
245 fileInputStream.close();
246
247 }
248
249 return result;
250
251 }
252
253
254
255 /*
256
257 * Print out any errors, if any, related to the deploy.
258
259 * @param result - DeployResult
260
261 */
262
263 private void printErrors(DeployResult result, String messageHeader) {
264
265 DeployDetails details = result.getDetails();
266
267 StringBuilder stringBuilder = new StringBuilder();
268
269 if (details != null) {
270
271 DeployMessage[] componentFailures = details.getComponentFailures();
272
273 for (DeployMessage failure : componentFailures) {
274
275 String loc = "(" + failure.getLineNumber() + ", " + failure.getColumnNumber();
276
277 if (loc.length() == 0 && !failure.getFileName().equals(failure.getFullName()))
278
279 {
280
281 loc = "(" + failure.getFullName() + ")";
282
283 }
284
285 stringBuilder.append(failure.getFileName() + loc + ":"
286
287 + failure.getProblem()).append('\n');
288
289 }
290
291 RunTestsResult rtr = details.getRunTestResult();
292
293 if (rtr.getFailures() != null) {
294
295 for (RunTestFailure failure : rtr.getFailures()) {
296
297 String n = (failure.getNamespace() == null ? "" :
298
299 (failure.getNamespace() + ".")) + failure.getName();
300
301 stringBuilder.append("Test failure, method: " + n + "." +
302
303 failure.getMethodName() + " -- " + failure.getMessage() +
304
305 " stack " + failure.getStackTrace() + "\n\n");
306
307 }
308
309 }
310
311 if (rtr.getCodeCoverageWarnings() != null) {
312
313 for (CodeCoverageWarning ccw : rtr.getCodeCoverageWarnings()) {
314
315 stringBuilder.append("Code coverage issue");
316
317 if (ccw.getName() != null) {
318
319 String n = (ccw.getNamespace() == null ? "" :
320
321 (ccw.getNamespace() + ".")) + ccw.getName();
322
323 stringBuilder.append(", class: " + n);
324
325 }
326
327 stringBuilder.append(" -- " + ccw.getMessage() + "\n");
328
329 }
330
331 }
332
333 }
334
335 if (stringBuilder.length() > 0) {
336
337 stringBuilder.insert(0, messageHeader);
338
339 System.out.println(stringBuilder.toString());
340
341 }
342
343 }
344
345
346
347
348
349 private void retrieveZip() throws Exception {
350
351 RetrieveRequest retrieveRequest = new RetrieveRequest();
352
353 // The version in package.xml overrides the version in RetrieveRequest
354
355 retrieveRequest.setApiVersion(API_VERSION);
356
357 setUnpackaged(retrieveRequest);
358
359
360
361 AsyncResult asyncResult = metadataConnection.retrieve(retrieveRequest);
362
363 RetrieveResult result = waitForRetrieveCompletion(asyncResult);
364
365
366
367 if (result.getStatus() == RetrieveStatus.Failed) {
368
369 throw new Exception(result.getErrorStatusCode() + " msg: " +
370
371 result.getErrorMessage());
372
373 } else if (result.getStatus() == RetrieveStatus.Succeeded) {
374
375 // Print out any warning messages
376
377 StringBuilder stringBuilder = new StringBuilder();
378
379 if (result.getMessages() != null) {
380
381 for (RetrieveMessage rm : result.getMessages()) {
382
383 stringBuilder.append(rm.getFileName() + " - " + rm.getProblem() + "\n");
384
385 }
386
387 }
388
389 if (stringBuilder.length() > 0) {
390
391 System.out.println("Retrieve warnings:\n" + stringBuilder);
392
393 }
394
395
396
397 System.out.println("Writing results to zip file");
398
399 File resultsFile = new File(ZIP_FILE);
400
401 FileOutputStream os = new FileOutputStream(resultsFile);
402
403
404
405 try {
406
407 os.write(result.getZipFile());
408
409 } finally {
410
411 os.close();
412
413 }
414
415 }
416
417 }
418
419
420
421 private DeployResult waitForDeployCompletion(String asyncResultId) throws Exception {
422
423 int poll = 0;
424
425 long waitTimeMilliSecs = ONE_SECOND;
426
427 DeployResult deployResult;
428
429 boolean fetchDetails;
430
431 do {
432
433 Thread.sleep(waitTimeMilliSecs);
434
435 // double the wait time for the next iteration
436
437
438
439 waitTimeMilliSecs *= 2;
440
441 if (poll++ > MAX_NUM_POLL_REQUESTS) {
442
443 throw new Exception(
444
445 "Request timed out. If this is a large set of metadata components, " +
446
447 "ensure that MAX_NUM_POLL_REQUESTS is sufficient.");
448
449 }
450
451 // Fetch in-progress details once for every 3 polls
452
453 fetchDetails = (poll % 3 == 0);
454
455
456
457 deployResult = metadataConnection.checkDeployStatus(asyncResultId, fetchDetails);
458
459 System.out.println("Status is: " + deployResult.getStatus());
460
461 if (!deployResult.isDone() && fetchDetails) {
462
463 printErrors(deployResult, "Failures for deployment in progress:\n");
464
465 }
466
467 }
468
469 while (!deployResult.isDone());
470
471
472
473 if (!deployResult.isSuccess() && deployResult.getErrorStatusCode() != null) {
474
475 throw new Exception(deployResult.getErrorStatusCode() + " msg: " +
476
477 deployResult.getErrorMessage());
478
479 }
480
481
482
483 if (!fetchDetails) {
484
485 // Get the final result with details if we didn't do it in the last attempt.
486
487 deployResult = metadataConnection.checkDeployStatus(asyncResultId, true);
488
489 }
490
491
492
493 return deployResult;
494
495 }
496
497
498
499 private RetrieveResult waitForRetrieveCompletion(AsyncResult asyncResult) throws Exception {
500
501 // Wait for the retrieve to complete
502
503 int poll = 0;
504
505 long waitTimeMilliSecs = ONE_SECOND;
506
507 String asyncResultId = asyncResult.getId();
508
509 RetrieveResult result = null;
510
511 do {
512
513 Thread.sleep(waitTimeMilliSecs);
514
515 // Double the wait time for the next iteration
516
517 waitTimeMilliSecs *= 2;
518
519 if (poll++ > MAX_NUM_POLL_REQUESTS) {
520
521 throw new Exception("Request timed out. If this is a large set " +
522
523 "of metadata components, check that the time allowed " +
524
525 "by MAX_NUM_POLL_REQUESTS is sufficient.");
526
527 }
528
529 result = metadataConnection.checkRetrieveStatus(
530
531 asyncResultId);
532
533 System.out.println("Retrieve Status: " + result.getStatus());
534
535 } while (!result.isDone());
536
537
538
539 return result;
540
541 }
542
543
544
545 private void setUnpackaged(RetrieveRequest request) throws Exception {
546
547 // Edit the path, if necessary, if your package.xml file is located elsewhere
548
549 File unpackedManifest = new File(MANIFEST_FILE);
550
551 System.out.println("Manifest file: " + unpackedManifest.getAbsolutePath());
552
553
554
555 if (!unpackedManifest.exists() || !unpackedManifest.isFile()) {
556
557 throw new Exception("Should provide a valid retrieve manifest " +
558
559 "for unpackaged content. Looking for " +
560
561 unpackedManifest.getAbsolutePath());
562
563 }
564
565
566
567 // Note that we use the fully quualified class name because
568
569 // of a collision with the java.lang.Package class
570
571 com.sforce.soap.metadata.Package p = parsePackageManifest(unpackedManifest);
572
573 request.setUnpackaged(p);
574
575 }
576
577
578
579 private com.sforce.soap.metadata.Package parsePackageManifest(File file)
580
581 throws ParserConfigurationException, IOException, SAXException {
582
583 com.sforce.soap.metadata.Package packageManifest = null;
584
585 List<PackageTypeMembers> listPackageTypes = new ArrayList<PackageTypeMembers>();
586
587 DocumentBuilder db =
588
589 DocumentBuilderFactory.newInstance().newDocumentBuilder();
590
591 InputStream inputStream = new FileInputStream(file);
592
593 Element d = db.parse(inputStream).getDocumentElement();
594
595 for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling()) {
596
597 if (c instanceof Element) {
598
599 Element ce = (Element) c;
600
601 NodeList nodeList = ce.getElementsByTagName("name");
602
603 if (nodeList.getLength() == 0) {
604
605 continue;
606
607 }
608
609 String name = nodeList.item(0).getTextContent();
610
611 NodeList m = ce.getElementsByTagName("members");
612
613 List<String> members = new ArrayList<String>();
614
615 for (int i = 0; i < m.getLength(); i++) {
616
617 Node mm = m.item(i);
618
619 members.add(mm.getTextContent());
620
621 }
622
623 PackageTypeMembers packageTypes = new PackageTypeMembers();
624
625 packageTypes.setName(name);
626
627 packageTypes.setMembers(members.toArray(new String[members.size()]));
628
629 listPackageTypes.add(packageTypes);
630
631 }
632
633 }
634
635 packageManifest = new com.sforce.soap.metadata.Package();
636
637 PackageTypeMembers[] packageTypesArray =
638
639 new PackageTypeMembers[listPackageTypes.size()];
640
641 packageManifest.setTypes(listPackageTypes.toArray(packageTypesArray));
642
643 packageManifest.setVersion(API_VERSION + "");
644
645 return packageManifest;
646
647 }
648
649}