You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by sd...@apache.org on 2022/12/07 08:27:42 UTC

[netbeans] branch master updated: Support for proxy detection / autoconfiguration.

This is an automated email from the ASF dual-hosted git repository.

sdedic pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new a65047e41e Support for proxy detection / autoconfiguration.
     new bd3877440a Merge pull request #5006 from sdedic/gradle/proxydetect
a65047e41e is described below

commit a65047e41ea4ea5e7ed884c1af5de4675fce58fa
Author: Svata Dedic <sv...@oracle.com>
AuthorDate: Thu Nov 24 14:07:19 2022 +0100

    Support for proxy detection / autoconfiguration.
---
 extide/gradle/apichanges.xml                       |   2 +-
 extide/gradle/arch.xml                             |   7 +
 extide/gradle/manifest.mf                          |   2 +-
 .../modules/gradle/api/execute/Bundle.properties   |   5 +
 .../gradle/execute/GradleDaemonExecutor.java       |  14 +
 .../gradle/execute/GradleNetworkProxySupport.java  | 596 +++++++++++++++++++++
 .../gradle/loaders/LegacyProjectLoader.java        |  29 +-
 .../modules/gradle/options/Bundle.properties       |   1 +
 .../gradle/options/GradleExperimentalSettings.java |  14 +
 .../gradle/options/NetworkProxySettings.java       |  79 +++
 .../modules/gradle/options/SettingsPanel.form      |  42 +-
 .../modules/gradle/options/SettingsPanel.java      |  43 +-
 12 files changed, 814 insertions(+), 20 deletions(-)

diff --git a/extide/gradle/apichanges.xml b/extide/gradle/apichanges.xml
index 47013fa0c6..2c6e7e1fb9 100644
--- a/extide/gradle/apichanges.xml
+++ b/extide/gradle/apichanges.xml
@@ -91,7 +91,7 @@ is the proper place.
             <author login="sdedic"/>
             <compatibility semantic="compatible" addition="yes"/>
             <description>
-                Tasks declared by other projects are marked as 'external'. Tasjs can report their declaring project's path.
+                Tasks declared by other projects are marked as 'external'. Tasks can report their declaring project's path.
             </description>
             <class package="org.netbeans.modules.gradle.api" name="GradleTask"/>
         </change>
diff --git a/extide/gradle/arch.xml b/extide/gradle/arch.xml
index 78d3e8f8d3..c303d76d91 100644
--- a/extide/gradle/arch.xml
+++ b/extide/gradle/arch.xml
@@ -130,6 +130,13 @@
 
 
  <answer id="exec-property">
+     <api category="devel" group="branding" name="org.netbeans.modules.gradle.api.execute.NetworkProxySettings.allowOverride" type="export">
+         Brand the <code>org.netbeans.modules.gradle.api.execute.NetworkProxySettings.allowOverride</code> key in a 
+         <code>org.netbeans.modules.gradle.api.execute.Bundle</code> file
+         with one of the values <code>true</code> or <code>false</code> to specify whether to offer override of proxies
+         when running Gradle daemon. With <code>false</code>, the user is only given an option to upgrade gradle's configuration files.
+         Since 
+     </api>
      <api category="devel" group="branding" name="org.netbeans.modules.gradle.spi.DEFAULT_REUSE_OUTPUT" type="export">
          Brand the <code>DEFAULT_REUSE_OUTPUT</code> key in a 
          <code>org.netbeans.modules.gradle.spi.Bundle</code> file
diff --git a/extide/gradle/manifest.mf b/extide/gradle/manifest.mf
index 197c9fd82d..03eafb6fbd 100644
--- a/extide/gradle/manifest.mf
+++ b/extide/gradle/manifest.mf
@@ -3,4 +3,4 @@ AutoUpdate-Show-In-Client: false
 OpenIDE-Module: org.netbeans.modules.gradle/2
 OpenIDE-Module-Layer: org/netbeans/modules/gradle/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/gradle/Bundle.properties
-OpenIDE-Module-Specification-Version: 2.30
+OpenIDE-Module-Specification-Version: 2.31
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/Bundle.properties b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/Bundle.properties
index ca3bcc51eb..51a98c0356 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/Bundle.properties
+++ b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/Bundle.properties
@@ -82,3 +82,8 @@ WRITE_VERIFICATION_METADATA_DSC=<p>Generates checksums for dependencies used in
 org.netbeans.modules.gradle.api.execute.TrustProjectOption.TrustOnce=-1
 org.netbeans.modules.gradle.api.execute.TrustProjectOption.PermanentTrust=2
 org.netbeans.modules.gradle.api.execute.TrustProjectOption.RunAlways=0
+
+# A distribution can choose if 'override' proxy option is available: NetBeans will
+# override proxy in the gradle daemon's JVM using explicit system properties.
+org.netbeans.modules.gradle.api.execute.NetworkProxySettings.allowOverride=true
+
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDaemonExecutor.java b/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDaemonExecutor.java
index cd10f28c59..e9a4ae8f7a 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDaemonExecutor.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDaemonExecutor.java
@@ -56,6 +56,7 @@ import org.netbeans.api.project.ProjectInformation;
 import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 import org.netbeans.modules.gradle.api.execute.GradleExecConfiguration;
+import org.netbeans.modules.gradle.execute.GradleNetworkProxySupport.ProxyResult;
 import org.netbeans.modules.gradle.spi.GradleFiles;
 import org.netbeans.modules.gradle.spi.execute.GradleDistributionProvider;
 import org.netbeans.modules.gradle.spi.execute.GradleJavaPlatformProvider;
@@ -239,6 +240,19 @@ public final class GradleDaemonExecutor extends AbstractGradleExecutor {
                 }
             }
             GradleExecAccessor.instance().configureGradleHome(buildLauncher);
