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 <svatopluk.de...@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="<NetworkProxySettings>"/> + </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, "{key}")"/> + </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-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists