You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by lk...@apache.org on 2020/09/10 00:28:45 UTC

[netbeans] 02/02: [NETBEANS-4731, NETBEANS-4732] GradleDistributionManager as an API

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

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

commit f7b535c223436e14357ba10387e9edab1be99602
Author: Laszlo Kishalmi <la...@gmail.com>
AuthorDate: Sat Aug 22 11:58:32 2020 -0700

    [NETBEANS-4731, NETBEANS-4732] GradleDistributionManager as an API
---
 extide/gradle/apichanges.xml                       |  53 ++-
 .../org/netbeans/modules/gradle/GradleDaemon.java  |   4 +-
 .../modules/gradle/GradleDistributionManager.java  | 491 --------------------
 .../modules/gradle/GradleInstallPanel.form         |  81 ----
 .../modules/gradle/GradleInstallPanel.java         |  94 ----
 .../modules/gradle/GradleProjectCache.java         |  16 -
 .../modules/gradle/GradleProjectConnection.java    |  40 +-
 .../api/execute/GradleDistributionManager.java     | 513 +++++++++++++++++++++
 .../modules/gradle/api/execute/RunUtils.java       |  44 +-
 .../gradle/execute/GradleDaemonExecutor.java       |  54 ++-
 .../execute/GradleDistributionProviderImpl.java    | 121 +++--
 .../gradle/options/GradleOptionsController.java    |   6 +-
 .../modules/gradle/options/SettingsPanel.form      |   2 +-
 .../modules/gradle/options/SettingsPanel.java      |  48 +-
 .../gradle/queries/GradleSourceForBinary.java      |  10 +-
 .../spi/execute/GradleDistributionProvider.java    |  38 +-
 .../classpath/AbstractGradleScriptClassPath.java   |  10 +-
 17 files changed, 757 insertions(+), 868 deletions(-)

diff --git a/extide/gradle/apichanges.xml b/extide/gradle/apichanges.xml
index 793b2e3..875ee40 100644
--- a/extide/gradle/apichanges.xml
+++ b/extide/gradle/apichanges.xml
@@ -83,6 +83,44 @@ is the proper place.
     <!-- ACTUAL CHANGES BEGIN HERE: -->
 
     <changes>
+        <change id="gradle-project-connection">
+            <api name="general"/>
+            <summary>Expose Gradle ProjectConnection through Project's Lookup.</summary>
+            <version major="2" minor="4"/>
+            <date year="2020" month="8" day="21"/>
+            <author login="lkishalmi"/>
+            <compatibility binary="compatible" source="compatible"/>
+            <description>
+                <p>
+                    Gradle's <code><a href="https://docs.gradle.org/current/javadoc/org/gradle/tooling/ProjectConnection.html">ProjectConnection</a></code>
+                    is now available on the project lookup, making possible for modules to interact with Gradle in deeper level.
+                </p>
+            </description>
+        </change>
+        <change id="gradle-distribution-manager">
+            <api name="general"/>
+            <summary>Expose GradleDistribution manager as an API.</summary>
+            <version major="2" minor="4"/>
+            <date year="2020" month="8" day="21"/>
+            <author login="lkishalmi"/>
+            <compatibility binary="compatible" source="compatible"/>
+            <description>
+                <p>
+                    Starting to give the project more freedom over control of
+                    their <code><a href="@TOP@/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.GradleDistribution.html">GradleDistribution</a></code> in use, the previously non-API
+                    <code><a href="@TOP@/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.html">GradleDistributionManager</a></code> got a review and got moved into
+                    execution apis. Together with the <code>GradleDistributionProvider</code>
+                    (API/SPI) it is possible to fetch information on the Gradle
+                    Distribution used in the project.
+                </p>
+                <p>
+                    <code><a href="@TOP@/org/netbeans/modules/gradle/spi/execute/GradleDistributionProvider.html">GradleDistributionProvider</a></code> has a default implementation it
+                    is not necessary to implement it. With that new interface
+                    the <code><a href="@TOP@/org/netbeans/modules/gradle/api/execute/RunUtils.html#evaluateGradleDistribution-org.netbeans.api.project.Project-boolean-">RunUtils.evaluateGradleDistribution()</a></code>
+                    got deprecated.
+                </p>
+            </description>
+        </change>
         <change id="gradle-java-independence">
             <api name="general"/>
             <summary>Remove Java Platform dependency.</summary>
@@ -97,15 +135,14 @@ is the proper place.
                     Java Platform module has been removed.
                 </p>
                 <p>
-                    The removal affects <code>org.netbeans.modules.gradle.api.execute.RunUtils</code>
-                    <code>getActivePlatform</code> methods. The method returns a
-                    Pair with <code>null</code> as <code>second</code> which
+                    The removal affects <code><a href="@TOP@/org/netbeans/modules/gradle/api/execute/RunUtils.html#getActivePlatform-java.lang.String-">RunUtils.getActivePlatform</a></code>
+                    methods. The method returns a Pair with <code>null</code> as <code>second</code> which
                     indicates a broken Platform. This is for keeping the binary compatibility only.
                     These methods shall be not used at all. There is no
                     replacement planned for these API functions.
                 </p>
                 <p>
-                    Added <code>org.netbeans.modules.gradle.spi.exectute.GradleJavaPlatformProvider</code>
+                    Added <code><a href="@TOP@/org/netbeans/modules/gradle/spi/exectute/GradleJavaPlatformProvider.html">GradleJavaPlatformProvider</a></code>
                     which can be put into the project lookup, to be able to specify the JDK
                     to be used for Gradle build execution.
                 </p>
@@ -133,7 +170,7 @@ is the proper place.
                     This change is backported to version 2.0.1 as well.
                 </p>
                 <p>
-                    Added <code>RunUtils.isProjectTrusted(Project,boolean)</code>
+                    Added <code><a href="@TOP@/org/netbeans/modules/gradle/api/execute/RunUtils.html#isProjectTrusted-org.netbeans.api.project.Project-boolean-">RunUtils.isProjectTrusted(Project,boolean)</a></code>
                     where plugin can check if a project is trusted or request
                     one time trust with interactive mode.
                 </p>
@@ -144,8 +181,8 @@ is the proper place.
                     a result of an user action.
                 </p>
                 <p>
-                    Added <code>org.netbeans.modules.gradle.spi.GradleSettings.GradleExecutionRule</code>
-                    with setter ad getter in <code>org.netbeans.modules.gradle.spi.GradleSettings</code>
+                    Added <code><a href="@TOP@/org/netbeans/modules/gradle/spi/GradleSettings.GradleExecutionRule.html">GradleSettings.GradleExecutionRule</a></code>
+                    with setter ad getter in <code><a href="@TOP@/org/netbeans/modules/gradle/spi/GradleSettings.html">GradleSettings</a></code>
                     as a global option to how to treat automatic Gradle Execution globally.
                 </p>
             </description>
@@ -199,7 +236,7 @@ is the proper place.
             <compatibility source="compatible"/>
             <description>
                 <p>
-                    Added <code>RunUtils.cancelGradle(RunConfig)</code> to allow plugins to cancel an executed Gradle process.
+                    Added <code><a href="@TOP@/org/netbeans/modules/gradle/api/execute/RunUtils.html#cancelGradle-org.netbeans.modules.gradle.api.execute.RunConfig-">RunUtils.cancelGradle(RunConfig)</a></code> to allow plugins to cancel an executed Gradle process.
                 </p>
             </description>
             <class package="org.netbeans.modules.gradle.api.execute" name="RunUtils"/>
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/GradleDaemon.java b/extide/gradle/src/org/netbeans/modules/gradle/GradleDaemon.java
index 54a37bd..d516ee8 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/GradleDaemon.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/GradleDaemon.java
@@ -29,6 +29,7 @@ import org.gradle.tooling.BuildController;
 import org.gradle.tooling.GradleConnectionException;
 import org.gradle.tooling.GradleConnector;
 import org.gradle.tooling.ProjectConnection;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager;
 import org.openide.modules.InstalledFileLocator;
 import org.openide.modules.OnStart;
 import org.openide.util.RequestProcessor;
