diff --git a/src/main/java/com/salesforce/dataloader/action/visitor/AbstractQueryVisitor.java b/src/main/java/com/salesforce/dataloader/action/visitor/AbstractQueryVisitor.java index ce5a4a3c..9a80410d 100644 --- a/src/main/java/com/salesforce/dataloader/action/visitor/AbstractQueryVisitor.java +++ b/src/main/java/com/salesforce/dataloader/action/visitor/AbstractQueryVisitor.java @@ -207,8 +207,7 @@ private String getBinaryContentForURL(String urlStr, String fieldName) { + refId; HttpTransportInterface transport = (HttpTransportInterface) controller.getClient().getConnectorConfig().createTransport(); - HttpURLConnection httpConnection = transport.openHttpGetConnection(urlStr, null); - InputStream is = transport.httpGet(httpConnection, urlStr); + InputStream is = transport.httpGet(urlStr); byte[] binaryResponse = is.readAllBytes(); return Base64.getEncoder().encodeToString(binaryResponse); } catch (Exception e) { diff --git a/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkV2Connection.java b/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkV2Connection.java index c7e6d658..a67784eb 100644 --- a/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkV2Connection.java +++ b/src/main/java/com/salesforce/dataloader/action/visitor/bulk/BulkV2Connection.java @@ -139,13 +139,14 @@ private static void writeTo(BufferedInputStream bis, BufferedOutputStream bos) t import java.io.OutputStream; import java.io.Serializable; import java.io.FileOutputStream; -import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Set; +import org.apache.http.Header; +import org.apache.http.HttpResponse; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -426,8 +427,7 @@ private JobInfo doSendJobRequestToServer(String urlString, } // make a get request try { - HttpURLConnection conn = transport.openHttpGetConnection(urlString, headers); - in = transport.httpGet(conn, urlString); + in = transport.httpGet(urlString); } catch (HttpClientTransportException ex) { parseAndThrowException(ex); } @@ -526,11 +526,19 @@ private InputStream doGetQueryResultStream(URL resultsURL, HashMap 0 && proxyPort > 0) { - logger.info(Messages.getFormattedString( - "Client.sforceLoginProxyDetail", new String[] { proxyHost, String.valueOf(proxyPort) })); //$NON-NLS-1$ - cc.setProxy(proxyHost, proxyPort); - - String proxyUsername = config.getString(Config.PROXY_USERNAME); - if (proxyUsername != null && proxyUsername.length() > 0) { - logger.info(Messages.getFormattedString("Client.sforceLoginProxyUser", proxyUsername)); //$NON-NLS-1$ - cc.setProxyUsername(proxyUsername); - - String proxyPassword = config.getString(Config.PROXY_PASSWORD); - if (proxyPassword != null && proxyPassword.length() > 0) { - logger.info(Messages.getString("Client.sforceLoginProxyPassword")); //$NON-NLS-1$ - cc.setProxyPassword(proxyPassword); - } else { - cc.setProxyPassword(""); - } - } - - String proxyNtlmDomain = config.getString(Config.PROXY_NTLM_DOMAIN); - if (proxyNtlmDomain != null && proxyNtlmDomain.length() > 0) { - logger.info(Messages.getFormattedString("Client.sforceLoginProxyNtlm", proxyNtlmDomain)); //$NON-NLS-1$ - cc.setNtlmDomain(proxyNtlmDomain); - } - } - - } catch (ParameterLoadException e) { - logger.error(e.getMessage()); - } - + AppUtil.setConnectorConfigProxySettings(config, cc); // Time out after 5 seconds for connection int connTimeoutSecs; try { @@ -210,7 +178,7 @@ public ConnectorConfig getConnectorConfig() { return cc; } - + public static String getCurrentAPIVersionInWSC() { String[] connectURLArray = Connector.END_POINT.split("\\/"); return connectURLArray[connectURLArray.length-1]; diff --git a/src/main/java/com/salesforce/dataloader/client/HttpClientTransport.java b/src/main/java/com/salesforce/dataloader/client/HttpClientTransport.java index 51b2d390..bb05843d 100644 --- a/src/main/java/com/salesforce/dataloader/client/HttpClientTransport.java +++ b/src/main/java/com/salesforce/dataloader/client/HttpClientTransport.java @@ -29,16 +29,9 @@ import java.net.*; import java.nio.charset.StandardCharsets; import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; - import org.apache.commons.io.IOUtils; import org.apache.http.*; import org.apache.http.auth.*; @@ -46,6 +39,7 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPost; @@ -62,15 +56,14 @@ import com.salesforce.dataloader.util.AppUtil; import com.sforce.async.AsyncApiException; import com.sforce.ws.ConnectorConfig; -import com.sforce.ws.MessageHandler; -import com.sforce.ws.MessageHandlerWithHeaders; + import com.sforce.ws.tools.VersionInfo; import com.sforce.ws.transport.*; -import com.sforce.ws.util.FileUtil; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.ProxyAuthenticationStrategy; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -84,6 +77,10 @@ */ public class HttpClientTransport implements HttpTransportInterface { + + private static final String AUTH_HEADER_VALUE_PREFIX = "Bearer "; + private static final String AUTH_HEADER = "Authorization"; + private static ConnectorConfig currentConfig = null; private boolean successful; private HttpRequestBase httpMethod; @@ -93,7 +90,8 @@ public class HttpClientTransport implements HttpTransportInterface { private static boolean reuseConnection = true; private static long serverInvocationCount = 0; private static Logger logger = LogManager.getLogger(HttpClientTransport.class); - + private HttpResponse httpResponse; + public HttpClientTransport() { } @@ -135,61 +133,25 @@ private boolean areEquivalentConfigs(ConnectorConfig config1, ConnectorConfig co } else if (config1 == null || config2 == null) { // one of the configs is null, other isn't. They can't be equal. return false; - } else if (config1.equals(config2)) { - return true; - } - - InetSocketAddress socketAddress1 = (InetSocketAddress)config1.getProxy().address(); - InetSocketAddress socketAddress2 = (InetSocketAddress)config2.getProxy().address(); - - if (socketAddress1 == null && socketAddress2 == null) { - return true; - } else if (socketAddress1 == null || socketAddress2 == null) { + } else if (!config1.equals(config2)) { return false; - } else { - String field1, field2; - field1 = config1.getProxyUsername() == null ? "" : config1.getProxyUsername(); - field2 = config2.getProxyUsername() == null ? "" : config2.getProxyUsername(); - if (field1.compareTo(field2) != 0) { - return false; - } - - field1 = config1.getProxyPassword() == null ? "" : config1.getProxyPassword(); - field2 = config2.getProxyPassword() == null ? "" : config2.getProxyPassword(); - if (field1.compareTo(field2) != 0) { - return false; - } - - field1 = config1.getNtlmDomain() == null ? "" : config1.getNtlmDomain(); - field2 = config2.getNtlmDomain() == null ? "" : config2.getNtlmDomain(); - if (field1.compareTo(field2) != 0) { - return false; - } - - field1 = socketAddress1.getHostName() == null ? "" : socketAddress1.getHostName(); - field2 = socketAddress2.getHostName() == null ? "" : socketAddress2.getHostName(); - if (field1.compareTo(field2) != 0) { - return false; - } - - int intField1 = socketAddress1.getPort(); - int intField2 = socketAddress2.getPort(); - if (intField1 != intField2) { - return false; - } } return true; } - private static synchronized void initializeHttpClient() throws UnknownHostException { - if (isReuseConnection() && currentHttpClient != null) { + private boolean isHttpClientInitialized = false; + + private synchronized void initializeHttpClient() throws UnknownHostException { + if (isHttpClientInitialized) { // already initialized. return; } closeConnections(); HttpClientBuilder httpClientBuilder = HttpClientBuilder.create().useSystemProperties(); - if (currentConfig.getProxy().address() != null) { + if (currentConfig != null + && currentConfig.getProxy() != null + && currentConfig.getProxy().address() != null) { String proxyUser = currentConfig.getProxyUsername() == null ? "" : currentConfig.getProxyUsername(); String proxyPassword = currentConfig.getProxyPassword() == null ? "" : currentConfig.getProxyPassword(); @@ -199,7 +161,6 @@ private static synchronized void initializeHttpClient() throws UnknownHostExcept CredentialsProvider credentialsprovider = new BasicCredentialsProvider(); AuthScope scope = new AuthScope(proxyAddress.getHostName(), proxyAddress.getPort(), null, null); - httpClientBuilder.setDefaultCredentialsProvider(credentialsprovider); Credentials credentials; if (AppUtil.getOSType() == AppUtil.OSType.WINDOWS) { @@ -208,31 +169,42 @@ private static synchronized void initializeHttpClient() throws UnknownHostExcept } else { credentials = new UsernamePasswordCredentials(proxyUser, proxyPassword); } + // based on answer at https://stackoverflow.com/questions/6962047/apache-httpclient-4-1-proxy-authentication credentialsprovider.setCredentials(scope, credentials); + httpClientBuilder.setDefaultCredentialsProvider(credentialsprovider); + httpClientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy()); currentHttpClient = httpClientBuilder.build(); if (AppUtil.getOSType() == AppUtil.OSType.WINDOWS) { - try (CloseableHttpResponse ignored = currentHttpClient.execute(new HttpHead("http://salesforce.com"))) { + /* + try (CloseableHttpResponse ignored = currentHttpClient.execute(new HttpHead(AppUtil.DATALOADER_DOWNLOAD_URL))) { + logger.debug("able to ping salesforce service"); } catch (Exception e) { logger.info("Unable to use NTCredentials for proxy. Switching to UsernamePasswordCredentials"); credentials = new UsernamePasswordCredentials(proxyUser, proxyPassword); credentialsprovider.setCredentials(scope, credentials); } + */ } } - currentHttpClient = httpClientBuilder.build(); + if (currentHttpClient == null) { + currentHttpClient = httpClientBuilder.build(); + } + isHttpClientInitialized = true; } @Override public synchronized InputStream getContent() throws IOException { serverInvocationCount++; initializeHttpClient(); + this.httpMethod.addHeader(AUTH_HEADER, AUTH_HEADER_VALUE_PREFIX + currentConfig.getSessionId()); + this.httpMethod.addHeader("User-Agent", VersionInfo.info()); if (this.httpMethod instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase)this.httpMethod).getEntity() == null) { - byte[] entityBytes = entityByteOut.toByteArray(); - HttpEntity entity = new ByteArrayEntity(entityBytes); - currentConfig.setUseChunkedPost(false); - ((HttpEntityEnclosingRequestBase)this.httpMethod).setEntity(entity); - } + byte[] entityBytes = entityByteOut.toByteArray(); + HttpEntity entity = new ByteArrayEntity(entityBytes); + currentConfig.setUseChunkedPost(false); + ((HttpEntityEnclosingRequestBase)this.httpMethod).setEntity(entity); + } InputStream input = new ByteArrayInputStream(new byte[1]); try { HttpClientContext context = HttpClientContext.create(); @@ -250,6 +222,7 @@ public synchronized InputStream getContent() throws IOException { try (CloseableHttpResponse response = currentHttpClient.execute(this.httpMethod, context)) { successful = true; + httpResponse = response; if (response.getStatusLine().getStatusCode() > 399) { successful = false; if (response.getStatusLine().getStatusCode() == 407) { @@ -259,14 +232,14 @@ public synchronized InputStream getContent() throws IOException { // copy input stream data into a new input stream because releasing the connection will close the input stream ByteArrayOutputStream bOut = new ByteArrayOutputStream(); if (response.getEntity() != null) { - try (InputStream inStream = response.getEntity().getContent()) { - IOUtils.copy(inStream, bOut); - input = new ByteArrayInputStream(bOut.toByteArray()); - if (response.containsHeader("Content-Encoding") && response.getHeaders("Content-Encoding")[0].getValue().equals("gzip")) { - input = new GZIPInputStream(input); + try (InputStream inStream = response.getEntity().getContent()) { + IOUtils.copy(inStream, bOut); + input = new ByteArrayInputStream(bOut.toByteArray()); + if (response.containsHeader("Content-Encoding") && response.getHeaders("Content-Encoding")[0].getValue().equals("gzip")) { + input = new GZIPInputStream(input); + } } } - } } } finally { if (isReuseConnection()) { @@ -276,6 +249,10 @@ public synchronized InputStream getContent() throws IOException { return input; } + public HttpResponse getHttpResponse() { + return this.httpResponse; + } + @Override public boolean isSuccessful() { return successful; @@ -288,61 +265,71 @@ public OutputStream connect(String endpoint, HashMap httpHeaders @Override public OutputStream connect(String endpoint, HashMap httpHeaders, boolean enableCompression) throws IOException { - return connect(endpoint, httpHeaders, enableCompression, SupportedHttpMethodType.POST); + return connect(endpoint, httpHeaders, enableCompression, SupportedHttpMethodType.POST); } - @Override - public OutputStream connect(String endpoint, HashMap httpHeaders, boolean enableCompression, - SupportedHttpMethodType httpMethod) throws IOException { - return doConnect(endpoint, httpHeaders, enableCompression, httpMethod, null, null); - } + @Override + public OutputStream connect(String endpoint, HashMap httpHeaders, boolean enableCompression, + SupportedHttpMethodType httpMethod) throws IOException { + return doConnect(endpoint, httpHeaders, enableCompression, httpMethod, null, null); + } - @Override - public void connect(String endpoint, HashMap httpHeaders, boolean enableCompression, - SupportedHttpMethodType httpMethod, InputStream contentInputStream, String contentEncoding) - throws IOException { - doConnect(endpoint, httpHeaders, enableCompression, httpMethod, contentInputStream, contentEncoding); - } + @Override + public void connect(String endpoint, HashMap httpHeaders, boolean enableCompression, + SupportedHttpMethodType httpMethod, InputStream contentInputStream, String contentEncoding) + throws IOException { + doConnect(endpoint, httpHeaders, enableCompression, httpMethod, contentInputStream, contentEncoding); + } - public static long getServerInvocationCount() { - return serverInvocationCount; - } - - public static void resetServerInvocationCount() { - serverInvocationCount = 0; - } - - private OutputStream doConnect(String endpoint, HashMap httpHeaders, boolean enableCompression, SupportedHttpMethodType httpMethodType, InputStream requestInputStream, String contentTypeStr) throws IOException { - switch (httpMethodType) { - case PATCH : - this.httpMethod = new HttpPatch(endpoint); - break; - case PUT : - this.httpMethod = new HttpPut(endpoint); - break; - case DELETE : - this.httpMethod = new HttpDelete(endpoint); - break; - default: - this.httpMethod = new HttpPost(endpoint); - } - for (String name : httpHeaders.keySet()) { - this.httpMethod.addHeader(name, httpHeaders.get(name)); + public static long getServerInvocationCount() { + return serverInvocationCount; + } + + public static void resetServerInvocationCount() { + serverInvocationCount = 0; + } + + private OutputStream doConnect(String endpoint, + HashMap httpHeaders, + boolean enableCompression, + SupportedHttpMethodType httpMethodType, + InputStream requestInputStream, + String contentTypeStr) throws IOException { + switch (httpMethodType) { + case GET : + this.httpMethod = new HttpGet(endpoint); + break; + case PATCH : + this.httpMethod = new HttpPatch(endpoint); + break; + case PUT : + this.httpMethod = new HttpPut(endpoint); + break; + case DELETE : + this.httpMethod = new HttpDelete(endpoint); + break; + default: + this.httpMethod = new HttpPost(endpoint); } - + if (httpHeaders != null) { + for (String name : httpHeaders.keySet()) { + this.httpMethod.addHeader(name, httpHeaders.get(name)); + } + } + this.httpMethod.addHeader(AUTH_HEADER, AUTH_HEADER_VALUE_PREFIX + currentConfig.getSessionId()); this.httpMethod.addHeader("User-Agent", VersionInfo.info()); if (requestInputStream != null) { - ContentType contentType = ContentType.DEFAULT_TEXT; - if (contentTypeStr != null) { - contentType = ContentType.create(contentTypeStr); - } - BufferedHttpEntity entity = new BufferedHttpEntity(new InputStreamEntity(requestInputStream, contentType)); - currentConfig.setUseChunkedPost(true); + ContentType contentType = ContentType.DEFAULT_TEXT; + if (contentTypeStr != null) { + contentType = ContentType.create(contentTypeStr); + } + BufferedHttpEntity entity = new BufferedHttpEntity(new InputStreamEntity(requestInputStream, contentType)); + currentConfig.setUseChunkedPost(true); if (this.httpMethod instanceof HttpEntityEnclosingRequestBase) { ((HttpEntityEnclosingRequestBase)this.httpMethod).setEntity(entity); } - return null; + return null; } if (enableCompression && currentConfig.isCompression()) { @@ -385,93 +372,18 @@ public static void closeConnections() { } public static void setReuseConnection(boolean reuse) { - reuseConnection = reuse; + reuseConnection = reuse; } public static boolean isReuseConnection() { - return reuseConnection; + return reuseConnection; } - private static final String AUTH_HEADER_VALUE_PREFIX = "Bearer "; - private static final String AUTH_HEADER = "Authorization"; - - public HttpURLConnection openHttpGetConnection(String urlStr, Map headers) throws IOException { - URL url = new URL(urlStr); - HttpURLConnection connection = currentConfig.createConnection(url, null); - SSLContext sslContext = currentConfig.getSslContext(); - if (sslContext != null && connection instanceof HttpsURLConnection) { - ((HttpsURLConnection)connection).setSSLSocketFactory(sslContext.getSocketFactory()); - } - if (headers != null && !headers.isEmpty()) { - Set headerNameSet = headers.keySet(); - for (String headerName : headerNameSet) { - connection.setRequestProperty(headerName, headers.get(headerName)); - } - } - String authHeaderValue = AUTH_HEADER_VALUE_PREFIX + currentConfig.getSessionId(); - connection.setRequestProperty(AUTH_HEADER, authHeaderValue); - return connection; - } - - public InputStream httpGet(HttpURLConnection connection, String urlStr) throws IOException, AsyncApiException, HttpClientTransportException { - boolean success = true; - InputStream in; - URL url = new URL(urlStr); - try { - in = connection.getInputStream(); - } catch (IOException e) { - success = false; - in = connection.getErrorStream(); - } - - String encoding = connection.getHeaderField("Content-Encoding"); - if ("gzip".equals(encoding)) { - in = new GZIPInputStream(in); - } - - if (currentConfig.isTraceMessage() || currentConfig.hasMessageHandlers()) { - byte[] bytes = FileUtil.toBytes(in); - in = new ByteArrayInputStream(bytes); - - if (currentConfig.hasMessageHandlers()) { - Iterator it = currentConfig.getMessagerHandlers(); - while (it.hasNext()) { - MessageHandler handler = it.next(); - if (handler instanceof MessageHandlerWithHeaders) { - ((MessageHandlerWithHeaders)handler).handleRequest(url, new byte[0], null); - ((MessageHandlerWithHeaders)handler).handleResponse(url, bytes, connection.getHeaderFields()); - } else { - handler.handleRequest(url, new byte[0]); - handler.handleResponse(url, bytes); - } - } - } - - if (currentConfig.isTraceMessage()) { - currentConfig.getTraceStream().println(url.toExternalForm()); - - Map> headers = connection.getHeaderFields(); - for (Map.Entry>entry : headers.entrySet()) { - StringBuffer sb = new StringBuffer(); - List values = entry.getValue(); - - if (values != null) { - for (String v : values) { - sb.append(v); - } - } - - currentConfig.getTraceStream().println(entry.getKey() + ": " + sb.toString()); - } - - currentConfig.teeInputStream(bytes); - } - } - - if (!success) { - HttpClientTransportException ex = new HttpClientTransportException("Unsuccessful GET operation", connection, in); - throw ex; - } + public InputStream httpGet(String urlStr) throws IOException, AsyncApiException, HttpClientTransportException { + InputStream in = null; + initializeHttpClient(); + connect(urlStr, null, false, SupportedHttpMethodType.GET, null, null); + in = getContent(); return in; } } \ No newline at end of file diff --git a/src/main/java/com/salesforce/dataloader/client/HttpClientTransportv1.java b/src/main/java/com/salesforce/dataloader/client/HttpClientTransportv1.java new file mode 100644 index 00000000..677e66d9 --- /dev/null +++ b/src/main/java/com/salesforce/dataloader/client/HttpClientTransportv1.java @@ -0,0 +1,477 @@ +/* + * 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 java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; + +import org.apache.commons.io.IOUtils; +import org.apache.http.*; +import org.apache.http.auth.*; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.InputStreamEntity; + +import com.salesforce.dataloader.exception.HttpClientTransportException; +import com.salesforce.dataloader.util.AppUtil; +import com.sforce.async.AsyncApiException; +import com.sforce.ws.ConnectorConfig; +import com.sforce.ws.MessageHandler; +import com.sforce.ws.MessageHandlerWithHeaders; +import com.sforce.ws.tools.VersionInfo; +import com.sforce.ws.transport.*; +import com.sforce.ws.util.FileUtil; + +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * This class implements the Transport interface for WSC with HttpClient in order to properly work + * with NTLM proxies. The existing JdkHttpTransport in WSC does not work with NTLM proxies when + * compiled on Java 1.6 + * + * @author Jeff Lai + * @since 25.0.2 + */ +public class HttpClientTransportv1 implements HttpTransportInterfacev1 { + + private static ConnectorConfig currentConfig = null; + private boolean successful; + private HttpRequestBase httpMethod; + private OutputStream output; + private ByteArrayOutputStream entityByteOut; + private static CloseableHttpClient currentHttpClient = null; + private static boolean reuseConnection = true; + private static long serverInvocationCount = 0; + private static Logger logger = LogManager.getLogger(HttpClientTransport.class); + + public HttpClientTransportv1() { + } + + public HttpClientTransportv1(ConnectorConfig newConfig) { + setConfig(newConfig); + } + + @Override + public synchronized void setConfig(ConnectorConfig newConfig) { + if (!areEquivalentConfigs(currentConfig, newConfig) && currentHttpClient != null) { + try { + currentHttpClient.close(); + } catch (IOException ex) { + // do nothing + } + currentHttpClient = null; + } + currentConfig = newConfig; + } + + @Override + public OutputStream connect(String url, String soapAction) throws IOException { + if (soapAction == null) { + soapAction = ""; + } + + HashMap header = new HashMap(); + + header.put("SOAPAction", "\"" + soapAction + "\""); + header.put("Content-Type", "text/xml; charset=" + StandardCharsets.UTF_8.name()); + header.put("Accept", "text/xml"); + + return connect(url, header); + } + + private boolean areEquivalentConfigs(ConnectorConfig config1, ConnectorConfig config2) { + if (config1 == null && config2 == null) { + return true; + } else if (config1 == null || config2 == null) { + // one of the configs is null, other isn't. They can't be equal. + return false; + } else if (config1.equals(config2)) { + return true; + } + + InetSocketAddress socketAddress1 = (InetSocketAddress)config1.getProxy().address(); + InetSocketAddress socketAddress2 = (InetSocketAddress)config2.getProxy().address(); + + if (socketAddress1 == null && socketAddress2 == null) { + return true; + } else if (socketAddress1 == null || socketAddress2 == null) { + return false; + } else { + String field1, field2; + field1 = config1.getProxyUsername() == null ? "" : config1.getProxyUsername(); + field2 = config2.getProxyUsername() == null ? "" : config2.getProxyUsername(); + if (field1.compareTo(field2) != 0) { + return false; + } + + field1 = config1.getProxyPassword() == null ? "" : config1.getProxyPassword(); + field2 = config2.getProxyPassword() == null ? "" : config2.getProxyPassword(); + if (field1.compareTo(field2) != 0) { + return false; + } + + field1 = config1.getNtlmDomain() == null ? "" : config1.getNtlmDomain(); + field2 = config2.getNtlmDomain() == null ? "" : config2.getNtlmDomain(); + if (field1.compareTo(field2) != 0) { + return false; + } + + field1 = socketAddress1.getHostName() == null ? "" : socketAddress1.getHostName(); + field2 = socketAddress2.getHostName() == null ? "" : socketAddress2.getHostName(); + if (field1.compareTo(field2) != 0) { + return false; + } + + int intField1 = socketAddress1.getPort(); + int intField2 = socketAddress2.getPort(); + if (intField1 != intField2) { + return false; + } + } + return true; + } + + private static synchronized void initializeHttpClient() throws UnknownHostException { + if (isReuseConnection() && currentHttpClient != null) { + // already initialized. + return; + } + closeConnections(); + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create().useSystemProperties(); + + if (currentConfig.getProxy().address() != null) { + String proxyUser = currentConfig.getProxyUsername() == null ? "" : currentConfig.getProxyUsername(); + String proxyPassword = currentConfig.getProxyPassword() == null ? "" : currentConfig.getProxyPassword(); + + InetSocketAddress proxyAddress = (InetSocketAddress) currentConfig.getProxy().address(); + HttpHost proxyHost = new HttpHost(proxyAddress.getHostName(), proxyAddress.getPort(), "http"); + httpClientBuilder.setProxy(proxyHost); + + CredentialsProvider credentialsprovider = new BasicCredentialsProvider(); + AuthScope scope = new AuthScope(proxyAddress.getHostName(), proxyAddress.getPort(), null, null); + httpClientBuilder.setDefaultCredentialsProvider(credentialsprovider); + + Credentials credentials; + if (AppUtil.getOSType() == AppUtil.OSType.WINDOWS) { + String computerName = InetAddress.getLocalHost().getCanonicalHostName(); + credentials = new NTCredentials(proxyUser, proxyPassword, computerName, currentConfig.getNtlmDomain()); + } else { + credentials = new UsernamePasswordCredentials(proxyUser, proxyPassword); + } + credentialsprovider.setCredentials(scope, credentials); + currentHttpClient = httpClientBuilder.build(); + if (AppUtil.getOSType() == AppUtil.OSType.WINDOWS) { + try (CloseableHttpResponse ignored = currentHttpClient.execute(new HttpHead("http://salesforce.com"))) { + } catch (Exception e) { + logger.info("Unable to use NTCredentials for proxy. Switching to UsernamePasswordCredentials"); + credentials = new UsernamePasswordCredentials(proxyUser, proxyPassword); + credentialsprovider.setCredentials(scope, credentials); + } + } + } + currentHttpClient = httpClientBuilder.build(); + } + + @Override + public synchronized InputStream getContent() throws IOException { + serverInvocationCount++; + initializeHttpClient(); + if (this.httpMethod instanceof HttpEntityEnclosingRequestBase + && ((HttpEntityEnclosingRequestBase)this.httpMethod).getEntity() == null) { + byte[] entityBytes = entityByteOut.toByteArray(); + HttpEntity entity = new ByteArrayEntity(entityBytes); + currentConfig.setUseChunkedPost(false); + ((HttpEntityEnclosingRequestBase)this.httpMethod).setEntity(entity); + } + InputStream input = new ByteArrayInputStream(new byte[1]); + try { + HttpClientContext context = HttpClientContext.create(); + RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(currentConfig.useChunkedPost()).build(); + context.setRequestConfig(config); + + if (currentConfig.getNtlmDomain() != null && !currentConfig.getNtlmDomain().equals("")) { + // need to send a HEAD request to trigger NTLM authentication + try (CloseableHttpResponse ignored = currentHttpClient.execute(new HttpHead("http://salesforce.com"))) { + } catch (Exception ex) { + logger.error(ex.getMessage()); + throw ex; + } + } + + try (CloseableHttpResponse response = currentHttpClient.execute(this.httpMethod, context)) { + successful = true; + if (response.getStatusLine().getStatusCode() > 399) { + successful = false; + if (response.getStatusLine().getStatusCode() == 407) { + throw new RuntimeException(response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase()); + } + } + // copy input stream data into a new input stream because releasing the connection will close the input stream + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + if (response.getEntity() != null) { + try (InputStream inStream = response.getEntity().getContent()) { + IOUtils.copy(inStream, bOut); + input = new ByteArrayInputStream(bOut.toByteArray()); + if (response.containsHeader("Content-Encoding") && response.getHeaders("Content-Encoding")[0].getValue().equals("gzip")) { + input = new GZIPInputStream(input); + } + } + } + } + } finally { + if (isReuseConnection()) { + closeConnections(); + } + } + return input; + } + + @Override + public boolean isSuccessful() { + return successful; + } + + @Override + public OutputStream connect(String endpoint, HashMap httpHeaders) throws IOException { + return connect(endpoint, httpHeaders, true); + } + + @Override + public OutputStream connect(String endpoint, HashMap httpHeaders, boolean enableCompression) throws IOException { + return connect(endpoint, httpHeaders, enableCompression, SupportedHttpMethodType.POST); + } + + @Override + public OutputStream connect(String endpoint, HashMap httpHeaders, boolean enableCompression, + SupportedHttpMethodType httpMethod) throws IOException { + return doConnect(endpoint, httpHeaders, enableCompression, httpMethod, null, null); + } + + @Override + public void connect(String endpoint, HashMap httpHeaders, boolean enableCompression, + SupportedHttpMethodType httpMethod, InputStream contentInputStream, String contentEncoding) + throws IOException { + doConnect(endpoint, httpHeaders, enableCompression, httpMethod, contentInputStream, contentEncoding); + } + + public static long getServerInvocationCount() { + return serverInvocationCount; + } + + public static void resetServerInvocationCount() { + serverInvocationCount = 0; + } + + private OutputStream doConnect(String endpoint, HashMap httpHeaders, boolean enableCompression, SupportedHttpMethodType httpMethodType, InputStream requestInputStream, String contentTypeStr) throws IOException { + switch (httpMethodType) { + case PATCH : + this.httpMethod = new HttpPatch(endpoint); + break; + case PUT : + this.httpMethod = new HttpPut(endpoint); + break; + case DELETE : + this.httpMethod = new HttpDelete(endpoint); + break; + default: + this.httpMethod = new HttpPost(endpoint); + } + for (String name : httpHeaders.keySet()) { + this.httpMethod.addHeader(name, httpHeaders.get(name)); + } + + this.httpMethod.addHeader("User-Agent", VersionInfo.info()); + + if (requestInputStream != null) { + ContentType contentType = ContentType.DEFAULT_TEXT; + if (contentTypeStr != null) { + contentType = ContentType.create(contentTypeStr); + } + BufferedHttpEntity entity = new BufferedHttpEntity(new InputStreamEntity(requestInputStream, contentType)); + currentConfig.setUseChunkedPost(true); + if (this.httpMethod instanceof HttpEntityEnclosingRequestBase) { + ((HttpEntityEnclosingRequestBase)this.httpMethod).setEntity(entity); + } + return null; + } + + if (enableCompression && currentConfig.isCompression()) { + this.httpMethod.addHeader("Content-Encoding", "gzip"); + this.httpMethod.addHeader("Accept-Encoding", "gzip"); + } + + entityByteOut = new ByteArrayOutputStream(); + output = entityByteOut; + + if (currentConfig.getMaxRequestSize() > 0) { + output = new LimitingOutputStream(currentConfig.getMaxRequestSize(), output); + } + + if (enableCompression && currentConfig.isCompression()) { + output = new GZIPOutputStream(output); + } + + if (currentConfig.isTraceMessage()) { + output = currentConfig.teeOutputStream(output); + } + + if (currentConfig.hasMessageHandlers()) { + URL url = new URL(endpoint); + output = new MessageHandlerOutputStream(currentConfig, url, output); + } + + return output; + } + + public static void closeConnections() { + if (currentHttpClient != null) { + try { + currentHttpClient.close(); + } catch (IOException ex) { + // do nothing + } + currentHttpClient = null; + } + } + + public static void setReuseConnection(boolean reuse) { + reuseConnection = reuse; + } + + public static boolean isReuseConnection() { + return reuseConnection; + } + + private static final String AUTH_HEADER_VALUE_PREFIX = "Bearer "; + private static final String AUTH_HEADER = "Authorization"; + + public HttpURLConnection openHttpGetConnection(String urlStr, Map headers) throws IOException { + URL url = new URL(urlStr); + HttpURLConnection connection = currentConfig.createConnection(url, null); + SSLContext sslContext = currentConfig.getSslContext(); + if (sslContext != null && connection instanceof HttpsURLConnection) { + ((HttpsURLConnection)connection).setSSLSocketFactory(sslContext.getSocketFactory()); + } + if (headers != null && !headers.isEmpty()) { + Set headerNameSet = headers.keySet(); + for (String headerName : headerNameSet) { + connection.setRequestProperty(headerName, headers.get(headerName)); + } + } + String authHeaderValue = AUTH_HEADER_VALUE_PREFIX + currentConfig.getSessionId(); + connection.setRequestProperty(AUTH_HEADER, authHeaderValue); + return connection; + } + + public InputStream httpGet(HttpURLConnection connection, String urlStr) throws IOException, AsyncApiException, HttpClientTransportException { + boolean success = true; + InputStream in; + URL url = new URL(urlStr); + try { + in = connection.getInputStream(); + } catch (IOException e) { + success = false; + in = connection.getErrorStream(); + } + + String encoding = connection.getHeaderField("Content-Encoding"); + if ("gzip".equals(encoding)) { + in = new GZIPInputStream(in); + } + + if (currentConfig.isTraceMessage() || currentConfig.hasMessageHandlers()) { + byte[] bytes = FileUtil.toBytes(in); + in = new ByteArrayInputStream(bytes); + + if (currentConfig.hasMessageHandlers()) { + Iterator it = currentConfig.getMessagerHandlers(); + while (it.hasNext()) { + MessageHandler handler = it.next(); + if (handler instanceof MessageHandlerWithHeaders) { + ((MessageHandlerWithHeaders)handler).handleRequest(url, new byte[0], null); + ((MessageHandlerWithHeaders)handler).handleResponse(url, bytes, connection.getHeaderFields()); + } else { + handler.handleRequest(url, new byte[0]); + handler.handleResponse(url, bytes); + } + } + } + + if (currentConfig.isTraceMessage()) { + currentConfig.getTraceStream().println(url.toExternalForm()); + + Map> headers = connection.getHeaderFields(); + for (Map.Entry>entry : headers.entrySet()) { + StringBuffer sb = new StringBuffer(); + List values = entry.getValue(); + + if (values != null) { + for (String v : values) { + sb.append(v); + } + } + + currentConfig.getTraceStream().println(entry.getKey() + ": " + sb.toString()); + } + + currentConfig.teeInputStream(bytes); + } + } + + if (!success) { + HttpClientTransportException ex = new HttpClientTransportException("Unsuccessful GET operation", connection, in); + throw ex; + } + return in; + } +} \ No newline at end of file diff --git a/src/main/java/com/salesforce/dataloader/client/HttpTransportInterface.java b/src/main/java/com/salesforce/dataloader/client/HttpTransportInterface.java index 556685aa..8ed9eb78 100644 --- a/src/main/java/com/salesforce/dataloader/client/HttpTransportInterface.java +++ b/src/main/java/com/salesforce/dataloader/client/HttpTransportInterface.java @@ -27,13 +27,11 @@ package com.salesforce.dataloader.client; import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; import java.io.InputStream; import java.io.IOException; import java.util.HashMap; -import java.util.Map; + +import org.apache.http.HttpResponse; import com.salesforce.dataloader.exception.HttpClientTransportException; import com.sforce.async.AsyncApiException; @@ -51,7 +49,8 @@ enum SupportedHttpMethodType { PUT, POST, PATCH, - DELETE + DELETE, + GET } OutputStream connect(String endpoint, HashMap httpHeaders, boolean enableCompression, HttpTransportInterface.SupportedHttpMethodType httpMethod) throws IOException; @@ -59,6 +58,6 @@ OutputStream connect(String endpoint, HashMap httpHeaders, boole void connect(String endpoint, HashMap httpHeaders, boolean enableCompression, HttpTransportInterface.SupportedHttpMethodType httpMethod, InputStream contentInputStream, String contentEncoding) throws IOException; - HttpURLConnection openHttpGetConnection(String urlStr, Map headers) throws IOException; - InputStream httpGet(HttpURLConnection connection, String urlStr) throws IOException, AsyncApiException, HttpClientTransportException; + InputStream httpGet(String urlStr) throws IOException, AsyncApiException, HttpClientTransportException; + HttpResponse getHttpResponse(); } diff --git a/src/main/java/com/salesforce/dataloader/client/HttpTransportInterfacev1.java b/src/main/java/com/salesforce/dataloader/client/HttpTransportInterfacev1.java new file mode 100644 index 00000000..ce2c20ee --- /dev/null +++ b/src/main/java/com/salesforce/dataloader/client/HttpTransportInterfacev1.java @@ -0,0 +1,62 @@ +/* + * 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 java.io.OutputStream; +import java.net.HttpURLConnection; +import java.io.InputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.salesforce.dataloader.exception.HttpClientTransportException; +import com.sforce.async.AsyncApiException; +import com.sforce.ws.transport.Transport; + +/** + * This interface defines a Transport. + * + * @author http://cheenath.com + * @version 1.0 + * @since 1.0 Nov 30, 2005 + */ +public interface HttpTransportInterfacev1 extends Transport { + enum SupportedHttpMethodType { + PUT, + POST, + PATCH, + DELETE + } + OutputStream connect(String endpoint, HashMap httpHeaders, boolean enableCompression, + HttpTransportInterfacev1.SupportedHttpMethodType httpMethod) throws IOException; + + void connect(String endpoint, HashMap httpHeaders, boolean enableCompression, + HttpTransportInterfacev1.SupportedHttpMethodType httpMethod, InputStream contentInputStream, String contentEncoding) throws IOException; + + HttpURLConnection openHttpGetConnection(String urlStr, Map headers) throws IOException; + InputStream httpGet(HttpURLConnection connection, String urlStr) throws IOException, AsyncApiException, HttpClientTransportException; +} diff --git a/src/main/java/com/salesforce/dataloader/controller/Controller.java b/src/main/java/com/salesforce/dataloader/controller/Controller.java index f348fb24..29e1aafe 100644 --- a/src/main/java/com/salesforce/dataloader/controller/Controller.java +++ b/src/main/java/com/salesforce/dataloader/controller/Controller.java @@ -63,6 +63,7 @@ import org.apache.logging.log4j.LogManager; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; @@ -73,6 +74,8 @@ import java.util.Date; import java.util.Map; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * The class that controls dataloader engine (config, salesforce communication, mapping, dao). For @@ -135,6 +138,36 @@ private Controller(Map argsMap) throws ControllerInitializationE daoFactory = new DataAccessObjectFactory(); } HttpClientTransport.setReuseConnection(config.getBoolean(Config.REUSE_CLIENT_CONNECTION)); + getLatestDownloadableDataLoaderVersion(); + } + + private static String latestDownloadableDataLoaderVersion = null; + private static final String DL_DOWNLOADABLE_REGEX = "[0-9]+\\.[0-9]+\\.[0-9]+\\.zip"; + public synchronized String getLatestDownloadableDataLoaderVersion() { + if (latestDownloadableDataLoaderVersion != null) { + return latestDownloadableDataLoaderVersion; + } + try { + ConnectorConfig connConfig = new ConnectorConfig(); + AppUtil.setConnectorConfigProxySettings(config, connConfig); + HttpClientTransport clientTransport = new HttpClientTransport(connConfig); + InputStream inputStream = clientTransport.httpGet(AppUtil.DATALOADER_DOWNLOAD_URL); + + String responseContent = new String(inputStream.readAllBytes()); + Pattern htmlTagInRichTextPattern = Pattern.compile(DL_DOWNLOADABLE_REGEX); + Matcher matcher = htmlTagInRichTextPattern.matcher(responseContent); + String downloadableVersion = AppUtil.DATALOADER_VERSION; + if (matcher.find()) { + downloadableVersion = matcher.group(); + downloadableVersion = downloadableVersion.substring(0, downloadableVersion.lastIndexOf(".")); + } + latestDownloadableDataLoaderVersion = downloadableVersion; + return downloadableVersion; + } catch (Exception e) { + logger.info("Unable to check for the latest available data loader version: " + e.getMessage()); + latestDownloadableDataLoaderVersion = AppUtil.DATALOADER_VERSION; + return AppUtil.DATALOADER_VERSION; + } } public synchronized void executeAction(ILoaderProgress monitor) throws DataAccessObjectException, OperationException { diff --git a/src/main/java/com/salesforce/dataloader/ui/LoaderUpgradeDialog.java b/src/main/java/com/salesforce/dataloader/ui/LoaderUpgradeDialog.java index 85728fce..d2347b60 100644 --- a/src/main/java/com/salesforce/dataloader/ui/LoaderUpgradeDialog.java +++ b/src/main/java/com/salesforce/dataloader/ui/LoaderUpgradeDialog.java @@ -31,6 +31,7 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; +import com.salesforce.dataloader.controller.Controller; import com.salesforce.dataloader.util.AppUtil; /** @@ -39,15 +40,16 @@ */ public class LoaderUpgradeDialog extends LoaderTitleAreaDialog { - + private Controller controller; /** * MyTitleAreaDialog constructor * * @param shell * the parent shell */ - public LoaderUpgradeDialog(Shell activeShell) { + public LoaderUpgradeDialog(Shell activeShell, Controller controller) { super(activeShell); + this.controller = controller; } /** @@ -66,7 +68,7 @@ protected Control createContents(Composite parent) { // Set the message setMessage(Labels.getFormattedString("LoaderDownloadDialog.messageLineOne", - new String[] {AppUtil.getLatestDownloadableDataLoaderVersion(), + new String[] {controller.getLatestDownloadableDataLoaderVersion(), AppUtil.DATALOADER_DOWNLOAD_URL})); //$NON-NLS-1$ // Set the image diff --git a/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java b/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java index f10f2529..cc6052d6 100644 --- a/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java +++ b/src/main/java/com/salesforce/dataloader/ui/LoaderWindow.java @@ -278,13 +278,13 @@ public void run() { } private void displayUpgradeDialog(final Display display) { - if (AppUtil.DATALOADER_VERSION.equals(AppUtil.getLatestDownloadableDataLoaderVersion())) { + if (AppUtil.DATALOADER_VERSION.equals(controller.getLatestDownloadableDataLoaderVersion())) { return; // running app's version matches with downloadable version. } display.asyncExec(new Thread() { @Override public void run() { - LoaderUpgradeDialog dlg = new LoaderUpgradeDialog(display.getActiveShell()); + LoaderUpgradeDialog dlg = new LoaderUpgradeDialog(display.getActiveShell(), controller); dlg.open(); addMenuBar(); } diff --git a/src/main/java/com/salesforce/dataloader/ui/uiActions/HelpUIAction.java b/src/main/java/com/salesforce/dataloader/ui/uiActions/HelpUIAction.java index 721d9dc6..9f649ab7 100644 --- a/src/main/java/com/salesforce/dataloader/ui/uiActions/HelpUIAction.java +++ b/src/main/java/com/salesforce/dataloader/ui/uiActions/HelpUIAction.java @@ -64,10 +64,10 @@ public void run() { private String getLoaderUpgradeMessage() { String upgradeMsg = Labels.getString("HelpUIAction.latestVersion"); - if (!AppUtil.DATALOADER_VERSION.equals(AppUtil.getLatestDownloadableDataLoaderVersion())) { + if (!AppUtil.DATALOADER_VERSION.equals(controller.getLatestDownloadableDataLoaderVersion())) { upgradeMsg = Labels.getFormattedString("LoaderDownloadDialog.messageLineOne", - new String[] {AppUtil.getLatestDownloadableDataLoaderVersion(), + new String[] {controller.getLatestDownloadableDataLoaderVersion(), AppUtil.DATALOADER_DOWNLOAD_URL}); } upgradeMsg += System.getProperty("line.separator") + System.getProperty("line.separator"); diff --git a/src/main/java/com/salesforce/dataloader/util/AppUtil.java b/src/main/java/com/salesforce/dataloader/util/AppUtil.java index 1d324ada..71a2b368 100644 --- a/src/main/java/com/salesforce/dataloader/util/AppUtil.java +++ b/src/main/java/com/salesforce/dataloader/util/AppUtil.java @@ -28,11 +28,11 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.ProxySelector; @@ -40,11 +40,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.Builder; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; @@ -58,8 +53,6 @@ import java.util.Map; import java.util.Properties; import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.xml.parsers.FactoryConfigurationError; @@ -73,6 +66,8 @@ import com.salesforce.dataloader.config.Config; import com.salesforce.dataloader.config.Messages; import com.salesforce.dataloader.exception.ConfigInitializationException; +import com.salesforce.dataloader.exception.ParameterLoadException; +import com.sforce.ws.ConnectorConfig; import com.sforce.ws.bind.CalendarCodec; @@ -120,7 +115,6 @@ public enum APP_RUN_MODE { private static APP_RUN_MODE appRunMode = APP_RUN_MODE.UI; private static Logger logger = null; - private static String latestDownloadableDataLoaderVersion; private static final ArrayList CONTENT_SOBJECT_LIST = new ArrayList(); static { @@ -145,13 +139,8 @@ public static String[] initializeAppConfig(String[] args) throws FactoryConfigur LoggingUtil.initializeLog(argsMap); logger = LogManager.getLogger(AppUtil.class); setUseGMTForDateFieldValue(argsMap); - latestDownloadableDataLoaderVersion = _getLatestAvailableAppVersionFromWebsite(); return convertCommandArgsMapToArgsArray(argsMap); } - - public static String getLatestDownloadableDataLoaderVersion() { - return latestDownloadableDataLoaderVersion; - } public static OSType getOSType() throws SecurityException { String osName = System.getProperty(OS_NAME); @@ -465,38 +454,7 @@ public static int exec(List command, String exceptionMessage) { logger.error(exceptionMessage, e); } return exitVal; - } - - private static final String DL_DOWNLOADABLE_REGEX = "[0-9]+\\.[0-9]+\\.[0-9]+\\.zip"; - private static String _getLatestAvailableAppVersionFromWebsite() { - try { - Builder requestBuilder = HttpRequest.newBuilder(new URI(DATALOADER_DOWNLOAD_URL)); - HttpRequest request = requestBuilder.GET().build(); - HttpClient client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .proxy(ProxySelector.getDefault()) - .build(); - HttpResponse response = client.send(request, BodyHandlers.ofString()); - if (response.statusCode() != HttpURLConnection.HTTP_OK) { - logger.info("Unable to check for the latest available data loader version. Response code = " + response.statusCode()); - return DATALOADER_VERSION; - } - String responseContent = response.body(); - - Pattern htmlTagInRichTextPattern = Pattern.compile(DL_DOWNLOADABLE_REGEX); - Matcher matcher = htmlTagInRichTextPattern.matcher(responseContent); - String downloadableVersion = DATALOADER_VERSION; - if (matcher.find()) { - downloadableVersion = matcher.group(); - downloadableVersion = downloadableVersion.substring(0, downloadableVersion.lastIndexOf(".")); - } - return downloadableVersion; - } catch (Exception e) { - logger.info("Unable to check for the latest available data loader version: " + e.getMessage()); - return DATALOADER_VERSION; - } - } - + } public static boolean isValidHttpsUrl(String url) { try { @@ -555,9 +513,11 @@ public static void setSystemProxyValues() { logger.debug("detecting proxies"); List l = null; try { - l = ProxySelector.getDefault().select(new URI("https://www.salesforce.com")); - } - catch (URISyntaxException e) { + ProxySelector ps = ProxySelector.getDefault(); + if (ps != null) { + l = ps.select(new URI("https://www.salesforce.com")); + } + } catch (URISyntaxException e) { e.printStackTrace(); } if (l != null) { @@ -578,4 +538,53 @@ public static void setSystemProxyValues() { } } } + + public static void setConnectorConfigProxySettings(Config config, ConnectorConfig connConfig) { + // proxy properties + try { + String proxyHost = config.getString(Config.PROXY_HOST); + int proxyPort = config.getInt(Config.PROXY_PORT); + if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) { + logger.info(Messages.getFormattedString( + "AppUtil.sforceLoginProxyDetail", new String[] { proxyHost, String.valueOf(proxyPort) })); //$NON-NLS-1$ + connConfig.setProxy(proxyHost, proxyPort); + + String proxyUsername = config.getString(Config.PROXY_USERNAME); + if (proxyUsername != null && proxyUsername.length() > 0) { + logger.info(Messages.getFormattedString("AppUtil.sforceLoginProxyUser", proxyUsername)); //$NON-NLS-1$ + connConfig.setProxyUsername(proxyUsername); + + String proxyPassword = config.getString(Config.PROXY_PASSWORD); + if (proxyPassword != null && proxyPassword.length() > 0) { + logger.info(Messages.getString("AppUtil.sforceLoginProxyPassword")); //$NON-NLS-1$ + connConfig.setProxyPassword(proxyPassword); + } else { + connConfig.setProxyPassword(""); + logger.info("no proxy password"); + } + } + + String proxyNtlmDomain = config.getString(Config.PROXY_NTLM_DOMAIN); + if (proxyNtlmDomain != null && proxyNtlmDomain.length() > 0) { + logger.info(Messages.getFormattedString("AppUtil.sforceLoginProxyNtlm", proxyNtlmDomain)); //$NON-NLS-1$ + connConfig.setNtlmDomain(proxyNtlmDomain); + } + } + } catch (ParameterLoadException e) { + logger.error(e.getMessage()); + } + if (config.getBoolean(Config.DEBUG_MESSAGES)) { + connConfig.setTraceMessage(true); + connConfig.setPrettyPrintXml(true); + String filename = config.getString(Config.DEBUG_MESSAGES_FILE); + if (filename.length() > 0) { + try { + connConfig.setTraceFile(filename); + } catch (FileNotFoundException e) { + logger.warn(Messages.getFormattedString("Client.errorMsgDebugFilename", filename)); + } + } + } + + } } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 72ec5619..0c36f7a3 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -92,10 +92,10 @@ Client.sforceLogin=Beginning Partner Salesforce login .... PartnerClient.sforceLoginDetail=Salesforce login to {0} as user {1} PartnerClient.failedUsernamePasswordAuth=Failed to authenticate using the authentication service URL {0} specified for {1} environment. Error: {2} PartnerClient.retryUsernamePasswordAuth=Retrying to authenticate using username/password server URL {0} by setting the property {1} -Client.sforceLoginProxyDetail=Salesforce login will use proxy host: {0} port: {1} -Client.sforceLoginProxyUser=Salesforce login will use proxy user: {0} -Client.sforceLoginProxyPassword=Salesforce login will use proxy password from settings -Client.sforceLoginProxyNtlm=Salesforce login will use proxy NTLM domain: {0} +AppUtil.sforceLoginProxyDetail=Calls to Salesforce service will use proxy host: {0} port: {1} +AppUtil.sforceLoginProxyUser=Calls to Salesforce service will use proxy user: {0} +AppUtil.sforceLoginProxyPassword=Calls to Salesforce service will use proxy password from settings +AppUtil.sforceLoginProxyNtlm=Calls to Salesforce service will use proxy NTLM domain: {0} Client.fieldsError=Error getting Fields Client.arraySize=Object array size to be loaded: Client.resultNull=Save Result Null