ステップ 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 ごとに変更を行わずにこのコードの一部を再利用できます。
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/38.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 のサンプルコード
サンプルコードは、ログインユーティリティを使用してログインします。次に、取得、リリース、および終了のメニューを表示します。
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>38.0</version>
16</Package>API コールに続く、エラー処理コードに注意してください。
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}