+            GradleNetworkProxySupport proxySupport = config.getProject().getLookup().lookup(GradleNetworkProxySupport.class);
+            if (proxySupport != null) {
+                try {
+                    ProxyResult result = proxySupport.checkProxySettings().get();
+                    if (result.getStatus() == GradleNetworkProxySupport.Status.ABORT) {
+                        showAbort();
+                        return;
+                    }
+                    buildLauncher = result.configure(buildLauncher);
+                } catch (InterruptedException | ExecutionException ex) {
+                    throw new BuildCancelledException("Interrupted", ex);
+                }
+            }
             buildLauncher.run();
             StatusDisplayer.getDefault().setStatusText(Bundle.BUILD_SUCCESS(getProjectName()));
             gradleTask.finish(0);
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleNetworkProxySupport.java b/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleNetworkProxySupport.java
new file mode 100644
index 0000000000..10a4181852
--- /dev/null
+++ b/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleNetworkProxySupport.java
@@ -0,0 +1,596 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.gradle.execute;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.gradle.tooling.BuildActionExecuter;
+import org.gradle.tooling.ConfigurableLauncher;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.gradle.api.NbGradleProject;
+import org.netbeans.modules.gradle.options.GradleExperimentalSettings;
+import org.netbeans.modules.gradle.options.NetworkProxySettings;
+import org.netbeans.modules.gradle.spi.GradleFiles;
+import org.netbeans.spi.project.ProjectServiceProvider;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.awt.NotificationDisplayer;
+import org.openide.awt.StatusDisplayer;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.EditableProperties;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+
+/**
+ * Support for proxy autodetection or autoconfiguration. The class works with {@link GradleExperimentalSettings} and {@link NetworkProxySettings} to determine
+ * the behaviour:
+ * <ul>
+ * <li>{@link NetworkProxySettings#IGNORE} - skip autodetection at all
+ * <li>{@link NetworkProxySettings#NOTICE} - just note mismatch, do not update configuration and continue building
+ * <li>{@link NetworkProxySettings#OVERRIDE} - override gradle.properties by explicit system properties
+ * <li>{@link NetworkProxySettings#UPDATE} - automatically update settings
+ * {@link NetworkProxySettings#ASK} - ask the user
+ * </ul>
+ * The user choice is remembered so for the same project and detected proxy, the question is not asked again. Also notice is displayed just once for project+detected proxy,
+ * so the log is not full of reminders.
+ * 
+ * @author sdedic
+ */
+@ProjectServiceProvider(service = GradleNetworkProxySupport.class, projectType = NbGradleProject.GRADLE_PROJECT_TYPE)
+public class GradleNetworkProxySupport {
+    private static final Logger LOG = Logger.getLogger(GradleNetworkProxySupport.class.getName());
+    /**
+     * Sample probe URI - google's public DNS server
+     */
+    private static final String PROBE_URI_STRING = "http://search.maven.org"; // NOI18N
+    
+    private static final String FILENAME_SUFFIX_OLD = ".old"; // NOI18N
+    private static final String SYSTEMPROP_HTTPS_PROXYPORT = "systemProp.https.proxyPort"; // NOI18N
+    private static final String SYSTEMPROP_HTTP_PROXYPORT = "systemProp.http.proxyPort"; // NOI18N
+    private static final String SYSTEMPROP_HTTPS_PROXYHOST = "systemProp.https.proxyHost"; // NOI18N
+    private static final String SYSTEMPROP_HTTP_PROXYHOST = "systemProp.http.proxyHost"; // NOI18N
+
+    private static final String JVM_HTTPS_PROXYPORT = "https.proxyPort"; // NOI18N
+    private static final String JVM_HTTP_PROXYPORT = "http.proxyPort"; // NOI18N
+    private static final String JVM_HTTPS_PROXYHOST = "https.proxyHost"; // NOI18N
+    private static final String JVM_HTTP_PROXYHOST = "http.proxyHost"; // NOI18N
+    
+    private static final int PORT_DEFAULT_HTTPS = 1080;
+    private static final int PORT_DEFAULT_HTTP = 80;
+    
+    private final Project project;
+    
+    /**
+     * Past decisions made by the user during this session. The Map is used so the user si not bothered that often with questions.
+     * If the user chooses 'override' or 'continue' (no action), the Map receives the public proxy spec and the result. If the same
+     * effective proxy is detected, the user is not asked again.
+     */
+    // @GuardedBy(this)
+    private Map<String, ProxyResult>    acknowledgedResults = new HashMap<>();
+    
+    public GradleNetworkProxySupport(Project project) {
+        this.project = project;
+    }
+    
+    public CompletableFuture<ProxyResult> checkProxySettings() {
+        return new Processor().checkProxy();
+    }
+    
+    public enum Status {
+        UNKNOWN,
+        CONTINUE,
+        RECONFIGURED,
+        OVERRIDE,
+        ABORT
+    }
+    
+    public static final class ProxyResult {
+        private final Status status;
+        private final Proxy proxy;
+        private final String toolProxy;
+        private final String proxyHost;
+        private final String proxySpec;
+        private final int proxyPort;
+
+        public ProxyResult(Status status, Proxy proxy) {
+            this.status = status;
+            this.proxy = proxy;
+            this.toolProxy = null;
+            this.proxySpec = null;
+            this.proxyHost = null;
+            this.proxyPort = -1;
+        }
+        
+        public ProxyResult(Status status, Proxy proxy, String toolProxy, String proxySpec, String proxyHost, int proxyPort) {
+            this.status = status;
+            this.proxy = proxy;
+            this.toolProxy = toolProxy;
+            this.proxySpec = proxySpec;
+            this.proxyHost = proxyHost;
+            this.proxyPort = proxyPort;
+        }
+
+        public Status getStatus() {
+            return status;
+        }
+
+        public Proxy getProxy() {
+            return proxy;
+        }
+
+        public String getToolProxy() {
+            return toolProxy;
+        }
+
+        public String getProxySpec() {
+            return proxySpec;
+        }
+        
+        public <T > BuildActionExecuter<T> configure(BuildActionExecuter<T> executor) {
+            configure((ConfigurableLauncher)executor);
+            return executor;
+        }
+        
+        public <T extends ConfigurableLauncher> T configure(T executor) {
+            if (status != Status.OVERRIDE) {
+                return executor;
+            }
+            addSystemProperty(executor, JVM_HTTP_PROXYHOST, proxyHost);
+            addSystemProperty(executor, JVM_HTTP_PROXYPORT, Integer.toString(proxyPort));
+            addSystemProperty(executor, JVM_HTTPS_PROXYHOST, proxyHost);
+            addSystemProperty(executor, JVM_HTTPS_PROXYPORT, Integer.toString(proxyPort));
+            
+            return executor;
+        }
+        
+        private void addSystemProperty(ConfigurableLauncher<?> executer, String propName, String value) {
+            executer.addJvmArguments("-D" + propName + "=" + (value == null ? "" : value));
+        }
+    }
+
+    @NbBundle.Messages({
+        "TITLE_GradleProxyMismatch=Possible Network Proxy Issue",
+        "# {0} - gradle proxy",
+        "MSG_ProxyMisconfiguredDirect=Gradle is configured for a proxy {0}, but the system does not require a proxy for network connections. Proxy settings should be removed from user gradle.properties.",
+        "# {0} - system proxy",
+        "MSG_ProxyMisconfiguredMissing=Gradle is not configured to use a network proxy, but the proxy {0} seems to be required for network communication. User gradle.properties should be updated to specify a proxy.",
+        "# {0} - system proxy",
+        "# {1} - gradle proxy",
+        "MSG_ProxyMisconfiguredOther=Gradle is configured to use a network proxy {1}, but the proxy {0} seems to be required for network communication. Proxy settings should be updated in user gradle.properties.",
+        "MSG_AppendAskUpdate=\nUpdate Gradle configuration ? Choose \"Override\" to apply detected proxy only to IDE operations.",
+        "MSG_AppendAskUpdate2=\nUpdate Gradle configuration ?",
+        "ACTION_Override=Override",
+        "ACTION_Continue=Keep settings",
+        "# {0} - date/time of the update",
+        "COMMENT_CreatedByNetBeans=# This proxy configuration has been updated by Apache NetBeans on {0}",
+        "TITLE_ConfigUpdateFailed=Configuration update failed",
+        "# {0} - error message",
+        "ERROR_ConfigUpdateFailed=Failed to modify Gradle user properties: {0}",
+        "# {0} - proxy specification",
+        "MSG_ProxySetTo=Gradle Network proxy set to: {0}",
+        "MSG_ProxyCleared=Gradle Network proxy removed",
+        
+        "# Branding API: change to false to disable suggestion to override proxies in Gradle invocation",
+        "CTRL_SuggestProxyOverride=true"
+    })
+    /**
+     * Encapsulates a single check to avoid an enormous method or a ton of parameters passed through
+     * a method chain. Should be constructed for each new check separately.
+     */
+    private class Processor {
+        Proxy publicProxy;
+        String publicProxyHost;
+        int publicProxyPort;
+        int publicProxyNonDefaultPort;
+        
+        String proxyAuthority;
+        String proxyHost;
+        String publicProxySpec;
+        
+        int proxyPort;
+        GradleFiles gradleFiles;
+        
+        public CompletableFuture<ProxyResult> checkProxy() {
+            boolean supportOverride = NetworkProxySettings.allowProxyOverride();
+            NetworkProxySettings action = GradleExperimentalSettings.getDefault().getNetworkProxy();
+            if (action == NetworkProxySettings.IGNORE) {
+                return CompletableFuture.completedFuture(createResult(Status.CONTINUE));
+            }
+            
+            obtainPublicProxy();
+            loadProjectProxy();
+            
+            boolean direct = publicProxy == null || publicProxy.type() == Proxy.Type.DIRECT;
+            
+            if (direct && proxyAuthority == null || gradleFiles == null) {
+                LOG.log(Level.FINE, "Project does not specify a proxy and none is needed");
+                return CompletableFuture.completedFuture(createResult(Status.CONTINUE));
+            }
+            
+            if (publicProxy != null) {
+                if (publicProxyHost == null) {
+                    // unable to decipher proxy address
+                    LOG.log(Level.WARNING, "Unable to decipher proxy: {0}", publicProxy);
+                    return CompletableFuture.completedFuture(new ProxyResult(Status.UNKNOWN, null));
+                }
+                if (publicProxyHost.equals(proxyHost) && proxyPort == publicProxyPort) {
+                    LOG.log(Level.FINE, "Project specifies detected proxy: {0}", publicProxySpec);
+                    return CompletableFuture.completedFuture(new ProxyResult(Status.CONTINUE, publicProxy));
+                }
+            }
+            
+            // at this point, it's obvious that 
+            
+            String userMessage;
+            
+            if (direct) {
+                userMessage = Bundle.MSG_ProxyMisconfiguredDirect(proxyAuthority);
+            } else if (proxyAuthority == null) {
+                userMessage = Bundle.MSG_ProxyMisconfiguredMissing(publicProxySpec);
+            } else {
+                userMessage = Bundle.MSG_ProxyMisconfiguredOther(publicProxySpec, proxyAuthority);
+            }
+            
+            ProxyResult result;
+            synchronized (this) {
+                result = acknowledgedResults.get(publicProxySpec);
+            }
+            if (result != null) {
+                LOG.log(Level.FINE, "Reusing previous decision: {0} with proxy {1}", new Object[] { result.getStatus(), result.proxySpec });
+                switch (result.getStatus()) {
+                    case CONTINUE:
+                        // includes noth NOTICE and IGNORE settings !
+                        action = NetworkProxySettings.IGNORE;
+                        break;
+                    case OVERRIDE: 
+                        action = NetworkProxySettings.OVERRIDE;
+                        break;
+                    case RECONFIGURED:
+                        action = NetworkProxySettings.UPDATE;
+                        break;
+                }
+            }
+            // TODO: because of some strange gradle tooling API behaviour, it is not possible to 
+            // override ~/.gradle/gradle.properties system properties with values passed on the commandline or -D ...
+            // ... but ./gradlew works for some strange reason.
+            // See https://github.com/gradle/gradle/issues/22856
+            if (proxyHost != null) {
+                supportOverride = false;
+                if (action == NetworkProxySettings.OVERRIDE) {
+                    action = NetworkProxySettings.NOTICE;
+                }
+            }
+            switch (action) {
+                case IGNORE:
+                    return CompletableFuture.completedFuture(createResult(Status.CONTINUE));
+                    
+                case NOTICE:
+                    NotificationDisplayer.getDefault().notify(
+                            Bundle.TITLE_GradleProxyMismatch(),
+                            NbGradleProject.getIcon(),
+                            userMessage, null, NotificationDisplayer.Priority.NORMAL, NotificationDisplayer.Category.WARNING);
+                    return CompletableFuture.completedFuture(createResult(Status.CONTINUE));
+                
+                case OVERRIDE:
+                    return CompletableFuture.completedFuture(createResult(Status.OVERRIDE));
+                    
+                case UPDATE:
+                    return CompletableFuture.completedFuture(updateGradleConfiguration(false));
+                    
+                case ASK:
+                    if (result != null) {
+                        return CompletableFuture.completedFuture(result);
+                    }
+                    String promptMsg;
+                    
+                    if (supportOverride) {
+                        promptMsg = userMessage + Bundle.MSG_AppendAskUpdate();
+                    } else {
+                        promptMsg = userMessage + Bundle.MSG_AppendAskUpdate2();
+                    }
+                    NotifyDescriptor desc = new NotifyDescriptor.Confirmation(
+                            promptMsg, Bundle.TITLE_GradleProxyMismatch(),
+                            NotifyDescriptor.OK_CANCEL_OPTION,  NotifyDescriptor.WARNING_MESSAGE);
+                    if (supportOverride) {
+                        desc.setAdditionalOptions(new Object[] { Bundle.ACTION_Continue(), Bundle.ACTION_Override() });
+                    } else {
+                        desc.setAdditionalOptions(new Object[] { Bundle.ACTION_Continue() });
+                    }
+                    desc.setValue(NotifyDescriptor.OK_OPTION);
+                    
+                    return DialogDisplayer.getDefault().notifyFuture(desc).thenApply(this::processUserConfirmation).exceptionally(t -> {
+                        if ((t instanceof CompletionException) && (t.getCause() instanceof CancellationException)) {
+                            return createResult(Status.ABORT);
+                        } else {
+                            return createResult(Status.UNKNOWN);
+                        }
+                    });
+            }
+            
+            return null;
+        }
+
+        ProxyResult createResult(Status s) {
+            boolean keep = false;
+            switch (s) {
+                case OVERRIDE:
+                    keep = true;
+                    LOG.log(Level.FINE, "Will override proxy to {0}", publicProxy);
+                    break;
+                case ABORT:
+                    LOG.log(Level.FINE, "Will abort operation");
+                    break;
+                case CONTINUE:
+                    keep = true;
+                    LOG.log(Level.FINE, "No action will be taken");
+                    break;
+                case RECONFIGURED:
+                    LOG.log(Level.FINE, "User properties were reconfigured to {0}", publicProxy);
+                    break;
+            }
+            ProxyResult r = new ProxyResult(s, publicProxy, proxyAuthority, publicProxySpec, publicProxyHost, publicProxyPort);
+            if (keep) {
+                synchronized (this) {
+                    acknowledgedResults.put(publicProxySpec, r);
+                }
+            }
+            return r;
+        }
+        
+        ProxyResult updateGradleConfiguration(boolean interactive) {
+            EditableProperties eprops = new EditableProperties(true);
+            
+            File userProps = gradleFiles.getFile(GradleFiles.Kind.USER_PROPERTIES);
+            
+            // TODO: would be better if, when removing the proxy, the support would only comment out the keys. But EditableProperties is not suitable for that
+            // now.
+            if (userProps.exists()) {
+                try (FileInputStream is = new FileInputStream(userProps)) {
+                    eprops.load(is);
+                } catch (IOException ex) {
+                    NotificationDisplayer.getDefault().notify(
+                            Bundle.TITLE_ConfigUpdateFailed(), 
+                            NbGradleProject.getWarningIcon(),
+                            Bundle.ERROR_ConfigUpdateFailed(ex.getLocalizedMessage()), null,
+                            NotificationDisplayer.Priority.HIGH, NotificationDisplayer.Category.ERROR);
+                    return createResult(Status.UNKNOWN);
+                }
+            } else {
+                if (publicProxyHost == null) {
+                    return createResult(Status.CONTINUE);
+                }
+            }
+            
+            if (publicProxy != null) {
+                eprops.put(SYSTEMPROP_HTTP_PROXYHOST, publicProxyHost);
+                eprops.put(SYSTEMPROP_HTTPS_PROXYHOST, publicProxyHost);
+                if (publicProxyNonDefaultPort > 0) {
+                    eprops.put(SYSTEMPROP_HTTP_PROXYPORT, Integer.toString(publicProxyNonDefaultPort));
+                    eprops.put(SYSTEMPROP_HTTPS_PROXYPORT, Integer.toString(publicProxyNonDefaultPort));
+                } else {
+                    eprops.remove(SYSTEMPROP_HTTP_PROXYPORT);
+                    eprops.remove(SYSTEMPROP_HTTPS_PROXYPORT);
+                }
+                eprops.setComment(SYSTEMPROP_HTTP_PROXYHOST, new String[] {
+                        Bundle.COMMENT_CreatedByNetBeans(DateFormat.getDateTimeInstance().format(new Date()))
+                    }, true );
+            } else {
+                eprops.remove(SYSTEMPROP_HTTP_PROXYHOST);
+                eprops.remove(SYSTEMPROP_HTTP_PROXYPORT);
+                eprops.remove(SYSTEMPROP_HTTPS_PROXYHOST);
+                eprops.remove(SYSTEMPROP_HTTPS_PROXYPORT);
+            }
+            
+            if (userProps.exists()) {
+                String base = userProps.getName() + FILENAME_SUFFIX_OLD;
+                File f = new File(userProps.getParentFile(), base);
+                int n = 1;
+                while (f.exists()) {
+                    f = new File(userProps.getParentFile(), base + "." + n); // NOI18N
+                    n++;
+                }
+                userProps.renameTo(f);
+            }
+            try (FileOutputStream os = new FileOutputStream(userProps)) {
+                eprops.store(os);
+                StatusDisplayer.getDefault().setStatusText(
+                        proxyHost == null ?
+                                Bundle.MSG_ProxyCleared() :
+                                Bundle.MSG_ProxySetTo(proxyAuthority)
+                );
+            } catch (IOException ex) {
+                NotificationDisplayer.getDefault().notify(
+                        Bundle.TITLE_ConfigUpdateFailed(), 
+                        NbGradleProject.getWarningIcon(), 
+                        Bundle.ERROR_ConfigUpdateFailed(ex.getLocalizedMessage()), null,
+                        NotificationDisplayer.Priority.HIGH, NotificationDisplayer.Category.ERROR);
+                return createResult(Status.ABORT);
+            }
+            return createResult(Status.RECONFIGURED);
+        }
+        
+        ProxyResult processUserConfirmation(NotifyDescriptor desc) {
+            Object val = desc.getValue();
+            if (val == NotifyDescriptor.CANCEL_OPTION) {
+                return createResult(Status.ABORT);
+            } else if (val == Bundle.ACTION_Continue()) {
+                return createResult(Status.CONTINUE);
+            } else if (val == Bundle.ACTION_Override()) {
+                return createResult(Status.OVERRIDE);
+            } else if (val == NotifyDescriptor.OK_OPTION) {
+                return updateGradleConfiguration(true);
+            }
+            return createResult(Status.UNKNOWN);
+        }
+        
+        private void obtainPublicProxy() {
+            URI probeUri;
+            try {
+                probeUri = new URI(PROBE_URI_STRING);
+            } catch (URISyntaxException ex) {
+                // this is competely unexpected
+                Exceptions.printStackTrace(ex);
+                return;
+            }
+            List<Proxy> proxies = ProxySelector.getDefault().select(probeUri);
+            LOG.log(Level.FINER, "Detected proxies for URI {0}: {1}", new Object[] { probeUri, proxies });
+            for (Proxy p : proxies) {
+                if (p.type() == Proxy.Type.HTTP) {
+                    publicProxy = p;
+                    LOG.log(Level.FINE, "Selected HTTP proxy: {0}", p);
+                    break;
+                } else if (p.type() == Proxy.Type.SOCKS) {
+                    if (publicProxy == null) {
+                        LOG.log(Level.FINE, "Found SOCKS proxy: {0}", p);
+                        publicProxy = p;
+                    }
+                }
+            }
+            if (publicProxy != null) {
+                SocketAddress proxyAddress = publicProxy.address();
+                if (proxyAddress instanceof InetSocketAddress) {
+                    InetSocketAddress iaddr = (InetSocketAddress)proxyAddress;
+                    int port = iaddr.getPort();
+                    int defPort = -1;
+
+                    switch(publicProxy.type()) {
+                        case HTTP:
+                            defPort = PORT_DEFAULT_HTTP; 
+                            break;
+                        case SOCKS:
+                            defPort = PORT_DEFAULT_HTTPS; 
+                            break;
+                    }
+                    
+                    if (port > 1) {
+                        publicProxyPort = port;
+                        if (publicProxyPort != defPort) {
+                            publicProxyNonDefaultPort = port;
+                        }
+                    }
+                    publicProxyHost = ((InetSocketAddress) proxyAddress).getHostString();
+                    publicProxySpec = publicProxyHost + ((publicProxyNonDefaultPort == 0) ? "" : ":" + publicProxyNonDefaultPort);
+                    LOG.log(Level.FINE, "Detected proxy: {0}", publicProxySpec);
+                }
+            }
+        }
+
+        private boolean extractNetworkProxy(Properties props) {
+            proxyHost = props.getProperty(SYSTEMPROP_HTTP_PROXYHOST);
+            String portKey;
+            int defPort;
+            
+            if (proxyHost == null || proxyHost.isEmpty()) {
+                proxyHost = props.getProperty(SYSTEMPROP_HTTPS_PROXYHOST);
+                if (proxyHost == null || proxyHost.isEmpty()) {
+                    proxyHost = null;
+                    proxyPort = -1;
+                    return false;
+                } else {
+                    LOG.log(Level.FINER, "Found https proxy: ", proxyHost);
+                }
+                portKey = SYSTEMPROP_HTTPS_PROXYPORT;
+                defPort = 443;
+            } else {
+                LOG.log(Level.FINER, "Found http proxy: ", proxyHost);
+                defPort = 80;
+                portKey = SYSTEMPROP_HTTP_PROXYPORT;
+            }
+            
+            String port = props.getProperty(portKey);
+            if (port != null && !port.trim().isEmpty()) {
+                proxyAuthority = proxyHost + ":" + port;
+                try {
+                    proxyPort = Integer.parseInt(port);
+                } catch (NumberFormatException ex) {
+                    // expected ?
+                    proxyPort = defPort;
+                    proxyAuthority = proxyHost;
+                }
+            } else {
+                proxyPort = defPort;
+                proxyAuthority = proxyHost;
+            }
+            return true;
+        }
+        
+        
+        private void loadProjectProxy() {
+            File f = FileUtil.toFile(project.getProjectDirectory());
+            if (f == null || !f.exists()) {
+                LOG.log(Level.WARNING, "Project has no directory: {0}", project);
+                return;
+            }
+            GradleFiles gf = new GradleFiles(f);
+            gradleFiles = gf;
+            // system properties are only read from the root project's directory, not from subprojects.
+            File rootDir = gf.getRootDir();
+            LOG.log(Level.FINE, "Project directory: {0}, root directory: {1}", new Object[] { f, rootDir });
+            if (!rootDir.equals(f)) {
+                gf = new GradleFiles(rootDir);
+            }
+            
+            Properties props = new Properties();
+            
+            File userProperties = gf.getFile(GradleFiles.Kind.USER_PROPERTIES);
+            File projectProperties = gf.getFile(GradleFiles.Kind.PROJECT_PROPERTIES);
+            
+            if (projectProperties != null && projectProperties.exists()) {
+                try (FileInputStream fis = new FileInputStream(projectProperties)) {
+                    LOG.log(Level.FINER, "Loading project properties from {0}", projectProperties);
+                    props.load(fis);
+                } catch (IOException ex) {
+                    // TBD: log
+                }
+            }
+            // override project properties with user properties; see Gradle manual for precedence.
+            if (userProperties != null && userProperties.exists()) {
+                try (FileInputStream fis = new FileInputStream(userProperties)) {
+                    LOG.log(Level.FINER, "Loading user properties from {0}", userProperties);
+                    props.load(fis);
+                } catch (IOException ex) {
+                    // TBD: log
+                }
+            }
+            
+            extractNetworkProxy(props);
+        }
+        
+    }
+    
+}
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java b/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java
index b05f751916..9406a19764 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java
@@ -75,6 +75,7 @@ import org.netbeans.modules.gradle.tooling.internal.NbProjectInfo.Report;
 import org.netbeans.modules.gradle.api.execute.GradleCommandLine;
 import org.netbeans.modules.gradle.api.execute.RunUtils;
 import org.netbeans.modules.gradle.cache.ProjectInfoDiskCache;