@@ -61,8 +62,7 @@ public final class GradleDaemon {
         public void run() {
             GradleSettings settings = GradleSettings.getDefault();
             GradleDistributionManager dmgr = GradleDistributionManager.get(settings.getGradleUserHome());
-            if ( settings.isStartDaemonOnStart()
-                    && dmgr.defaultToolingVersion().isAvailable()) {
+            if ( settings.isStartDaemonOnStart() && dmgr.defaultDistribution().isAvailable()) {
                 GRADLE_LOADER_RP.submit(GradleDaemon::doLoadDaemon);
             }
         }
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/GradleDistributionManager.java b/extide/gradle/src/org/netbeans/modules/gradle/GradleDistributionManager.java
deleted file mode 100644
index 5467929..0000000
--- a/extide/gradle/src/org/netbeans/modules/gradle/GradleDistributionManager.java
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * 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;
-
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.gradle.util.GradleVersion;
-import org.gradle.wrapper.IDownload;
-import org.gradle.wrapper.Install;
-import org.gradle.wrapper.Logger;
-import org.gradle.wrapper.PathAssembler;
-import org.gradle.wrapper.WrapperConfiguration;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
-import org.json.simple.parser.ParseException;
-import org.netbeans.api.progress.ProgressHandle;
-import org.netbeans.api.progress.ProgressHandleFactory;
-import org.netbeans.modules.gradle.api.NbGradleProject;
-import org.netbeans.modules.gradle.spi.GradleSettings;
-import org.openide.DialogDescriptor;
-import org.openide.DialogDisplayer;
-import org.openide.awt.Notification;
-import org.openide.awt.NotificationDisplayer;
-import org.openide.util.Exceptions;
-import org.openide.util.NbBundle.Messages;
-import org.openide.util.RequestProcessor;
-
-/**
- *
- * @author Laszlo Kishalmi
- */
-public class GradleDistributionManager {
-
-    private static final Pattern WRAPPER_DIR_PATTERN = Pattern.compile("gradle-(\\d+\\.\\d+.*)-(bin|all)"); //NOI18N
-    private static final Pattern DIST_VERSION_PATTERN = Pattern.compile(".*gradle-(\\d+\\.\\d+.*)-(bin|all)\\.zip"); //NOI18N
-
-    private static final String DOWNLOAD_URI = "https://services.gradle.org/distributions/gradle-%s-bin.zip"; //NOI18N
-
-    private static final RequestProcessor RP = new RequestProcessor("Gradle Installer", 1); //NOI18N
-
-    private static final Set<String> VERSION_BLACKLIST = new HashSet<>(Arrays.asList("2.3", "2.13")); //NOI18N
-    private static final GradleVersion MINIMUM_SUPPORTED_VERSION = GradleVersion.version("2.0"); //NOI18N
-
-    private static final Map<File, GradleDistributionManager> CACHE = new WeakHashMap<>();
-    private static final int JAVA_VERSION;
-
-    static {
-        int ver = 8;
-        String version = System.getProperty("java.specification.version", System.getProperty("java.version")); //NOI18N
-        try {
-            int dot = version.indexOf('.');
-            ver = dot > 0 ? Integer.parseInt(version.substring(0,dot)) : Integer.parseInt(version);
-            if (ver == 1) {
-                version = version.substring(dot + 1);
-                dot = version.indexOf('.');
-                ver = dot > 0 ? Integer.parseInt(version.substring(0,dot)) : Integer.parseInt(version);
-            }
-        } catch (NumberFormatException ex) {
-            Exceptions.printStackTrace(ex);
-        }
-        JAVA_VERSION = ver;
-    }
-
-    final File gradleUserHome;
-    private final Map<URI, NbGradleVersion> versions = new HashMap<>();
-
-    private GradleDistributionManager(File gradleUserHome) {
-        this.gradleUserHome = gradleUserHome;
-    }
-
-    public static GradleDistributionManager get(File gradleUserHome) {
-        GradleDistributionManager ret = CACHE.get(gradleUserHome);
-        if (ret == null) {
-            ret = new GradleDistributionManager(gradleUserHome);
-            CACHE.put(gradleUserHome, ret);
-        }
-        return ret;
-    }
-
-    public NbGradleVersion defaultToolingVersion() {
-        return createVersion(GradleVersion.current().getVersion());
-    }
-
-    public File install(NbGradleVersion version) {
-        File ret = null;
-        if (version.install()) {
-            Lock lock = new ReentrantLock();
-            PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
-                if (NbGradleVersion.PROP_AVAILABLE.equals(evt.getPropertyName())) {
-                    synchronized (lock) {
-                        lock.notifyAll();
-                    }
-                }
-            };
-            try {
-                synchronized (lock) {
-                    version.addPropertyChangeListener(pcl);
-                    lock.wait();
-                }
-            } catch (InterruptedException ex) {
-                return ret;
-            } finally {
-                version.removePropertyChangeListener(pcl);
-            }
-            ret = version.distributionDir();
-        } else {
-            ret = version.distributionDir();
-        }
-        return ret;
-    }
-    /**
-     * Tries to evaluate the project distribution. If wrapper
-     * is preferred and no offline execution is required then it's better to
-     * leave the GradleConnetor use Installation empty, so the requested
-     * distribution will be downloaded.
-     *
-     * @param rootDir the root directory of the root project.
-     * @return a the version used in the wrapper or null if there is no wrapper
-     *         or the version cannot be determined.
-     */
-    public NbGradleVersion evaluateGradleWrapperDistribution(File rootDir) {
-        NbGradleVersion ret = null;
-        File wrapperProps = new File(rootDir, "gradle/wrapper/gradle-wrapper.properties"); //NOI18N
-        if (wrapperProps.isFile() && wrapperProps.canRead()) {
-            Properties wrapper = new Properties();
-            try (FileInputStream is = new FileInputStream(wrapperProps)) {
-                wrapper.load(is);
-            } catch (IOException ex) {
-
-            }
-            String distUrlProp = wrapper.getProperty("distributionUrl"); //NOI18N
-            if (distUrlProp != null) {
-                try {
-                    URI distURL = new URI(distUrlProp);
-                    ret = createVersion(distURL);
-                } catch (URISyntaxException ex) {
-                    // Wrong URL, give up
-                }
-            }
-        }
-        return ret;
-    }
-
-    public List<NbGradleVersion> availableVersions(boolean releaseOnly) {
-        List<NbGradleVersion> ret = new ArrayList<>();
-        JSONParser parser = new JSONParser();
-        try {
-            URL allVersions = new URL("https://services.gradle.org/versions/all"); //NOI18N
-            try (InputStreamReader is = new InputStreamReader(allVersions.openStream(), StandardCharsets.UTF_8)) {
-                JSONArray versions = (JSONArray) parser.parse(is);
-                for (Object o : versions) {
-                    JSONObject v = (JSONObject) o;
-                    URI downloadURL = new URI((String) v.get("downloadUrl")); //NOI18N
-                    boolean snapshot = (Boolean) v.get("snapshot");           //NOI18N
-                    boolean nightly = (Boolean) v.get("nightly");             //NOI18N
-                    boolean broken = (Boolean) v.get("broken");               //NOI18N
-                    String version = (String) v.get("version");               //NOI18N
-                    String rcFor = (String) v.get("rcFor");                   //NOI18N
-                    if (nightly || broken || snapshot) {
-                        continue;
-                    }
-                    if (!rcFor.isEmpty() && releaseOnly) {
-                        continue;
-                    }
-                    if (GradleVersion.version(version).compareTo(MINIMUM_SUPPORTED_VERSION) < 0) {
-                        continue;
-                    }
-                    ret.add(createVersion(version, downloadURL, rcFor.isEmpty()));
-                }
-            } catch (ParseException | IOException | URISyntaxException ex) {
-                //TODO: Shall we do something about this?
-            }
-        } catch (MalformedURLException ex) {
-            //Shall not happen with hardcoded URL
-        }
-        return ret;
-    }
-
-    public List<String> installedVersions(File gradleUserHome) {
-        List<String> ret = new ArrayList<>();
-        File wrapperDir = new File(gradleUserHome, "wrapper/dists"); //NOI18N
-        if (wrapperDir.isDirectory()) {
-            File[] dirs = wrapperDir.listFiles();
-            for (File dir : dirs) {
-                if (dir.isDirectory()) {
-                    Matcher m = WRAPPER_DIR_PATTERN.matcher(dir.getName());
-                    if (m.matches()) {
-                        ret.add(m.group(1));
-                    }
-                }
-            }
-        }
-        return ret;
-    }
-
-    public NbGradleVersion createVersion(URI distributionUrl) {
-        NbGradleVersion ret = null;
-        Matcher m = DIST_VERSION_PATTERN.matcher(distributionUrl.getPath());
-        if (m.matches()) {
-            String version = m.group(1);
-            ret = createVersion(version, distributionUrl, !version.contains("-"));
-        }
-        return ret;
-    }
-
-    public NbGradleVersion createVersion(String version) {
-        try {
-            URI url = new URI(String.format(DOWNLOAD_URI, version));
-            return createVersion(version, url, !version.contains("-"));
-        } catch (URISyntaxException ex) {
-            //Should not really happen.
-        }
-        return null;
-    }
-
-    private NbGradleVersion createVersion(String version, URI url, boolean b) {
-        NbGradleVersion ret = versions.get(url);
-        if (ret == null) {
-            ret = new NbGradleVersion(version, url, b);
-            versions.put(url, ret);
-        }
-        return ret;
-    }
-
-    private static List<File> listDirs(File d) {
-        List<File> ret = new ArrayList<>();
-        if (d.isDirectory()) {
-            for (File f : d.listFiles()) {
-                if (f.isDirectory()) {
-                    ret.add(f);
-                }
-            }
-        }
-        return ret;
-    }
-
-    public final class NbGradleVersion implements Comparable<NbGradleVersion>{
-
-        public static final String PROP_AVAILABLE = "available"; //NOI18N
-        final GradleVersion version;
-        final URI downloadLocation;
-        final boolean release;
-        private PropertyChangeSupport pcs;
-
-        private NbGradleVersion(String version, URI downloadLocation, boolean release) {
-            this.version = GradleVersion.version(version);
-            this.downloadLocation = downloadLocation;
-            this.release = release;
-        }
-
-        public GradleVersion getVersion() {
-            return version;
-        }
-
-        public URI getDownloadLocation() {
-            return downloadLocation;
-        }
-
-        public boolean isRelease() {
-            return release;
-        }
-
-        public boolean isCompatibleWithSystemJava() {
-            return  JAVA_VERSION < 11 ? true : version.compareTo(GradleVersion.version("4.10.2")) >= 0; //NOI18N
-        }
-
-        public boolean isAvailable() {
-            File distDir = distributionDir();
-            return (distDir != null) && distDir.isDirectory();
-        }
-
-        public boolean isBlackListed() {
-            return VERSION_BLACKLIST.contains(version.getVersion());
-        }
-
-        @Messages("TIT_GradleInstall=Install Gradle")
-        public boolean install() {
-            if (!isAvailable()) {
-                if (GradleSettings.getDefault().isSilentInstall()) {
-                    RP.post(new DownloadTask(this), 500);
-                    return true;
-                } else {
-                    GradleInstallPanel panel = new GradleInstallPanel(version.getVersion());
-                    DialogDescriptor dd = new DialogDescriptor(panel,
-                            Bundle.TIT_GradleInstall(),
-                            true,
-                            DialogDescriptor.OK_CANCEL_OPTION,
-                            DialogDescriptor.OK_OPTION,
-                            null
-                    );
-                    if (DialogDisplayer.getDefault().notify(dd) == DialogDescriptor.OK_OPTION) {
-                        GradleSettings.getDefault().setSilentInstall(panel.isSilentInstall());
-                        RP.post(new DownloadTask(this), 500);
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
-
-        public File distributionDir() {
-            File distDir = distributionBaseDir();
-            if (distDir.isDirectory()) {
-                List<File> dirs = listDirs(distDir);
-                assert dirs.size() <= 1 : "Only one directory allowed in distribution dir"; //NOI18N
-                if (!dirs.isEmpty()) {
-                    return dirs.get(0);
-                }
-            }
-            //Try to guess something not too wild here
-            return new File(distDir, "gradle-" + version.getVersion());
-        }
-
-        private File distributionBaseDir() {
-            WrapperConfiguration conf = new WrapperConfiguration();
-            conf.setDistribution(downloadLocation);
-            PathAssembler pa = new PathAssembler(gradleUserHome);
-            PathAssembler.LocalDistribution dist = pa.getDistribution(conf);
-            return dist.getDistributionDir();
-        }
-
-        public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
-            if (pcs == null) {
-                pcs = new PropertyChangeSupport(this);
-            }
-            pcs.addPropertyChangeListener(l);
-        }
-
-        public synchronized void removePropertyChangeListener(PropertyChangeListener l) {
-            if (pcs != null) {
-                pcs.removePropertyChangeListener(l);
-            }
-        }
-
-        void fireVersionAvailable() {
-            if (pcs != null) {
-                pcs.firePropertyChange(PROP_AVAILABLE, null, null);
-            }
-        }
-
-        @Override
-        public int hashCode() {
-            return version.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if ((obj != null) && (obj instanceof NbGradleVersion)) {
-                return version.equals(((NbGradleVersion) obj).version);
-            } else {
-                return false;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return version.getVersion();
-        }
-
-        @Override
-        public int compareTo(NbGradleVersion o) {
-            return version.compareTo(o.getVersion());
-        }
-
-    }
-
-    private class DownloadTask implements Runnable, IDownload {
-
-        private final NbGradleVersion version;
-        private final ProgressHandle handle;
-        private final Notification notification;
-
-        @Messages({
-            "# {0} - The downloading GradleVersion ",
-            "TIT_Download_Gradle=Downloading {0}",
-            "# {0} - The downloading GradleVersion ",
-            "MSG_Download_Gradle={0} is being downloaded and installed."
-        })
-        public DownloadTask(NbGradleVersion version) {
-            this.version = version;
-            handle = ProgressHandleFactory.createSystemHandle(Bundle.TIT_Download_Gradle(version.getVersion()));
-            notification = NotificationDisplayer.getDefault().notify(
-                    Bundle.TIT_Download_Gradle(version.getVersion()),
-                    NbGradleProject.getIcon(),
-                    Bundle.MSG_Download_Gradle(version.getVersion()),
-                    null,
-                    NotificationDisplayer.Priority.NORMAL,
-                    NotificationDisplayer.Category.INFO);
-        }
-
-        @Messages({
-            "# {0} - The downloading GradleVersion ",
-            "TIT_Install_Gradle_Failed=Failed installing {0}",
-        })
-        @Override
-        public void run() {
-            try {
-                WrapperConfiguration conf = new WrapperConfiguration();
-                conf.setDistribution(version.getDownloadLocation());
-                PathAssembler pa = new PathAssembler(gradleUserHome);
-                Install install = new Install(new Logger(true), this, pa);
-                install.createDist(conf);
-                version.fireVersionAvailable();
-            } catch (Exception ex) {
-                //Happens if something goes wrong with the download.
-                //TODO: Is it ok to let id silently die?
-                NotificationDisplayer.getDefault().notify(
-                        Bundle.TIT_Install_Gradle_Failed(version.getVersion()),
-                        NbGradleProject.getWarningIcon(),
-                        ex.getLocalizedMessage(),
-                        null,
-                        NotificationDisplayer.Priority.HIGH,
-                        NotificationDisplayer.Category.WARNING);
-            } finally {
-                handle.finish();
-                notification.clear();
-            }
-        }
-
-        @Override
-        public void download(URI uri, File file) throws Exception {
-            URL url = uri.toURL();
-            URLConnection conn = url.openConnection();
-            byte[] buf = new byte[8192];
-            try (FileOutputStream os = new FileOutputStream(file)) {
-                conn.connect();
-                int size = conn.getContentLength();
-                if (size > 0) {
-                    handle.start(size);
-                } else {
-                    handle.start();
-                }
-                int allRead = 0;
-                int read;
-                InputStream is = url.openStream();
-                while ((read = is.read(buf)) > 0) {
-                    os.write(buf, 0, read);
-                    allRead += read;
-                    if (size > 0) {
-                        handle.progress(allRead);
-                    }
-                }
-            }
-        }
-
-    }
-}
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/GradleInstallPanel.form b/extide/gradle/src/org/netbeans/modules/gradle/GradleInstallPanel.form
deleted file mode 100644
index 001b24d..0000000
--- a/extide/gradle/src/org/netbeans/modules/gradle/GradleInstallPanel.form
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-
-    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.
-
--->
-
-<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
-    <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"/>
-  </AuxValues>
-
-  <Layout>
-    <DimensionLayout dim="0">
-      <Group type="103" groupAlignment="0" attributes="0">
-          <Group type="102" attributes="0">
-              <EmptySpace max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Component id="lbInfo" alignment="0" pref="348" max="32767" attributes="0"/>
-                  <Group type="102" alignment="1" attributes="0">
-                      <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
-                      <Component id="cbSilentInstall" min="-2" max="-2" attributes="0"/>
-                  </Group>
-              </Group>
-              <EmptySpace max="-2" attributes="0"/>
-          </Group>
-      </Group>
-    </DimensionLayout>
-    <DimensionLayout dim="1">
-      <Group type="103" groupAlignment="0" attributes="0">
-          <Group type="102" alignment="1" attributes="0">
-              <EmptySpace max="-2" attributes="0"/>
-              <Component id="lbInfo" pref="77" max="32767" attributes="0"/>
-              <EmptySpace max="-2" attributes="0"/>
-              <Component id="cbSilentInstall" min="-2" max="-2" attributes="0"/>
-              <EmptySpace max="-2" attributes="0"/>
-          </Group>
-      </Group>
-    </DimensionLayout>
-  </Layout>
-  <SubComponents>
-    <Component class="javax.swing.JCheckBox" name="cbSilentInstall">
-      <Properties>
-        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-          <ResourceString bundle="org/netbeans/modules/gradle/Bundle.properties" key="GradleInstallPanel.cbSilentInstall.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-        </Property>
-      </Properties>
-    </Component>
-    <Component class="javax.swing.JLabel" name="lbInfo">
-      <Properties>
-        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-          <ResourceString bundle="org/netbeans/modules/gradle/Bundle.properties" key="GradleInstallPanel.lbInfo.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-        </Property>
-        <Property name="verticalAlignment" type="int" value="1"/>
-      </Properties>
-    </Component>
-  </SubComponents>
-</Form>
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/GradleInstallPanel.java b/extide/gradle/src/org/netbeans/modules/gradle/GradleInstallPanel.java
deleted file mode 100644
index dd60d77..0000000
--- a/extide/gradle/src/org/netbeans/modules/gradle/GradleInstallPanel.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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;
-
-import org.netbeans.modules.gradle.spi.GradleSettings;
-import org.openide.util.NbBundle.Messages;
-
-/**
- *
- * @author lkishalmi
- */
-@Messages({
-        "# {0} - Gradle Version",
-        "MSG_Install=<html>It seems the required Gradle Version <b>{0}</b> " +
-        "is not installed in the system. Would you like to install it?"
-})
-public class GradleInstallPanel extends javax.swing.JPanel {
-
-    /**
-     * Creates new form GradleInstallPanel
-     */
-    public GradleInstallPanel(String version) {
-        initComponents();
-        lbInfo.setText(Bundle.MSG_Install(version));
-        cbSilentInstall.setSelected(GradleSettings.getDefault().isSilentInstall());
-    }
-
-    public boolean isSilentInstall() {
-        return cbSilentInstall.isSelected();
-    }
-
-    /**
-     * This method is called from within the constructor to initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is always
-     * regenerated by the Form Editor.
-     */
-    @SuppressWarnings("unchecked")
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-
-        cbSilentInstall = new javax.swing.JCheckBox();
-        lbInfo = new javax.swing.JLabel();
-
-        org.openide.awt.Mnemonics.setLocalizedText(cbSilentInstall, org.openide.util.NbBundle.getMessage(GradleInstallPanel.class, "GradleInstallPanel.cbSilentInstall.text")); // NOI18N
-
-        org.openide.awt.Mnemonics.setLocalizedText(lbInfo, org.openide.util.NbBundle.getMessage(GradleInstallPanel.class, "GradleInstallPanel.lbInfo.text")); // NOI18N
-        lbInfo.setVerticalAlignment(javax.swing.SwingConstants.TOP);
-
-        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
-        this.setLayout(layout);
-        layout.setHorizontalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGroup(layout.createSequentialGroup()
-                .addContainerGap()
-                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                    .addComponent(lbInfo, javax.swing.GroupLayout.DEFAULT_SIZE, 348, Short.MAX_VALUE)
-                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
-                        .addGap(0, 0, Short.MAX_VALUE)
-                        .addComponent(cbSilentInstall)))
-                .addContainerGap())
-        );
-        layout.setVerticalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
-                .addContainerGap()
-                .addComponent(lbInfo, javax.swing.GroupLayout.DEFAULT_SIZE, 77, Short.MAX_VALUE)
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addComponent(cbSilentInstall)
-                .addContainerGap())
-        );
-    }// </editor-fold>//GEN-END:initComponents
-
-
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JCheckBox cbSilentInstall;
-    private javax.swing.JLabel lbInfo;
-    // End of variables declaration//GEN-END:variables
-}
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java b/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java
index 7cdf3c7..b02ba62 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectCache.java
@@ -163,23 +163,7 @@ public final class GradleProjectCache {
         NbProjectInfo info = null;
         Quality quality = ctx.aim;
         GradleBaseProject base = ctx.previous.getBaseProject();
-        /*
-        GradleConnector gconn = GradleConnector.newConnector();
 
-        File gradleInstall = RunUtils.evaluateGradleDistribution(ctx.project, true);
-        if (gradleInstall == null) {
-            GradleDistributionManager gdm = GradleDistributionManager.get(GradleSettings.getDefault().getGradleUserHome());
-            GradleDistributionManager.NbGradleVersion version = gdm.createVersion(GradleSettings.getDefault().getGradleVersion());
-            gradleInstall = gdm.install(version);
-        }
-        if (gradleInstall == null) {
-            return ctx.previous;
-        }
-        gconn.useInstallation(gradleInstall);
-        gconn.useGradleUserHomeDir(GradleSettings.getDefault().getGradleUserHome());
-
-        ProjectConnection pconn = gconn.forProjectDirectory(base.getProjectDir()).connect();
-        */
         ProjectConnection pconn = ctx.project.getLookup().lookup(ProjectConnection.class);
 
 
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectConnection.java b/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectConnection.java
index e4f9409..6a97395 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectConnection.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectConnection.java
@@ -34,6 +34,8 @@ import org.gradle.tooling.ResultHandler;
 import org.gradle.tooling.TestLauncher;
 import org.netbeans.api.project.Project;
 import org.netbeans.modules.gradle.api.NbGradleProject;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 import org.netbeans.modules.gradle.spi.execute.GradleDistributionProvider;
 import org.netbeans.spi.project.ProjectServiceProvider;
 import org.openide.filesystems.FileUtil;
@@ -114,25 +116,35 @@ public class GradleProjectConnection implements ProjectConnection {
             File projectDir = FileUtil.toFile(project.getProjectDirectory());
             GradleConnector gconn = GradleConnector.newConnector();
             GradleDistributionProvider pvd = project.getLookup().lookup(GradleDistributionProvider.class);
-            GradleDistributionProvider.Result res = pvd != null ? pvd.getGradleDistribution() : null;
-            if (res != null) {
-
-                File gradleHome = res.getGradleHome();
-                gconn = gradleHome != null ? gconn.useGradleUserHomeDir(gradleHome) : gconn;
-                File gradleInstall = res.getGradleInstall();
-                conn = (gradleInstall != null ? gconn.useInstallation(gradleInstall) : gconn).forProjectDirectory(projectDir).connect();
-                if (!res.isCompatibleWithSystemJava()) {
-                    gradleInstall = GradleDistributionManager.get(gradleHome).defaultToolingVersion().distributionDir();
-                    compatConn = gconn.useInstallation(gradleInstall).forProjectDirectory(gradleHome).connect();
-                } else {
-                    compatConn = conn;
+            if (pvd != null) {
+                pvd.addChangeListener(WeakListeners.change(listener, null));
+                GradleDistribution dist = pvd.getGradleDistribution();
+                if (dist != null) {
+                    conn = createConnection(dist, projectDir);
+                    if (dist.isCompatibleWithSystemJava()) {
+                        compatConn = conn;
+                    } else {
+                        GradleDistribution compatDist = GradleDistributionManager.get(dist.getGradleUserHome()).defaultDistribution();
+                        compatConn = createConnection(compatDist, projectDir);
+                    }
                 }
-                res.addChangeListener(WeakListeners.change(listener, res));
-            } else {
+            }
+            if (conn == null) {
                 conn = gconn.forProjectDirectory(projectDir).connect();
                 compatConn = conn;
             }
         }
         return compatible ? compatConn : conn;
     }
+
+    private static ProjectConnection createConnection(GradleDistribution dist, File projectDir) {
+        GradleConnector gconn = GradleConnector.newConnector();
+        gconn = gconn.useGradleUserHomeDir(dist.getGradleUserHome());
+        if (dist.isAvailable()) {
+            gconn = gconn.useInstallation(dist.getDistributionDir());
+        } else {
+            gconn = gconn.useDistribution(dist.getDistributionURI());
+        }
+        return gconn.forProjectDirectory(projectDir).connect();
+    }
 }
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java
new file mode 100644
index 0000000..c72190b
--- /dev/null
+++ b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/GradleDistributionManager.java
@@ -0,0 +1,513 @@
+/*
+ * 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.api.execute;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.jar.JarFile;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.gradle.util.GradleVersion;
+import org.gradle.wrapper.IDownload;
+import org.gradle.wrapper.Install;
+import org.gradle.wrapper.Logger;
+import org.gradle.wrapper.PathAssembler;
+import org.gradle.wrapper.WrapperConfiguration;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.progress.ProgressHandleFactory;
+import org.netbeans.modules.gradle.api.NbGradleProject;
+import org.openide.awt.Notification;
+import org.openide.awt.NotificationDisplayer;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+
+/**
+ * This class helps organizing the {@link GradleDistribution}-s used in the IDE.
+ *
+ * @since 2.4
+ * @author lkishalmi
+ */
+public final class GradleDistributionManager {
+
+    private static final RequestProcessor RP = new RequestProcessor("Gradle Installer", 1); //NOI18N
+
+    private static final String DOWNLOAD_URI = "https://services.gradle.org/distributions/gradle-%s-%s.zip"; //NOI18N
+    private static final Pattern DIST_VERSION_PATTERN = Pattern.compile(".*gradle-(\\d+\\.\\d+.*)-(bin|all)\\.zip"); //NOI18N
+    private static final Set<String> VERSION_BLACKLIST = new HashSet<>(Arrays.asList("2.3", "2.13")); //NOI18N
+    private static final Map<File, GradleDistributionManager> CACHE = new WeakHashMap<>();
+    private static final GradleVersion MINIMUM_SUPPORTED_VERSION = GradleVersion.version("2.0"); //NOI18N
+    private static final GradleVersion[] JDK_COMPAT = new GradleVersion[]{
+        GradleVersion.version("4.2.1"), // JDK-9
+        GradleVersion.version("4.7"), // JDK-10
+        GradleVersion.version("4.10.2"), // JDK-11
+        GradleVersion.version("5.4"), // JDK-12
+        GradleVersion.version("6.0"), // JDK-13
+        GradleVersion.version("6.3"), // JDK-14
+    };
+    private static final int JAVA_VERSION;
+
+    static {
+        int ver = 8;
+        String version = System.getProperty("java.specification.version", System.getProperty("java.version")); //NOI18N
+        try {
+            int dot = version.indexOf('.');
+            ver = dot > 0 ? Integer.parseInt(version.substring(0, dot)) : Integer.parseInt(version);
+            if (ver == 1) {
+                version = version.substring(dot + 1);
+                dot = version.indexOf('.');
+                ver = dot > 0 ? Integer.parseInt(version.substring(0, dot)) : Integer.parseInt(version);
+            }
+        } catch (NumberFormatException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        JAVA_VERSION = ver;
+    }
+
+    final File gradleUserHome;
+
+    private GradleDistributionManager(File gradleUserHome) {
+        this.gradleUserHome = gradleUserHome;
+    }
+
+    /**
+     * Return a {@link GradleDistributionManager} for the given Gradle user
+     * home.
+     *
+     * @param gradleUserHome
+     * @return
+     */
+    public static GradleDistributionManager get(File gradleUserHome) {
+        GradleDistributionManager ret = CACHE.get(gradleUserHome);
+        if (ret == null) {
+            ret = new GradleDistributionManager(gradleUserHome);
+            CACHE.put(gradleUserHome, ret);
+        }
+        return ret;
+    }
+
+    /**
+     * Create a {@link GradleDistribution} from a manually downloaded and
+     * unpacked directory.
+     *
+     * @param distDir the directory where Gradle has been installed manually
+     * @return the created GradleDistribution object.
+     * @throws IOException when the provided directory does not seem to be a
+     * Gradle distribution.
+     */
+    public GradleDistribution distributionFromDir(File distDir) throws IOException {
+        File lib = new File(distDir, "lib"); //NO18N
+        File[] gradleLauncher = lib.listFiles((dir, name) -> {
+            return name.startsWith("gradle-launcher-") && name.endsWith(".jar"); //NOI18N
+        });
+        if (gradleLauncher.length != 1) {
+            throw new FileNotFoundException(lib.getAbsolutePath() + "lib/gradle-launcher-xxxx.jar not found or ambigous!"); //NOI18N
+        }
+        JarFile launcherJar = new JarFile(gradleLauncher[0]);
+        String version = launcherJar.getManifest().getMainAttributes().getValue("Implementation-Version"); //NOI18N
+        return new GradleDistribution(distDir, null, version);
+    }
+
+    /**
+     * Create a {@link GradleDistribution} from a simple version string like
+     * <code>"6.3"</code>. The returned distribution might be not available but
+     * it can be downloaded and installed with the
+     * {@link GradleDistribution#install()} method. This method sets the
+     * standard download URI, if the <code>withSources</code> is set to
+     * <code>false</code> then the binary only URI would be set.
+     *
+     * @param version the Gradle version
+     * @param withSources choose between the 'all' and 'bin' distribution
+     * @return the created GradleDistribution object.
+     */
+    public GradleDistribution distributionFromVersion(String version, boolean withSources) {
+        try {
+            URI uri = new URI(String.format(DOWNLOAD_URI, version, withSources ? "all" : "bin")); //NOI18N
+            return new GradleDistribution(distributionBaseDir(uri, version), uri, version);
+        } catch (URISyntaxException ex) {
+            //This shall not happen;
+            return null;
+        }
+    }
+
+    /**
+     * Create a {@link GradleDistribution} from a simple version string like
+     * <code>"6.3"</code>. The returned distribution might be not available but
+     * it can be downloaded and installed with the
+     * {@link GradleDistribution#install()} method. This method sets the
+     * standard download URI for the binary distribution package (no source code
+     * attached).
+     *
+     * @param version the Gradle version
+     * @return the created GradleDistribution object.
+     */
+    public GradleDistribution distributionFromVersion(String version) {
+        return distributionFromVersion(version, false);
+    }
+
+    /**
+     * Create a {@link GradleDistribution} from a Gradle root project directory
+     * which contains a Gradle wrapper. The wrapper properties file is expected
+     * to be at <code>gradle/wrapper/gradle-wrapper.properties</code>.
+     *
+     * @param gradleProjectRoot the directory of the root Gradle project.
+     * @return the created GradleDistribution object.
+     * @throws IOException if the wrapper properties file not found or cannot be
+     *         read for some reason.
+     * @throws URISyntaxException when the <code>distributionUrl</code>
+     *         property is missing, has URI syntax problem or the version of
+     *         the Gradle distribution cannot be determined form it.
+     */
+    public GradleDistribution distributionFromWrapper(File gradleProjectRoot) throws IOException, URISyntaxException {
+        File wrapperProps = new File(gradleProjectRoot, "gradle/wrapper/gradle-wrapper.properties"); //NOI18N
+        if (wrapperProps.isFile() && wrapperProps.canRead()) {
+            Properties wrapper = new Properties();
+            try (FileInputStream is = new FileInputStream(wrapperProps)) {
+                wrapper.load(is);
+            } catch (IOException ex) {
+                throw ex;
+            }
+            String distUrlProp = wrapper.getProperty("distributionUrl"); //NOI18N
+            if (distUrlProp != null) {
+                URI uri = new URI(distUrlProp);
+                Matcher m = DIST_VERSION_PATTERN.matcher(distUrlProp);
+                if (m.matches()) {
+                    String version = m.group(1);
+                    return new GradleDistribution(distributionBaseDir(uri, version), uri, version);
+                } else {
+                    throw new URISyntaxException(distUrlProp, "Cannot get the Gradle distribution version from the URI"); //NOI18N
+                }
+            } else {
+                throw new URISyntaxException("", "No distributionUrl property found in: " + wrapperProps.getAbsolutePath()); //NOI18N
+            }
+        } else {
+            throw new FileNotFoundException("Gradle Wrapper properties not found at: " + wrapperProps.getAbsolutePath()); //NOI18N
+        }
+    }
+
+    /**
+     * Create a {@link GradleDistribution} from the Gradle version distributed
+     * with the Gradle Tooling of the IDE. This should be the most IDE compatible
+     * version, so it can be used as a fallback.
+     *
+     * @return the default Gradle distribution matches the IDE Gradle tooling.
+     */
+    public GradleDistribution defaultDistribution() {
+        return distributionFromVersion(GradleVersion.current().getVersion());
+    }
+
+    /**
+     * Lists all the {@link GradleDistribution}s available on the Gradle site and
+     * supported by the IDE. This method uses the
+     * <a href="https://services.gradle.org/versions/all">https://services.gradle.org/versions/all</a>
+     * web service to download the list of available versions.
+     * @param releaseOnly list only the released versions
+     *                    (release candidates and milestones included).
+     * @return the list of available Gradle distributions from the Gradle site.
+     * @throws IOException if downloading the list would fail.
+     */
+    public List<GradleDistribution> availableDistributions(boolean releaseOnly) throws IOException {
+        List<GradleDistribution> ret = new ArrayList<>();
+        JSONParser parser = new JSONParser();
+        try {
+            URL allVersions = new URL("https://services.gradle.org/versions/all"); //NOI18N
+            try (InputStreamReader is = new InputStreamReader(allVersions.openStream(), StandardCharsets.UTF_8)) {
+                JSONArray versions = (JSONArray) parser.parse(is);
+                for (Object o : versions) {
+                    JSONObject v = (JSONObject) o;
+                    URI downloadURL = new URI((String) v.get("downloadUrl")); //NOI18N
+                    boolean snapshot = (Boolean) v.get("snapshot");           //NOI18N
+                    boolean nightly = (Boolean) v.get("nightly");             //NOI18N
+                    boolean broken = (Boolean) v.get("broken");               //NOI18N
+                    String version = (String) v.get("version");               //NOI18N
+                    String rcFor = (String) v.get("rcFor");                   //NOI18N
+                    if (nightly || broken || snapshot) {
+                        continue;
+                    }
+                    if (!rcFor.isEmpty() && releaseOnly) {
+                        continue;
+                    }
+                    if (GradleVersion.version(version).compareTo(MINIMUM_SUPPORTED_VERSION) < 0) {
+                        continue;
+                    }
+                    ret.add(new GradleDistribution(distributionBaseDir(downloadURL, version), downloadURL, version));
+                }
+            } catch (ParseException | URISyntaxException ex) {
+                //TODO: Shall we do something about this?
+            } catch (IOException iex) {
+                throw iex;
+            }
+        } catch (MalformedURLException ex) {
+            //Shall not happen with hardcoded URL
+        }
+        return ret;
+    }
+
+    File distributionBaseDir(URI downloadLocation, String version) {
+        WrapperConfiguration conf = new WrapperConfiguration();
+        conf.setDistribution(downloadLocation);
+        PathAssembler pa = new PathAssembler(gradleUserHome);
+        PathAssembler.LocalDistribution dist = pa.getDistribution(conf);
+        return new File(dist.getDistributionDir(), "gradle-" + version);
+    }
+
+    /**
+     * This object represents a Gradle distribution in NetBeans combining the
+     * following four attributes:
+     * <ul>
+     *   <li>Gradle user home</li>
+     *   <li>Distribution directory</li>
+     *   <li>Gradle version</li>
+     *   <li>Gradle distribution URI</li>
+     * </ul>
+     */
+    public final class GradleDistribution implements Comparable<GradleDistribution> {
+
+        final File distributionDir;
+        final URI distributionURI;
+        final GradleVersion version;
+
+        private GradleDistribution(File distributionDir, URI distributionURL, String version) {
+            this.distributionDir = distributionDir;
+            this.distributionURI = distributionURL;
+            this.version = GradleVersion.version(version);
+        }
+
+        /**
+         * The Gradle user home directory of this distribution. Inherited through
+         * the {@link GradleDistributionManager}.
+         * @return the Gradle user home directory of this distribution
+         */
+        public File getGradleUserHome() {
+            return gradleUserHome;
+        }
+
+        /**
+         * The Gradle distribution directory. May or may not exists yet.
+         * If it does not exist then the {@link #getDistributionURI()} must
+         * return a valid URI to the distribution source.
+         *
+         * @return the Gradle distribution directory
+         */
+        public File getDistributionDir() {
+            return distributionDir;
+        }
+
+        /**
+         * The Gradle distribution URI. It may return <code>null</code> if this
+         * is a manually installed distribution. In that case the
+         * {@link #getDistributionDir()} shall return an existing distribution
+         * directory.
+         *
+         * @return the Gradle distribution URI
+         */
+        public URI getDistributionURI() {
+            return distributionURI;
+        }
+
+        /**
+         * The Gradle version of this distribution.
+         * @return the Gradle version of this distribution
+         */
+        public String getVersion() {
+            return version.getVersion();
+        }
+
+        /**
+         * Checks if this Gradle distribution is compatible with the given
+         * major version of Java. Java 1.6, 1.7 and 1.8 are treated as major
+         * version 6, 7, and 8.
+         *
+         * @param jdkMajorVersion the major version of the JDK
+         * @return <code>true</code> if this version is supported with that JDK.
+         */
+        public boolean isCompatibleWithJava(int jdkMajorVersion) {
+            int i = JDK_COMPAT.length - 1;
+            while ((i >= 0) && version.compareTo(JDK_COMPAT[i]) < 0) {
+                i--;
+            }
+            int maxSupportedJDK = i + 9;
+            return jdkMajorVersion <= maxSupportedJDK;
+        }
+
+        /**
+         * Checks if this Gradle distribution is compatible the NetBeans
+         * runtime JDK.
+         *
+         * @return <code>true</code> if this version is supported with the runtime JDK.
+         */
+        public boolean isCompatibleWithSystemJava() {
+            return isCompatibleWithJava(JAVA_VERSION);
+        }
+
+        /**
+         * Checks if this distribution is downloaded and available to use.
+         * Well the current check is based on if the distribution directory
+         * exists or not.
+         *
+         * @return if this distribution is available for use.
+         */
+        public boolean isAvailable() {
+            return distributionDir.isDirectory();
+        }
+
+        /**
+         * Checks if this distribution has known issues to be used with NetBeans.
+         * While these versions can be used, their usage are not recommended..
+         *
+         * @return if this distribution has compatibility issues with NetBeans.
+         */
+        public boolean isBlackListed() {
+            return VERSION_BLACKLIST.contains(version.getVersion());
+        }
+
+        /**
+         * Start to download and install this distribution from its
+         * distribution URI to its distribution directory asynchronous. It returns
+         * <code>null</code> if this distribution is available.
+         * <p>
+         * The install task has UI bindings in form of a IDE progress bar and 
+         * notification entry on the downloading of the distribution.
+         *
+         * @return the {@link Future} of the install task or <code>null</code>
+         *         if this distribution is already installed.
+         */
+        public Future<Void> install() {
+            return isAvailable() ? null : RP.schedule(new DownloadTask(this), 500, TimeUnit.MILLISECONDS);
+        }
+
+        @Override
+        public int compareTo(GradleDistribution o) {
+            return version.compareTo(o.version);
+        }
+
+        @Override
+        public String toString() {
+            return "GradleDistribution{" + "distributionDir=" + distributionDir + ", distributionURI=" + distributionURI + ", version=" + version + '}';
+        }
+
+    }
+
+    private class DownloadTask implements Callable<Void>, IDownload {
+
+        private final GradleDistribution dist;
+        private final ProgressHandle handle;
+        private final Notification notification;
+
+        @NbBundle.Messages({
+            "# {0} - The downloading GradleVersion ",
+            "TIT_Download_Gradle=Downloading {0}",
+            "# {0} - The downloading GradleVersion ",
+            "MSG_Download_Gradle=Gradle {0} is being downloaded and installed."
+        })
+        public DownloadTask(GradleDistribution dist) {
+            this.dist = dist;
+            handle = ProgressHandleFactory.createSystemHandle(Bundle.TIT_Download_Gradle(dist.getVersion()));
+            notification = NotificationDisplayer.getDefault().notify(
+                    Bundle.TIT_Download_Gradle(dist.getVersion()),
+                    NbGradleProject.getIcon(),
+                    Bundle.MSG_Download_Gradle(dist.getVersion()),
+                    null,
+                    NotificationDisplayer.Priority.NORMAL,
+                    NotificationDisplayer.Category.INFO);
+        }
+
+        @NbBundle.Messages({
+            "# {0} - The downloading GradleVersion ",
+            "TIT_Install_Gradle_Failed=Failed installing Gradle {0}",})
+        @Override
+        public Void call() throws Exception {
+            try {
+                WrapperConfiguration conf = new WrapperConfiguration();
+                conf.setDistribution(dist.getDistributionURI());
+                PathAssembler pa = new PathAssembler(gradleUserHome);
+                Install install = new Install(new Logger(true), this, pa);
+                install.createDist(conf);
+            } catch (Exception ex) {
+                //Happens if something goes wrong with the download.
+                //TODO: Is it ok to let id silently die?
+                NotificationDisplayer.getDefault().notify(
+                        Bundle.TIT_Install_Gradle_Failed(dist.getVersion()),
+                        NbGradleProject.getWarningIcon(),
+                        ex.getLocalizedMessage(),
+                        null,
+                        NotificationDisplayer.Priority.HIGH,
+                        NotificationDisplayer.Category.WARNING);
+                throw ex;
+            } finally {
+                handle.finish();
+                notification.clear();
+            }
+            return null;
+        }
+
+        @Override
+        public void download(URI uri, File file) throws Exception {
+            URL url = uri.toURL();
+            URLConnection conn = url.openConnection();
+            byte[] buf = new byte[8192];
+            try (FileOutputStream os = new FileOutputStream(file)) {
+                conn.connect();
+                int size = conn.getContentLength();
+                if (size > 0) {
+                    handle.start(size);
+                } else {
+                    handle.start();
+                }
+                int allRead = 0;
+                int read;
+                InputStream is = url.openStream();
+                while ((read = is.read(buf)) > 0) {
+                    os.write(buf, 0, read);
+                    allRead += read;
+                    if (size > 0) {
+                        handle.progress(allRead);
+                    }
+                }
+            }
+        }
+
+    }
+}
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java
index 0b35500..c275c86 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/api/execute/RunUtils.java
@@ -51,16 +51,16 @@ import java.util.Set;
 import java.util.WeakHashMap;
 
 import org.netbeans.api.project.ui.OpenProjects;
-import org.netbeans.modules.gradle.GradleDistributionManager;
 import org.netbeans.modules.gradle.ProjectTrust;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 import org.netbeans.modules.gradle.api.execute.RunConfig.ExecFlag;
 import org.netbeans.modules.gradle.execute.TrustProjectPanel;
 import org.netbeans.modules.gradle.spi.GradleSettings;
+import org.netbeans.modules.gradle.spi.execute.GradleDistributionProvider;
 import org.netbeans.spi.project.SingleMethod;
 import org.openide.DialogDescriptor;
 import org.openide.DialogDisplayer;
 import org.openide.filesystems.FileObject;
-import org.openide.filesystems.FileUtil;
 import org.openide.loaders.DataObject;
 import org.openide.util.NbBundle.Messages;
 import org.openide.util.Pair;
@@ -275,40 +275,18 @@ public final class RunUtils {
         return args != null ? new GradleCommandLine(args) : null;
     }
 
+    @Deprecated
     public static File evaluateGradleDistribution(Project project, boolean forceCompatibility) {
-        File ret = null;
-
-        GradleSettings settings = GradleSettings.getDefault();
-        GradleDistributionManager mgr = GradleDistributionManager.get(settings.getGradleUserHome());
-
-        GradleBaseProject gbp = GradleBaseProject.get(project);
-
-        if ((gbp != null) && settings.isWrapperPreferred()) {
-            GradleDistributionManager.NbGradleVersion ngv = mgr.evaluateGradleWrapperDistribution(gbp.getRootDir());
-            if ( (ngv != null) && forceCompatibility && !ngv.isCompatibleWithSystemJava()) {
-                ngv = mgr.defaultToolingVersion();
-            }
-            if ((ngv != null) && ngv.isAvailable()) {
-                ret = ngv.distributionDir();
-            }
-        }
 
-        if ((ret == null) && settings.useCustomGradle() && !settings.getDistributionHome().isEmpty()) {
-            File f = FileUtil.normalizeFile(new File(settings.getDistributionHome()));
-            if (f.isDirectory()) {
-                ret = f;
-            }
-        }
-        if (ret == null) {
-            GradleDistributionManager.NbGradleVersion ngv = mgr.createVersion(settings.getGradleVersion());
-            if ( (ngv != null) && forceCompatibility && !ngv.isCompatibleWithSystemJava()) {
-                ngv = mgr.defaultToolingVersion();
-            }
-            if ((ngv != null) && ngv.isAvailable()) {
-                ret = ngv.distributionDir();
-            }
+        GradleDistributionProvider pvd = project != null ? project.getLookup().lookup(GradleDistributionProvider.class) : null;
+        GradleDistribution dist = pvd != null ? pvd.getGradleDistribution() : null;
+        if (dist != null && (dist.isCompatibleWithSystemJava() || !forceCompatibility)) {
+            return dist.getDistributionDir();
+        } else {
+            GradleSettings settings = GradleSettings.getDefault();
+            dist = GradleDistributionManager.get(dist != null ? dist.getGradleUserHome() : settings.getGradleUserHome()).defaultDistribution();
+            return dist.getDistributionDir();
         }
-        return ret;
     }
 
     private static boolean isOptionEnabled(Project project, String option, boolean defaultValue) {
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 260008f..51a7645 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDaemonExecutor.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDaemonExecutor.java
@@ -37,6 +37,7 @@ import java.nio.file.Path;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.swing.AbstractAction;
@@ -53,13 +54,16 @@ import org.netbeans.api.progress.ProgressHandle;
 import org.netbeans.api.progress.ProgressHandleFactory;
 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.spi.GradleFiles;
+import org.netbeans.modules.gradle.spi.execute.GradleDistributionProvider;
 import org.netbeans.modules.gradle.spi.execute.GradleJavaPlatformProvider;
 import org.netbeans.spi.project.ui.support.BuildExecutionSupport;
 import org.openide.awt.StatusDisplayer;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.NbBundle;
 import org.openide.util.NbBundle.Messages;
+import org.openide.util.Utilities;
 import org.openide.util.io.ReaderInputStream;
 import org.openide.windows.IOColorPrint;
 import org.openide.windows.IOColors;
@@ -99,7 +103,12 @@ public final class GradleDaemonExecutor extends AbstractGradleExecutor {
         "BUILD_FAILED=Building {0} failed.",
         "# {0} - Platform Key",
         "NO_PLATFORM=No valid Java Platform found for key: ''{0}''",
-        "GRADLE_IO_ERROR=Gradle internal IO problem has been detected.\nThe running build may or may not have finished succesfully."
+        "GRADLE_IO_ERROR=Gradle internal IO problem has been detected.\nThe running build may or may not have finished succesfully.",
+        "# {0} - Gradle Version",
+        "DOWNLOAD_GRADLE=Downloading Gradle {0}...",
+        "# {0} - Gradle Version",
+        "# {1} - Gradle Distribution URI",
+        "DOWNLOAD_GRADLE_FAILED=Failed Downloading Gradle {0} from {1}",
     })
     @Override
     public void run() {
@@ -123,23 +132,24 @@ public final class GradleDaemonExecutor extends AbstractGradleExecutor {
                 ioput.select();
             }
             cancelTokenSource = GradleConnector.newCancellationTokenSource();
-            /*
-            GradleConnector gconn = GradleConnector.newConnector();
-            File gradleInstall = RunUtils.evaluateGradleDistribution(config.getProject(), false);
-            if (gradleInstall != null) {
-                gconn.useInstallation(gradleInstall);
-            } else {
-                gconn.useBuildDistribution();
-            }
-
-            gconn.useGradleUserHomeDir(GradleSettings.getDefault().getGradleUserHome());
 
-            File projectDir = FileUtil.toFile(config.getProject().getProjectDirectory());
-            pconn = gconn.forProjectDirectory(projectDir).connect();
-            */
+            GradleDistributionProvider distProvider = config.getProject().getLookup().lookup(GradleDistributionProvider.class);
+            GradleDistribution dist = distProvider != null ? distProvider.getGradleDistribution() : null;
+            if ((dist != null) && !dist.isAvailable()) {
+                try {
+                    IOColorPrint.print(io, Bundle.DOWNLOAD_GRADLE(dist.getVersion()) + "\n",IOColors.getColor(io, IOColors.OutputType.LOG_WARNING));
+                    try {
+                        dist.install().get();
+                    } catch(InterruptedException | ExecutionException ex) {
+                        IOColorPrint.print(io, Bundle.DOWNLOAD_GRADLE_FAILED(dist.getVersion(), dist.getDistributionURI()),IOColors.getColor(io, IOColors.OutputType.LOG_FAILURE));
+                        throw new BuildException(Bundle.DOWNLOAD_GRADLE_FAILED(dist.getVersion(), dist.getDistributionURI()), ex);
+                    }
+                } catch (IOException ex) {}
+            }
             ProjectConnection pconn = config.getProject().getLookup().lookup(ProjectConnection.class);
 
             BuildLauncher buildLauncher = pconn.newBuild();
+
             GradleCommandLine cmd = config.getCommandLine();
             if (RunUtils.isAugmentedBuildEnabled(config.getProject())) {
                 cmd = new GradleCommandLine(cmd);
@@ -255,11 +265,12 @@ public final class GradleDaemonExecutor extends AbstractGradleExecutor {
 
                 String relRoot = projectPath.relativize(rootPath).toString();
                 relRoot = relRoot.isEmpty() ? "." : relRoot;
-                commandLine.append(relRoot).append("/gradlew"); //NOI18N
+                commandLine.append(relRoot).append(gradlewExecutable());
             } else {
-                File gradleDistribution = RunUtils.evaluateGradleDistribution(null, false);
-                if (gradleDistribution != null) {
-                    File gradle = new File(gradleDistribution, "bin/gradle"); //NOI18N
+                GradleDistributionProvider pvd = config.getProject().getLookup().lookup(GradleDistributionProvider.class);
+                GradleDistribution dist = pvd != null ? pvd.getGradleDistribution() : null;
+                if (dist != null) {
+                    File gradle = new File(dist.getDistributionDir(), gradleExecutable());
                     commandLine.append(gradle.getAbsolutePath());
                 }
             }
@@ -313,4 +324,11 @@ public final class GradleDaemonExecutor extends AbstractGradleExecutor {
         return false;
     }
 
+    private static String gradlewExecutable() {
+        return Utilities.isWindows() ? "\\gradlew.bat" : "/gradlew"; //NOI18N
+    }
+
+    private static String gradleExecutable() {
+        return Utilities.isWindows() ? "bin\\gradle.bat" : "bin/gradle"; //NOI18N
+    }
 }
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDistributionProviderImpl.java b/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDistributionProviderImpl.java
index 243e10b..8f9d5af 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDistributionProviderImpl.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/execute/GradleDistributionProviderImpl.java
@@ -19,15 +19,19 @@
 package org.netbeans.modules.gradle.execute;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.prefs.PreferenceChangeEvent;
 import java.util.prefs.PreferenceChangeListener;
 import javax.swing.event.ChangeListener;
 import org.netbeans.api.project.Project;
-import org.netbeans.modules.gradle.GradleDistributionManager;
 import org.netbeans.modules.gradle.api.GradleBaseProject;
 import org.netbeans.modules.gradle.api.NbGradleProject;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 import org.netbeans.modules.gradle.spi.GradleSettings;
 import org.netbeans.modules.gradle.spi.execute.GradleDistributionProvider;
 import org.netbeans.spi.project.ProjectServiceProvider;
@@ -40,7 +44,9 @@ import static org.netbeans.modules.gradle.spi.GradleSettings.*;
  * @author lkishalmi
  */
 @ProjectServiceProvider(service = GradleDistributionProvider.class, projectType = NbGradleProject.GRADLE_PROJECT_TYPE)
-public class GradleDistributionProviderImpl implements GradleDistributionProvider{
+public class GradleDistributionProviderImpl implements GradleDistributionProvider {
+
+    private final static Logger LOGGER = Logger.getLogger(GradleDistributionProviderImpl.class.getName());
 
     private static final List<String> AFFECTING_PROPS = Arrays.asList(
             PROP_GRADLE_USER_HOME,
@@ -50,88 +56,67 @@ public class GradleDistributionProviderImpl implements GradleDistributionProvide
             PROP_GRADLE_VERSION
     );
 
+    private final ChangeSupport support = new ChangeSupport(this);
+    private final PreferenceChangeListener listener = (PreferenceChangeEvent evt) -> {
+        if (AFFECTING_PROPS.contains(evt.getKey())) {
+            dist = null;
+            support.fireChange();
+        }
+    };
+
     final Project project;
-    final Result res;
+    private GradleDistribution dist;
 
     public GradleDistributionProviderImpl(Project project) {
         this.project = project;
-        res = new Res();
-    }
-    
-    @Override
-    public Result getGradleDistribution() {
-        return res;
     }
 
-    private class Res implements Result {
-
-        private GradleDistributionManager.NbGradleVersion dist;
-
-        private final ChangeSupport support = new ChangeSupport(this);
-        private final PreferenceChangeListener listener = (PreferenceChangeEvent evt) -> {
-            if (AFFECTING_PROPS.contains(evt.getKey())) {
-                dist = null;
-                support.fireChange();
-            }
-        };
-
-        Res() {
-        }
-
-        @Override
-        public File getGradleInstall() {
-            GradleDistributionManager.NbGradleVersion d = getGradleDistribution();
-            return d != null ? d.distributionDir() : null;
-        }
+    @Override
+    public GradleDistribution getGradleDistribution() {
+        if (dist == null) {
+            GradleSettings settings = GradleSettings.getDefault();
 
-        @Override
-        public File getGradleHome() {
-            return GradleSettings.getDefault().getGradleUserHome();
-        }
+            GradleDistributionManager mgr = GradleDistributionManager.get(settings.getGradleUserHome());
 
-        @Override
-        public boolean isCompatibleWithSystemJava() {
-            GradleDistributionManager.NbGradleVersion d = getGradleDistribution();
-            return d != null ? d.isCompatibleWithSystemJava() : true;
-        }
+            GradleBaseProject gbp = GradleBaseProject.get(project);
 
-        @Override
-        public void addChangeListener(ChangeListener l) {
-            if (!support.hasListeners()) {
-                GradleSettings.getDefault().getPreferences().addPreferenceChangeListener(listener);
+            if ((gbp != null) && settings.isWrapperPreferred()) {
+                try {
+                    dist = mgr.distributionFromWrapper(gbp.getRootDir());
+                } catch (Exception ex) {
+                    LOGGER.log(Level.WARNING, "Cannot evaulate Gradle Wrapper", ex); //NOI18N
+                }
             }
-            support.addChangeListener(l);
-        }
 
-        @Override
-        public void removeChangeListener(ChangeListener l) {
-            support.removeChangeListener(l);
-            if (!support.hasListeners()) {
-                GradleSettings.getDefault().getPreferences().removePreferenceChangeListener(listener);
+            if ((dist == null) && settings.useCustomGradle() && !settings.getDistributionHome().isEmpty()) {
+                try {
+                    dist = mgr.distributionFromDir(new File(settings.getDistributionHome()));
+                } catch (IOException ex) {
+                    LOGGER.log(Level.WARNING, "Cannot evaulate Gradle Distribution", ex); //NOI18N
+                }
             }
-        }
-
-        private synchronized GradleDistributionManager.NbGradleVersion getGradleDistribution() {
             if (dist == null) {
-                GradleSettings settings = GradleSettings.getDefault();
-
-                GradleDistributionManager mgr = GradleDistributionManager.get(getGradleHome());
+                dist = mgr.distributionFromVersion(settings.getGradleVersion());
+            }
 
-                GradleBaseProject gbp = GradleBaseProject.get(project);
+        }
+        return dist;
+    }
 
-                if ((gbp != null) && settings.isWrapperPreferred()) {
-                    dist = mgr.evaluateGradleWrapperDistribution(gbp.getRootDir());
-                }
+    @Override
+    public void addChangeListener(ChangeListener l) {
+        if (!support.hasListeners()) {
+            GradleSettings.getDefault().getPreferences().addPreferenceChangeListener(listener);
+        }
+        support.addChangeListener(l);
+    }
 
-                if ((dist == null) && settings.useCustomGradle() && !settings.getDistributionHome().isEmpty()) {
-                    //TODO: Add support for file based Gradle Distribution
-                }
-                if (dist == null) {
-                    dist = mgr.createVersion(settings.getGradleVersion());
-                }
-                
-            }
-            return dist;
+    @Override
+    public void removeChangeListener(ChangeListener l) {
+        support.removeChangeListener(l);
+        if (!support.hasListeners()) {
+            GradleSettings.getDefault().getPreferences().removePreferenceChangeListener(listener);
         }
     }
+
 }
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/options/GradleOptionsController.java b/extide/gradle/src/org/netbeans/modules/gradle/options/GradleOptionsController.java
index 6d3b382..6249f8a 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/options/GradleOptionsController.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/GradleOptionsController.java
@@ -19,10 +19,11 @@
 
 package org.netbeans.modules.gradle.options;
 
-import org.netbeans.modules.gradle.GradleDistributionManager;
 import org.netbeans.modules.gradle.spi.GradleSettings;
 import java.beans.PropertyChangeListener;
 import javax.swing.JComponent;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 import org.netbeans.spi.options.OptionsPanelController;
 import org.openide.util.HelpCtx;
 import org.openide.util.Lookup;
@@ -53,7 +54,8 @@ public class GradleOptionsController extends OptionsPanelController {
         if (GradleSettings.getDefault().isSilentInstall()) {
             // If allowed, let's just install the required Gradle version.
             GradleDistributionManager gdm = GradleDistributionManager.get(GradleSettings.getDefault().getGradleUserHome());
-            gdm.createVersion(GradleSettings.getDefault().getGradleVersion()).install();
+            GradleDistribution dist = gdm.distributionFromVersion(GradleSettings.getDefault().getGradleVersion());
+            dist.install();
         }
     }
 
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 8e8a9d0..35c9ab2 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.form
@@ -318,7 +318,7 @@
                     <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cbGradleVersionItemStateChanged"/>
                   </Events>
                   <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;NbGradleVersion&gt;"/>
+                    <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;GradleDistribution&gt;"/>
                   </AuxValues>
                 </Component>
                 <Component class="javax.swing.JRadioButton" name="rbUseStandardGradle">
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 4536fc8..ee74ba7 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/options/SettingsPanel.java
@@ -19,13 +19,11 @@
 
 package org.netbeans.modules.gradle.options;
 
-import org.netbeans.modules.gradle.GradleDistributionManager;
 import org.netbeans.modules.gradle.spi.GradleSettings;
 import java.awt.CardLayout;
 import java.io.File;
 import javax.swing.JFileChooser;
 import org.netbeans.spi.options.OptionsPanelController;
-import org.netbeans.modules.gradle.GradleDistributionManager.NbGradleVersion;
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.event.ActionEvent;
@@ -40,7 +38,10 @@ import javax.swing.JLabel;
 import javax.swing.JList;
 import javax.swing.ListCellRenderer;
 import javax.swing.SwingWorker;
+import javax.swing.UIManager;
 import org.netbeans.api.annotations.common.StaticResource;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 import org.openide.LifecycleManager;
 import org.openide.awt.NotificationDisplayer;
 import org.openide.util.Exceptions;
@@ -639,12 +640,14 @@ public class SettingsPanel extends javax.swing.JPanel {
 
     @Messages("LBL_IncompatibleGradle=This version does not work with NetBeans!")
     private void cbGradleVersionItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cbGradleVersionItemStateChanged
-        NbGradleVersion v = gdm.createVersion(evt.getItem().toString());
-        if ((v != null) && (evt.getStateChange() == ItemEvent.SELECTED)) {
-            if (v.isBlackListed()) {
-                lbVersionInfo.setText(Bundle.LBL_IncompatibleGradle());
-            } else {
-                lbVersionInfo.setText(null);
+        if (evt.getItem() instanceof GradleDistribution) {
+            GradleDistribution v = (GradleDistribution) evt.getItem();
+            if ((v != null) && (evt.getStateChange() == ItemEvent.SELECTED)) {
+                if (v.isBlackListed()) {
+                    lbVersionInfo.setText(Bundle.LBL_IncompatibleGradle());
+                } else {
+                    lbVersionInfo.setText(null);
+                }
             }
         }
     }//GEN-LAST:event_cbGradleVersionItemStateChanged
@@ -728,18 +731,18 @@ public class SettingsPanel extends javax.swing.JPanel {
 
         cbAllowExecution.setSelectedItem(settings.getGradleExecutionRule());
 
-        new SwingWorker<List<NbGradleVersion>, Void>() {
+        new SwingWorker<List<GradleDistribution>, Void>() {
 
             @Override
-            protected List<NbGradleVersion> doInBackground() throws Exception {
-                return gdm.availableVersions(true);
+            protected List<GradleDistribution> doInBackground() throws Exception {
+                return gdm.availableDistributions(true);
             }
 
             @Override
             protected void done() {
                 try {
-                    NbGradleVersion[] items = get().toArray(new NbGradleVersion[0]);
-                    ComboBoxModel<NbGradleVersion> model = new DefaultComboBoxModel<>(items);
+                    GradleDistribution[] items = get().toArray(new GradleDistribution[0]);
+                    ComboBoxModel<GradleDistribution> model = new DefaultComboBoxModel<>(items);
                     cbGradleVersion.setModel(model);
                     model.setSelectedItem(settings.getGradleVersion());
                 } catch (InterruptedException | ExecutionException ex) {
@@ -762,7 +765,7 @@ public class SettingsPanel extends javax.swing.JPanel {
         } else {
             settings.setGradleUserHome(new File(tfGradleUserHome.getText()));
         }
-        settings.setGradleVersion(cbGradleVersion.getSelectedItem().toString());
+        settings.setGradleVersion(((GradleDistribution) cbGradleVersion.getSelectedItem()).getVersion());
         settings.setDistributionHome(tfUseCustomGradle.getText());
         settings.setWrapperPreferred(cbPreferWrapper.isSelected());
         boolean useCustomGradle = bgUsedDistribution.getSelection() == rbUseCustomGradle.getModel();
@@ -842,6 +845,8 @@ public class SettingsPanel extends javax.swing.JPanel {
     }
 
     private class VersionCellRenderer extends DefaultListCellRenderer {
+        final Color blackListColor = UIManager.getColor("nb.errorForeground");         //NOI18N
+        final Color unavailableColor = UIManager.getColor("Label.disabledForeground"); //NOI18N
         @SuppressWarnings("rawtypes")
         final ListCellRenderer delegate;
         @SuppressWarnings("rawtypes")
@@ -860,15 +865,16 @@ public class SettingsPanel extends javax.swing.JPanel {
             if (cmp instanceof JLabel) {
                 JLabel label = (JLabel) cmp;
                 label.setHorizontalAlignment(RIGHT);
-                if (value != null) {
-                    NbGradleVersion version = gdm.createVersion(value.toString());
-                    if (!version.isAvailable()) {
+                if (value != null && value instanceof GradleDistribution) {
+                    GradleDistribution dist = (GradleDistribution) value;
+                    label.setText(dist.getVersion());
+                    if (!dist.isAvailable()) {
                         label.setToolTipText(Bundle.NbGradleVersion_autoInstall_TXT());
-                        label.setForeground(Color.gray);
+                        label.setForeground(unavailableColor);
                     }
-                    if (version.isBlackListed()) {
+                    if (dist.isBlackListed()) {
                         label.setToolTipText(Bundle.NbGradleVersion_blacklist_TXT());
-                        label.setForeground(Color.red);
+                        label.setForeground(blackListColor);
                     }
                 }
             }
@@ -899,7 +905,7 @@ public class SettingsPanel extends javax.swing.JPanel {
     private javax.swing.JComboBox<GradleSettings.DownloadLibsRule> cbDownloadLibs;
     private javax.swing.JComboBox<GradleSettings.DownloadMiscRule> cbDownloadSources;
     private javax.swing.JCheckBox cbEnableCache;
-    private javax.swing.JComboBox<NbGradleVersion> cbGradleVersion;
+    private javax.swing.JComboBox<GradleDistribution> cbGradleVersion;
     private javax.swing.JCheckBox cbHideEmptyConfig;
     private javax.swing.JCheckBox cbNoRebuild;
     private javax.swing.JCheckBox cbOffline;
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/queries/GradleSourceForBinary.java b/extide/gradle/src/org/netbeans/modules/gradle/queries/GradleSourceForBinary.java
index 5be571c..caee406 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/queries/GradleSourceForBinary.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/queries/GradleSourceForBinary.java
@@ -19,7 +19,6 @@
 
 package org.netbeans.modules.gradle.queries;
 
-import java.io.File;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -27,7 +26,8 @@ import java.util.HashMap;
 import java.util.Map;
 import javax.swing.event.ChangeListener;
 import org.netbeans.api.java.queries.SourceForBinaryQuery;
-import org.netbeans.modules.gradle.api.execute.RunUtils;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager;
+import org.netbeans.modules.gradle.spi.GradleSettings;
 import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
 import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2;
 import org.openide.filesystems.FileObject;
@@ -56,9 +56,9 @@ public class GradleSourceForBinary implements SourceForBinaryQueryImplementation
     @Override
     public Result findSourceRoots2(URL binaryRoot) {
         Res ret = cache.get(binaryRoot);
-        File dist = RunUtils.evaluateGradleDistribution(null, false);
-        if ((ret == null) && (dist != null)) {
-            FileObject distDir = FileUtil.toFileObject(dist);
+        GradleDistributionManager.GradleDistribution dist = GradleDistributionManager.get(GradleSettings.getDefault().getGradleUserHome()).defaultDistribution();
+        if ((ret == null) && (dist.isAvailable())) {
+            FileObject distDir = FileUtil.toFileObject(dist.getDistributionDir());
             FileObject srcDir = distDir == null ? null : distDir.getFileObject("src"); //NOI18N
             if ((srcDir != null) && ("jar".equals(binaryRoot.getProtocol()))) {  //NOI18N
 
diff --git a/extide/gradle/src/org/netbeans/modules/gradle/spi/execute/GradleDistributionProvider.java b/extide/gradle/src/org/netbeans/modules/gradle/spi/execute/GradleDistributionProvider.java
index a8beeff..3e9e1dc 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/spi/execute/GradleDistributionProvider.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/spi/execute/GradleDistributionProvider.java
@@ -18,22 +18,40 @@
  */
 package org.netbeans.modules.gradle.spi.execute;
 
-import java.io.File;
 import javax.swing.event.ChangeListener;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 
 /**
+ * Projects can provide the required Gradle Distribution through this interface,
+ * by placing an implementation of it in the project lookup.
  *
+ * @since 2.4
  * @author lkishalmi
  */
 public interface GradleDistributionProvider {
-    Result getGradleDistribution();
 
-    interface Result {
-        File getGradleInstall();
-        File getGradleHome();
-        boolean isCompatibleWithSystemJava();
-        
-        void addChangeListener(ChangeListener l);
-        void removeChangeListener(ChangeListener l);
-    }
+    /**
+     * Shall return the {@link GradleDistribution} used by the project.
+     * It may return <code>null</code> if the project does not have specific
+     * GradleDistribution requirements. Gradle defaults of the actual project
+     * and tooling API would be used in that case (not recommended).
+     *
+     * @return The {@link GradeDistribution} to use for the project.
+     */
+    GradleDistribution getGradleDistribution();
+
+    /**
+     * Add a {@link ChangeListener} to be notified when the required
+     * {@link GradleDistribution} changes for the project;
+     *
+     * @param l the {@link ChanegListener}
+     */
+    void addChangeListener(ChangeListener l);
+
+    /**
+     * Remove a registered {@link ChangeListener}.
+     * @param l the {@link ChanegListener}
+     */
+    void removeChangeListener(ChangeListener l);
+
 }
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/classpath/AbstractGradleScriptClassPath.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/classpath/AbstractGradleScriptClassPath.java
index 91a0b5d..7a9a296 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/classpath/AbstractGradleScriptClassPath.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/classpath/AbstractGradleScriptClassPath.java
@@ -28,7 +28,8 @@ import java.util.List;
 import java.util.prefs.PreferenceChangeEvent;
 import java.util.prefs.PreferenceChangeListener;
 import java.util.prefs.Preferences;
-import org.netbeans.modules.gradle.api.execute.RunUtils;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager;
+import org.netbeans.modules.gradle.api.execute.GradleDistributionManager.GradleDistribution;
 import org.netbeans.spi.java.classpath.ClassPathImplementation;
 import org.netbeans.spi.java.classpath.PathResourceImplementation;
 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
@@ -48,7 +49,7 @@ abstract class AbstractGradleScriptClassPath implements ClassPathImplementation
     File distDir;
 
     public AbstractGradleScriptClassPath() {
-        distDir = RunUtils.evaluateGradleDistribution(null, false);
+        changeDistDir();
 
         prefListener = (PreferenceChangeEvent evt) -> {
             switch (evt.getKey()) {
@@ -91,8 +92,9 @@ abstract class AbstractGradleScriptClassPath implements ClassPathImplementation
     }
 
     private void changeDistDir() {
-        File newDistDir = RunUtils.evaluateGradleDistribution(null, false);
-        if (!distDir.equals(newDistDir)) {
+        GradleDistribution dist = GradleDistributionManager.get(GradleSettings.getDefault().getGradleUserHome()).defaultDistribution();
+        File newDistDir = dist.getDistributionDir();
+        if (distDir != null && !distDir.equals(newDistDir)) {
             distDir = newDistDir;
             resources = null;
             cs.firePropertyChange(ClassPathImplementation.PROP_RESOURCES, null, null);


---------------------------------------------------------------------
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