diff --git a/pom.xml b/pom.xml
index 4caca2ef0..30c93637d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.force
dataloader
- 64.1.0
+ 65.0.0
jar
Salesforce Data Loader
https://github.com/forcedotcom/dataloader
diff --git a/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkApiVisitorUtil.java b/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkApiVisitorUtil.java
index e09743490..d94312364 100644
--- a/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkApiVisitorUtil.java
+++ b/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkApiVisitorUtil.java
@@ -354,7 +354,7 @@ private void updateJobStatus() {
// hack because jobInfo is not updated if an entire batch fails
private int getNumRecordsProcessedInJob() {
- int numRecordsProcessedInJob = this.jobInfo.getNumberRecordsProcessed();
+ int numRecordsProcessedInJob = (int) this.jobInfo.getNumberRecordsProcessed();
if (appConfig.isBulkAPIEnabled() || appConfig.isBulkV2APIEnabled()) {
// Bulk v2 counts all processed records in the total
numRecordsProcessedInJob -= this.jobInfo.getNumberRecordsFailed();
@@ -374,7 +374,7 @@ private int getNumRecordsProcessedInJob() {
// hack because jobInfo is not updated if an entire batch fails
private int getNumRecordsFailedInJob() {
- int numRecordsFailedInJob = this.jobInfo.getNumberRecordsFailed();
+ int numRecordsFailedInJob = (int) this.jobInfo.getNumberRecordsFailed();
int numRecordsPerBatch = 0;
try {
numRecordsPerBatch = this.appConfig.getInt(AppConfig.PROP_IMPORT_BATCH_SIZE);
diff --git a/src/main/java/com/salesforce/dataloader/client/ClientBase.java b/src/main/java/com/salesforce/dataloader/client/ClientBase.java
index 64ca387b5..acb15d62a 100644
--- a/src/main/java/com/salesforce/dataloader/client/ClientBase.java
+++ b/src/main/java/com/salesforce/dataloader/client/ClientBase.java
@@ -220,6 +220,8 @@ public ConnectorConfig getConnectorConfig() {
cc.setAuthEndpoint(server + PartnerClient.getServicePath());
cc.setServiceEndpoint(server + PartnerClient.getServicePath()); // Partner SOAP service
cc.setRestEndpoint(server + BulkV1Client.getServicePath()); // REST service: Bulk v1
+ logger.info("Post-login endpoint configured: SOAP=" + PartnerClient.getServicePath()
+ + ", REST=" + BulkV1Client.getServicePath());
}
return cc;
@@ -229,7 +231,7 @@ public static String getCurrentAPIVersionInWSC() {
String[] connectURLArray = Connector.END_POINT.split("\\/");
return connectURLArray[connectURLArray.length-1];
}
-
+
public static String getPreviousAPIVersionInWSC() {
String currentAPIVersion = getCurrentAPIVersionInWSC();
String[] versionStrArray = currentAPIVersion.split("\\.");
diff --git a/src/main/java/com/salesforce/dataloader/client/LoginClient.java b/src/main/java/com/salesforce/dataloader/client/LoginClient.java
index b95d697ca..44a2b1cb3 100644
--- a/src/main/java/com/salesforce/dataloader/client/LoginClient.java
+++ b/src/main/java/com/salesforce/dataloader/client/LoginClient.java
@@ -62,6 +62,11 @@ public class LoginClient extends ClientBase {
private static Logger LOG = DLLogManager.getLogger(LoginClient.class);
+ /** Minimum API version where SOAP login is no longer supported (per Salesforce). */
+ private static final double SOAP_LOGIN_REMOVED_IN_VERSION = 65.0;
+ /** API version to use for SOAP login when session version >= 65.0 (per product decision). */
+ private static final String DEFAULT_LOGIN_API_VERSION = "64.0";
+
private ConnectorConfig connectorConfig = null;
private LoginClient(Controller controller) {
@@ -133,7 +138,8 @@ private boolean login() throws ConnectionException, ApiFault {
String origEndpoint = new String(appConfig.getAuthEndpointForCurrentEnv());
try {
dologin();
- logger.debug("able to successfully invoke server APIs of version " + getAPIVersionForTheSession());
+ logger.info("Login succeeded using API " + getApiVersionForLogin(getAPIVersionForTheSession())
+ + ", session API version for operations: " + getAPIVersionForTheSession());
} catch (UnexpectedErrorFault fault) {
// attempt login with previous API version
if (fault.getExceptionCode() == ExceptionCode.UNSUPPORTED_API_VERSION
@@ -331,8 +337,30 @@ public ConnectorConfig getConnectorConfig() {
return cc;
}
+ /**
+ * API version to use for the SOAP login request only.
+ * SOAP login is not supported in API versions >= 65; for those, returns 64.0.
+ * Other operations continue to use the session version unchanged.
+ */
+ public static String getApiVersionForLogin(String currentSessionVersion) {
+ if (currentSessionVersion == null || currentSessionVersion.isEmpty()) {
+ return currentSessionVersion;
+ }
+ try {
+ double version = Double.parseDouble(currentSessionVersion);
+ return version >= SOAP_LOGIN_REMOVED_IN_VERSION ? DEFAULT_LOGIN_API_VERSION : currentSessionVersion;
+ } catch (NumberFormatException e) {
+ LOG.error("Invalid session API version for login: {}", currentSessionVersion, e);
+ throw new IllegalArgumentException("Invalid session API version: " + currentSessionVersion, e);
+ }
+ }
+
public static String getServicePath() {
- return "/services/Soap/u/" + getAPIVersionForTheSession() + "/";
+ return getServicePath(getAPIVersionForTheSession());
+ }
+
+ public static String getServicePath(String apiVersion) {
+ return "/services/Soap/u/" + apiVersion + "/";
}
public LimitInfo getAPILimitInfo() {
@@ -353,8 +381,9 @@ private synchronized ConnectorConfig getLoginConnectorConfig(String serverURL) {
serverURL = appConfig.getAuthEndpointForCurrentEnv();
}
this.connectorConfig = getConnectorConfig();
- this.connectorConfig.setAuthEndpoint(serverURL + getServicePath());
- this.connectorConfig.setServiceEndpoint(serverURL + getServicePath());
+ String loginServicePath = getServicePath(getApiVersionForLogin(getAPIVersionForTheSession()));
+ this.connectorConfig.setAuthEndpoint(serverURL + loginServicePath);
+ this.connectorConfig.setServiceEndpoint(serverURL + loginServicePath);
return this.connectorConfig;
}
diff --git a/src/main/java/com/salesforce/dataloader/controller/Controller.java b/src/main/java/com/salesforce/dataloader/controller/Controller.java
index 9eaac1689..0ada75ccd 100644
--- a/src/main/java/com/salesforce/dataloader/controller/Controller.java
+++ b/src/main/java/com/salesforce/dataloader/controller/Controller.java
@@ -539,11 +539,13 @@ else if (filePath.equals(daoName))
public void logout() {
getLoginClient().logout();
getPartnerClient().logout();
+ getSObjectMetaDataClient().logout();
this.bulkV1Client = null;
this.bulkV2Client = null;
this.restClient = null;
appConfig.setValue(AppConfig.PROP_OAUTH_ACCESSTOKEN, "");
+ appConfig.setValue(AppConfig.PROP_ENTITY, "");
}
public boolean attachmentsEnabled() {
diff --git a/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java b/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java
index c7e8c72c6..ae98ae171 100644
--- a/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java
+++ b/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java
@@ -119,6 +119,11 @@ public void refresh() {
public void dispose() {
// make sure configuration gets written
if(this.controller != null) {
+ // Clear metadata client cache and entity selection to prevent stale org data on restart
+ if (this.controller.isLoggedIn()) {
+ this.controller.getSObjectMetaDataClient().logout();
+ this.controller.getAppConfig().setValue(com.salesforce.dataloader.config.AppConfig.PROP_ENTITY, "");
+ }
controller.saveConfig();
}
}
diff --git a/src/test/java/com/salesforce/dataloader/TestBase.java b/src/test/java/com/salesforce/dataloader/TestBase.java
index 98c234d53..2965ed2a5 100644
--- a/src/test/java/com/salesforce/dataloader/TestBase.java
+++ b/src/test/java/com/salesforce/dataloader/TestBase.java
@@ -26,6 +26,7 @@
package com.salesforce.dataloader;
import com.salesforce.dataloader.client.BulkV1Client;
+import com.salesforce.dataloader.client.LoginClient;
import com.salesforce.dataloader.client.PartnerClient;
import com.salesforce.dataloader.config.AppConfig;
import com.salesforce.dataloader.config.Messages;
@@ -293,8 +294,10 @@ protected ConnectorConfig getWSCConfig(String apiVersionStr) {
if (!configEndpoint.equals("")) { //$NON-NLS-1$
try {
PartnerClient.setAPIVersionForTheSession(apiVersionStr);
- bindingConfig.setAuthEndpoint(configEndpoint + PartnerClient.getServicePath());
- bindingConfig.setServiceEndpoint(configEndpoint + PartnerClient.getServicePath()); // Partner SOAP service
+ String loginVersion = LoginClient.getApiVersionForLogin(apiVersionStr);
+ String loginServicePath = LoginClient.getServicePath(loginVersion);
+ bindingConfig.setAuthEndpoint(configEndpoint + loginServicePath);
+ bindingConfig.setServiceEndpoint(configEndpoint + loginServicePath);
bindingConfig.setRestEndpoint(configEndpoint + BulkV1Client.getServicePath()); // REST service: Bulk v1
bindingConfig.setManualLogin(true);
// set long timeout for tests with larger data sets
diff --git a/src/test/java/com/salesforce/dataloader/client/LoginClientTest.java b/src/test/java/com/salesforce/dataloader/client/LoginClientTest.java
new file mode 100644
index 000000000..58f02f158
--- /dev/null
+++ b/src/test/java/com/salesforce/dataloader/client/LoginClientTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015, salesforce.com, inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.salesforce.dataloader.client;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Unit tests for LoginClient API version logic.
+ * SOAP login is not supported in API versions >= 65; login falls back to 64.0.
+ */
+public class LoginClientTest {
+
+ @Test
+ public void getApiVersionForLogin_when65_returns64() {
+ assertEquals("64.0", LoginClient.getApiVersionForLogin("65.0"));
+ }
+
+ @Test
+ public void getApiVersionForLogin_when66_returns64() {
+ assertEquals("64.0", LoginClient.getApiVersionForLogin("66.0"));
+ }
+
+ @Test
+ public void getApiVersionForLogin_when70_returns64() {
+ assertEquals("64.0", LoginClient.getApiVersionForLogin("70.0"));
+ }
+
+ @Test
+ public void getApiVersionForLogin_when64_returns64() {
+ assertEquals("64.0", LoginClient.getApiVersionForLogin("64.0"));
+ }
+
+ @Test
+ public void getApiVersionForLogin_when63_returns63() {
+ assertEquals("63.0", LoginClient.getApiVersionForLogin("63.0"));
+ }
+
+ @Test
+ public void getApiVersionForLogin_when50_returns50() {
+ assertEquals("50.0", LoginClient.getApiVersionForLogin("50.0"));
+ }
+
+ @Test
+ public void getApiVersionForLogin_whenNull_returnsNull() {
+ assertNull(LoginClient.getApiVersionForLogin(null));
+ }
+
+ @Test
+ public void getApiVersionForLogin_whenEmpty_returnsEmpty() {
+ assertEquals("", LoginClient.getApiVersionForLogin(""));
+ }
+
+ @Test
+ public void getServicePath_usesProvidedVersion() {
+ assertEquals("/services/Soap/u/64.0/", LoginClient.getServicePath("64.0"));
+ assertEquals("/services/Soap/u/65.0/", LoginClient.getServicePath("65.0"));
+ }
+
+ @Test
+ public void getServicePath_withLoginVersion_uses64ForV65Session() {
+ String loginVersion = LoginClient.getApiVersionForLogin("65.0");
+ assertEquals("/services/Soap/u/64.0/", LoginClient.getServicePath(loginVersion));
+ }
+
+ @Test
+ public void getServicePath_withLoginVersion_passesThrough64() {
+ String loginVersion = LoginClient.getApiVersionForLogin("64.0");
+ assertEquals("/services/Soap/u/64.0/", LoginClient.getServicePath(loginVersion));
+ }
+
+ @Test
+ public void getServicePath_withLoginVersion_passesThrough63() {
+ String loginVersion = LoginClient.getApiVersionForLogin("63.0");
+ assertEquals("/services/Soap/u/63.0/", LoginClient.getServicePath(loginVersion));
+ }
+
+}
diff --git a/src/test/java/com/salesforce/dataloader/util/DateOnlyCalendarTest.java b/src/test/java/com/salesforce/dataloader/util/DateOnlyCalendarTest.java
index 84d11beee..36aaef376 100644
--- a/src/test/java/com/salesforce/dataloader/util/DateOnlyCalendarTest.java
+++ b/src/test/java/com/salesforce/dataloader/util/DateOnlyCalendarTest.java
@@ -39,6 +39,7 @@ public void setUp() {
dateOnlyCalendar = new DateOnlyCalendar();
}
+ @Ignore // This test assumes that the default timezone is PST, which is not true on all systems
@Test
public void testSetTimeInMillisWithDefaultTimeZone() {
long timeInMillis = 1633046400000L; // 2021-10-01 00:00:00 GMT