+import org.netbeans.modules.gradle.execute.GradleNetworkProxySupport;
 import org.netbeans.modules.gradle.spi.GradleSettings;
 import org.openide.util.Cancellable;
 import org.openide.util.NbBundle;
@@ -326,6 +327,11 @@ public class LegacyProjectLoader extends AbstractProjectLoader {
         Throwable th = ex;
         while (th != null) {
             problems.add(GradleProject.createGradleReport(null, th.getMessage()));
+            ex = th;
+            th = th.getCause();
+            if (ex == th) {
+                break;
+            }
         }
         return problems;
     }
@@ -454,6 +460,9 @@ public class LegacyProjectLoader extends AbstractProjectLoader {
         return ret;
     }
 
+    @NbBundle.Messages({
+        "ERR_UserAbort=Project analysis aborted by the user."
+    })
     private static NbProjectInfo retrieveProjectInfo(NbGradleProjectImpl projectImpl, GoOnline goOnline, ProjectConnection pconn, GradleCommandLine cmd, CancellationToken token, ProgressListener pl, AtomicBoolean wasOnline) throws GradleConnectionException, IllegalStateException {
         NbProjectInfo ret;
 
@@ -489,8 +498,26 @@ public class LegacyProjectLoader extends AbstractProjectLoader {
                 }
             }
         }
+
+        BuildActionExecuter<NbProjectInfo> action = createInfoAction(pconn, online, token, pl);        
+        // since we're going online, check the network settings:
+        GradleNetworkProxySupport support = projectImpl.getLookup().lookup(GradleNetworkProxySupport.class);
+        if (support != null) {
+            try {
+                GradleNetworkProxySupport.ProxyResult result = support.checkProxySettings().get();
+                switch (result.getStatus()) {
+                    case ABORT:
+                        LOG.log(Level.FINE, "User cancelled the project load");
+                        throw new IllegalStateException(Bundle.ERR_UserAbort());
+                }
+                action = result.configure(action);
+            } catch (InterruptedException ex) {
+                throw new IllegalStateException(ex);
+            } catch (ExecutionException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
         
-        BuildActionExecuter<NbProjectInfo> action = createInfoAction(pconn, online, token, pl);
         wasOnline.set(true);
         return runInfoAction(action);
     }
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties b/extide/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties
index a192be03cb..76285a5c74 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/Bundle.properties
@@ -59,3 +59,4 @@ SettingsPanel.cbUseConfigCache.toolTipText=<html>This is an <b>incubating</b> fe
 SettingsPanel.cbUseConfigCache.text=Use Configuration Cache
 SettingsPanel.cbNoRebuild.text=Do not Rebuild Project Dependencies
 SettingsPanel.cbNoRebuild.toolTipText=<html>Useful for debugging and fine-tuning buildSrc, but <b>can lead to wrong results</b>.<br/>Use with caution!
+SettingsPanel.jLabel2.text=Network Proxy:
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/options/GradleExperimentalSettings.java b/extide/gradle/src/org/netbeans/modules/gradle/options/GradleExperimentalSettings.java
index 35432d52e5..3fc9a8185b 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/options/GradleExperimentalSettings.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/GradleExperimentalSettings.java
@@ -29,6 +29,7 @@ public final class GradleExperimentalSettings {
     public static final String PROP_DISABLE_CACHE = "disableCache";
     public static final String PROP_LAZY_OPEN_GROUPS = "lazyOpen";
     public static final String PROP_BUNDLED_LOADING = "bundledLoading";
+    public static final String PROP_NETWORK_PROXY = "networkProxy";
 
     private static final GradleExperimentalSettings INSTANCE = new GradleExperimentalSettings(NbPreferences.forModule(GradleExperimentalSettings.class));
     private final Preferences preferences;
@@ -68,4 +69,17 @@ public final class GradleExperimentalSettings {
     public boolean isBundledLoading() {
         return getPreferences().getBoolean(PROP_BUNDLED_LOADING, false);
     }
+    
+    public NetworkProxySettings getNetworkProxy() {
+        String s = getPreferences().get(PROP_NETWORK_PROXY, NetworkProxySettings.ASK.name());
+        try {
+            return NetworkProxySettings.valueOf(s);
+        } catch (IllegalArgumentException ex) {
+            return NetworkProxySettings.ASK;
+        }
+    }
+    
+    public void setNetworkProxy(NetworkProxySettings s) {
+        getPreferences().put(PROP_NETWORK_PROXY, s.name());
+    }
 }
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/options/NetworkProxySettings.java b/extide/gradle/src/org/netbeans/modules/gradle/options/NetworkProxySettings.java
new file mode 100644
index 0000000000..fdb03195ec
--- /dev/null
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/NetworkProxySettings.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.gradle.options;
+
+import org.netbeans.modules.gradle.api.execute.RunUtils;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author sdedic
+ */
+@NbBundle.Messages({
+    "PROXY_IGNORE=Do not check",
+    "PROXY_NOTICE=Display Mismatch Notice",
+    "PROXY_UPDATE=Update User Properties",
+    "PROXY_ASK=Ask Before Execution",
+    "PROXY_OVERRIDE=Override on execution",
+})
+public enum NetworkProxySettings {
+    /**
+     * Do not verify proxy settings.
+     */
+    IGNORE,
+    /**
+     * Display a notice that proxy settings mismatch.
+     */
+    NOTICE,
+    /**
+     * Update user's gradle.properties file.
+     */
+    UPDATE,
+    /**
+     * Ask the user for confirmation.
+     */
+    ASK,
+    /**
+     * Automatically override on execution, but do not change gradle.properties.
+     */
+    OVERRIDE;
+    
+    public String toString() {
+        switch (this) {
+            case IGNORE: return Bundle.PROXY_IGNORE();
+            case NOTICE: return Bundle.PROXY_NOTICE();
+            case UPDATE: return Bundle.PROXY_UPDATE();
+            case ASK: return Bundle.PROXY_ASK();
+            case OVERRIDE: return Bundle.PROXY_OVERRIDE();
+            
+            default:
+                return name();
+        }
+    }
+
+    private static final String BRANDING_API_OVERRIDE_ENABLED = "org.netbeans.modules.gradle.api.execute.NetworkProxySettings.allowOverride";
+    
+    /**
+     * Determines if override is a valid option.
+     * @return true, if override should be offered as an option
+     */
+    public static boolean allowProxyOverride() {
+        return Boolean.parseBoolean(NbBundle.getMessage(RunUtils.class, BRANDING_API_OVERRIDE_ENABLED));
+    }
+}
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form b/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
index 47224f56b6..e261be3182 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
@@ -41,7 +41,7 @@
     <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
     <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
     <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-70,0,0,3,82"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-35,0,0,3,82"/>
   </AuxValues>
 
   <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
@@ -72,7 +72,7 @@
                   <EmptySpace max="-2" attributes="0"/>
                   <Component id="lblCategories" min="-2" max="-2" attributes="0"/>
                   <EmptySpace max="-2" attributes="0"/>
-                  <Component id="lstCategories" max="32767" attributes="0"/>
+                  <Component id="lstCategories" pref="440" max="32767" attributes="0"/>
                   <EmptySpace max="-2" attributes="0"/>
               </Group>
           </Group>
@@ -146,9 +146,16 @@
                           </Group>
                           <Component id="jPanel2" alignment="0" max="32767" attributes="0"/>
                           <Group type="102" alignment="1" attributes="0">
-                              <Component id="lbAllowExecution" max="32767" attributes="0"/>
+                              <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+                              <Group type="103" groupAlignment="0" max="-2" attributes="0">
+                                  <Component id="lbAllowExecution" max="32767" attributes="0"/>
+                                  <Component id="jLabel2" max="32767" attributes="0"/>
+                              </Group>
                               <EmptySpace max="-2" attributes="0"/>
-                              <Component id="cbAllowExecution" min="-2" pref="280" max="-2" attributes="0"/>
+                              <Group type="103" groupAlignment="0" max="-2" attributes="0">
+                                  <Component id="cbNetworkProxy" max="32767" attributes="0"/>
+                                  <Component id="cbAllowExecution" pref="280" max="32767" attributes="0"/>
+                              </Group>
                           </Group>
                       </Group>
                       <EmptySpace max="-2" attributes="0"/>
@@ -166,7 +173,12 @@
                           <Component id="cbAllowExecution" alignment="3" min="-2" max="-2" attributes="0"/>
                           <Component id="lbAllowExecution" alignment="3" min="-2" max="-2" attributes="0"/>
                       </Group>
-                      <EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="3" attributes="0">
+                          <Component id="cbNetworkProxy" alignment="3" min="-2" max="-2" attributes="0"/>
+                          <Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace max="32767" attributes="0"/>
                       <Component id="cbPreferMaven" min="-2" max="-2" attributes="0"/>
                       <EmptySpace max="-2" attributes="0"/>
                   </Group>
@@ -442,7 +454,7 @@
                               <Component id="cbConfigureOnDemand" alignment="0" min="-2" max="-2" attributes="0"/>
                               <Component id="cbNoRebuild" alignment="0" min="-2" max="-2" attributes="0"/>
                           </Group>
-                          <EmptySpace max="32767" attributes="0"/>
+                          <EmptySpace pref="162" max="32767" attributes="0"/>
                           <Group type="103" groupAlignment="0" attributes="0">
                               <Component id="cbUseConfigCache" min="-2" max="-2" attributes="0"/>
                               <Group type="103" groupAlignment="0" max="-2" attributes="0">
@@ -548,6 +560,18 @@
                 </Property>
               </Properties>
             </Component>
+            <Component class="javax.swing.JComboBox" name="cbNetworkProxy">
+              <AuxValues>
+                <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;NetworkProxySettings&gt;"/>
+              </AuxValues>
+            </Component>
+            <Component class="javax.swing.JLabel" name="jLabel2">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/gradle/options/Bundle.properties" key="SettingsPanel.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+            </Component>
           </SubComponents>
         </Container>
         <Container class="javax.swing.JPanel" name="pnlAppearance">
@@ -582,7 +606,7 @@
                       <Component id="jPanel4" min="-2" max="-2" attributes="0"/>
                       <EmptySpace max="-2" attributes="0"/>
                       <Component id="jPanel5" min="-2" max="-2" attributes="0"/>
-                      <EmptySpace pref="204" max="32767" attributes="0"/>
+                      <EmptySpace pref="232" max="32767" attributes="0"/>
                   </Group>
               </Group>
             </DimensionLayout>
@@ -762,7 +786,7 @@
                           <Component id="lbDownloadJavadoc" alignment="3" min="-2" max="-2" attributes="0"/>
                           <Component id="cbDownloadJavadoc" alignment="3" min="-2" max="-2" attributes="0"/>
                       </Group>
-                      <EmptySpace pref="352" max="32767" attributes="0"/>
+                      <EmptySpace pref="384" max="32767" attributes="0"/>
                   </Group>
               </Group>
             </DimensionLayout>
@@ -865,7 +889,7 @@
                       <Component id="cbOpenLazy" min="-2" max="-2" attributes="0"/>
                       <EmptySpace max="-2" attributes="0"/>
                       <Component id="cbBundledLoading" min="-2" max="-2" attributes="0"/>
-                      <EmptySpace pref="334" max="32767" attributes="0"/>
+                      <EmptySpace pref="365" max="32767" attributes="0"/>
                   </Group>
               </Group>
             </DimensionLayout>
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java b/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
index 546024e5d4..e8d10f7312 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
@@ -76,6 +76,12 @@ public class SettingsPanel extends javax.swing.JPanel {
         cbDownloadSources.setModel(new DefaultComboBoxModel<>(GradleSettings.DownloadMiscRule.values()));
         cbDownloadJavadoc.setModel(new DefaultComboBoxModel<>(GradleSettings.DownloadMiscRule.values()));
         cbAllowExecution.setModel(new DefaultComboBoxModel<>(GradleSettings.GradleExecutionRule.values()));
+        
+        DefaultComboBoxModel mdl = new DefaultComboBoxModel<>(NetworkProxySettings.values());
+        if (!NetworkProxySettings.allowProxyOverride()) {
+            mdl.removeElement(NetworkProxySettings.OVERRIDE);
+        }
+        cbNetworkProxy.setModel(mdl);
     }
 
     /**
@@ -119,6 +125,8 @@ public class SettingsPanel extends javax.swing.JPanel {
         lbAllowExecution = new javax.swing.JLabel();
         cbAllowExecution = new javax.swing.JComboBox<>();
         cbPreferMaven = new javax.swing.JCheckBox();
+        cbNetworkProxy = new javax.swing.JComboBox<>();
+        jLabel2 = new javax.swing.JLabel();
         pnlAppearance = new javax.swing.JPanel();
         jPanel4 = new javax.swing.JPanel();
         cbDisplayDescription = new javax.swing.JCheckBox();
@@ -174,7 +182,7 @@ public class SettingsPanel extends javax.swing.JPanel {
                 .addContainerGap()
                 .addComponent(lblCategories)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addComponent(lstCategories, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addComponent(lstCategories, javax.swing.GroupLayout.DEFAULT_SIZE, 440, Short.MAX_VALUE)
                 .addContainerGap())
         );
 
@@ -364,7 +372,7 @@ public class SettingsPanel extends javax.swing.JPanel {
                     .addComponent(cbOffline)
                     .addComponent(cbConfigureOnDemand)
                     .addComponent(cbNoRebuild))
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 162, Short.MAX_VALUE)
                 .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addComponent(cbUseConfigCache)
                     .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
@@ -395,6 +403,8 @@ public class SettingsPanel extends javax.swing.JPanel {
 
         org.openide.awt.Mnemonics.setLocalizedText(cbPreferMaven, org.openide.util.NbBundle.getMessage(SettingsPanel.class, "SettingsPanel.cbPreferMaven.text")); // NOI18N
 
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(SettingsPanel.class, "SettingsPanel.jLabel2.text")); // NOI18N
+
         javax.swing.GroupLayout pnlExecutionLayout = new javax.swing.GroupLayout(pnlExecution);
         pnlExecution.setLayout(pnlExecutionLayout);
         pnlExecutionLayout.setHorizontalGroup(
@@ -408,9 +418,14 @@ public class SettingsPanel extends javax.swing.JPanel {
                         .addGap(0, 346, Short.MAX_VALUE))
                     .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                     .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlExecutionLayout.createSequentialGroup()
-                        .addComponent(lbAllowExecution, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addGap(0, 0, Short.MAX_VALUE)
+                        .addGroup(pnlExecutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+                            .addComponent(lbAllowExecution, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                            .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(cbAllowExecution, javax.swing.GroupLayout.PREFERRED_SIZE, 280, javax.swing.GroupLayout.PREFERRED_SIZE)))
+                        .addGroup(pnlExecutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+                            .addComponent(cbNetworkProxy, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                            .addComponent(cbAllowExecution, 0, 280, Short.MAX_VALUE))))
                 .addContainerGap())
         );
         pnlExecutionLayout.setVerticalGroup(
@@ -423,7 +438,11 @@ public class SettingsPanel extends javax.swing.JPanel {
                 .addGroup(pnlExecutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(cbAllowExecution, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                     .addComponent(lbAllowExecution))
-                .addGap(11, 11, 11)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(pnlExecutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(cbNetworkProxy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jLabel2))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                 .addComponent(cbPreferMaven)
                 .addContainerGap())
         );
@@ -509,7 +528,7 @@ public class SettingsPanel extends javax.swing.JPanel {
                 .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addContainerGap(204, Short.MAX_VALUE))
+                .addContainerGap(232, Short.MAX_VALUE))
         );
 
         pnlCards.add(pnlAppearance, "Appearance");
@@ -570,7 +589,7 @@ public class SettingsPanel extends javax.swing.JPanel {
                 .addGroup(pnlDependenciesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(lbDownloadJavadoc)
                     .addComponent(cbDownloadJavadoc, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addContainerGap(352, Short.MAX_VALUE))
+                .addContainerGap(384, Short.MAX_VALUE))
         );
 
         pnlCards.add(pnlDependencies, "Dependencies");
@@ -613,7 +632,7 @@ public class SettingsPanel extends javax.swing.JPanel {
                 .addComponent(cbOpenLazy)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(cbBundledLoading)
-                .addContainerGap(334, Short.MAX_VALUE))
+                .addContainerGap(365, Short.MAX_VALUE))
         );
 
         pnlCards.add(pnlExperimental, "Experimental");
@@ -746,6 +765,8 @@ public class SettingsPanel extends javax.swing.JPanel {
         cbDownloadJavadoc.setSelectedItem(settings.getDownloadJavadoc());
 
         cbAllowExecution.setSelectedItem(settings.getGradleExecutionRule());
+        
+        cbNetworkProxy.setSelectedItem(experimental.getNetworkProxy());
 
         new SwingWorker<List<GradleDistribution>, Void>() {
 
@@ -830,6 +851,8 @@ public class SettingsPanel extends javax.swing.JPanel {
                         LifecycleManager.getDefault().exit();
             });
         }
+        
+        experimental.setNetworkProxy((NetworkProxySettings)cbNetworkProxy.getSelectedItem());
     }
 
     public boolean hasChanges() {
@@ -869,6 +892,8 @@ public class SettingsPanel extends javax.swing.JPanel {
         isChanged |= settings.getDownloadJavadoc() != cbDownloadJavadoc.getSelectedItem();
 
         isChanged |= settings.getGradleExecutionRule() != cbAllowExecution.getSelectedItem();
+        
+        isChanged |= experimental.getNetworkProxy() != cbNetworkProxy.getSelectedItem();
 
         return isChanged;
     }
@@ -933,6 +958,7 @@ public class SettingsPanel extends javax.swing.JPanel {
     private javax.swing.JCheckBox cbEnableCache;
     private javax.swing.JComboBox<GradleDistribution> cbGradleVersion;
     private javax.swing.JCheckBox cbHideEmptyConfig;
+    private javax.swing.JComboBox<NetworkProxySettings> cbNetworkProxy;
     private javax.swing.JCheckBox cbNoRebuild;
     private javax.swing.JCheckBox cbOffline;
     private javax.swing.JCheckBox cbOpenLazy;
@@ -946,6 +972,7 @@ public class SettingsPanel extends javax.swing.JPanel {
     private javax.swing.JCheckBox cbStartDaemonOnStart;
     private javax.swing.JCheckBox cbUseConfigCache;
     private javax.swing.JLabel jLabel1;
+    private javax.swing.JLabel jLabel2;
     private javax.swing.JPanel jPanel1;
     private javax.swing.JPanel jPanel2;
     private javax.swing.JPanel jPanel3;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists