From de5412c1aebb72881b8352bc4d90500f47e963e0 Mon Sep 17 00:00:00 2001 From: Svata Dedic Date: Thu, 8 Aug 2024 11:33:09 +0200 Subject: [PATCH] LoadOptions introduced to control project loading. --- extide/gradle/apichanges.xml | 23 +++ extide/gradle/manifest.mf | 2 +- .../modules/gradle/GradleProjectLoader.java | 5 +- .../modules/gradle/NbGradleProjectImpl.java | 123 ++++++++++--- .../modules/gradle/api/NbGradleProject.java | 173 +++++++++++++++++- .../gradle/cache/ProjectInfoDiskCache.java | 8 +- .../gradle/loaders/AbstractProjectLoader.java | 20 +- .../loaders/DiskCacheProjectLoader.java | 2 +- .../loaders/GradleProjectLoaderImpl.java | 16 +- .../gradle/loaders/LegacyProjectLoader.java | 25 ++- .../NbProjectInfoCachingDescriptor.java | 2 +- .../spi/newproject/TemplateOperation.java | 4 +- 12 files changed, 345 insertions(+), 58 deletions(-) diff --git a/extide/gradle/apichanges.xml b/extide/gradle/apichanges.xml index 72ff79bef6b8..867fd993607c 100644 --- a/extide/gradle/apichanges.xml +++ b/extide/gradle/apichanges.xml @@ -83,6 +83,29 @@ is the proper place. + + + LoadOptions object replaces growing number of argumetns to project load APIs. Access to current Lookup added. + + + + + +

+ To avoid growing number of parameters to toQuality(), + a LoadOptions structure was created that can be used to provide details on + how the project should be loaded. +

+

+ The time the project data was actually loaded is now available using NbGradleProject.getEvaluateTime(). + The time can be used for timestamp checking against project files. +

+

+ Access to the current metadata information/services snapshot is newly available from NbGradleProject.currentLookup(). +

+
+ +
Gradle InitOperation now Supports --java-version and --comments flags diff --git a/extide/gradle/manifest.mf b/extide/gradle/manifest.mf index 1cc6fee80e30..d2993a2bf4a8 100644 --- a/extide/gradle/manifest.mf +++ b/extide/gradle/manifest.mf @@ -4,4 +4,4 @@ OpenIDE-Module: org.netbeans.modules.gradle/2 OpenIDE-Module-Layer: org/netbeans/modules/gradle/layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/gradle/Bundle.properties OpenIDE-Module-Java-Dependencies: Java > 17 -OpenIDE-Module-Specification-Version: 2.42 +OpenIDE-Module-Specification-Version: 2.43 diff --git a/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectLoader.java b/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectLoader.java index 5374de2b0cac..6f4d85452e56 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectLoader.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/GradleProjectLoader.java @@ -18,13 +18,12 @@ */ package org.netbeans.modules.gradle; -import org.netbeans.modules.gradle.api.NbGradleProject; +import org.netbeans.modules.gradle.api.NbGradleProject.LoadOptions; /** * * @author lkishalmi */ public interface GradleProjectLoader { - - GradleProject loadProject(NbGradleProject.Quality aim, String descriptionOpt, boolean ignoreCache, boolean interactive, String... args); + public GradleProject loadProject(LoadOptions options, String... args); } diff --git a/extide/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java b/extide/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java index 442b9ef7b505..86abed1beea7 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/NbGradleProjectImpl.java @@ -28,6 +28,8 @@ import java.io.File; import java.io.IOException; import java.lang.ref.WeakReference; +import java.nio.file.Files; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -65,6 +67,7 @@ import org.netbeans.api.project.ui.ProjectProblems; import org.netbeans.modules.gradle.api.GradleBaseProject; import org.netbeans.modules.gradle.api.GradleReport; +import org.netbeans.modules.gradle.api.NbGradleProject.LoadOptions; import org.netbeans.modules.gradle.options.GradleExperimentalSettings; import org.netbeans.spi.project.CacheDirectoryProvider; import org.netbeans.spi.project.support.LookupProviderSupport; @@ -106,6 +109,8 @@ public void run() { private volatile GradleProject project; // @GuardedBy(this) private Quality attemptedQuality; + // @GuardedBy(this) + private Instant timeLoaded; static { // invokes static initializer of ModelHandle.class @@ -257,6 +262,19 @@ public NbGradleProject getProjectWatcher() { return watcher; } + /** + * Time when the gradle project was evaluated. + * @return evaluation time. + */ + public long getEvaluationTime() { + GradleProject gp = this.project; + if (gp == null) { + return -1; + } else { + return gp.getEvaluationTime(); + } + } + /** * Obtains a project attempting at least the defined quality, without setting * that quality level for subsequent loads. Same as {@link #projectWithQualityTask} @@ -308,23 +326,55 @@ public GradleProject projectWithQuality(String desc, Quality aim, boolean intera * @param force to force load even though the quality does not change. * @return project instance */ + @Deprecated public CompletableFuture projectWithQualityTask(String desc, Quality aim, boolean interactive, boolean force) { + return projectWithQualityTask(NbGradleProject.loadOptions(aim). + setDescription(desc). + setInteractive(interactive). + setForce(force) + ); + } + + /** + * Obtains a project attempting at least the defined quality, without setting + * that quality level for subsequent loads. Note that the returned project's quality + * must be checked. If the currently loaded project declares the desired quality, + * no load is performed. + *

+ * This method should be used in preference to {@link #loadProject()} or {@link #loadOWnProject}, + * unless it's desired to force refresh the project contents to the current disk state. + *

+ * Implementation note: project reload events are dispatched synchronously + * in the calling thread. + *
+ * @param options requirements and optiosn for the load operation + * @return Future that completes with the project instance + */ + public CompletableFuture projectWithQualityTask(LoadOptions options) { + boolean force = options.isForce(); synchronized (this) { GradleProject c = project; - if (c != null) { - if (!force && c.getQuality().atLeast(aim)) { + if (options.isCheckFiles()) { + Instant newest = newestProjectFiletime(); + if (newest.isAfter(Instant.ofEpochMilli(c.getEvaluationTime()))) { + force = true; + } + } + if (!force && c != null) { + if (c.getQuality().atLeast(options.getAim())) { return CompletableFuture.completedFuture(c); } - if (!force && attemptedQuality.atLeast(aim)) { + if (attemptedQuality.atLeast(options.getAim())) { return CompletableFuture.completedFuture(c); } } } CompletableFuture toRet = new CompletableFuture<>(); + final boolean ff = force; RELOAD_RP.post(() -> - loadOwnProject0(desc, false, interactive, aim, false, force) + loadOwnProject0(options.setForce(ff), false) .handle((p, e) -> { - if (e == null) { + if (e == null) { toRet.complete(p); } else { toRet.completeExceptionally(e); @@ -388,26 +438,22 @@ CompletableFuture loadOwnProject(String desc, boolean ignoreCache private LoadingCF loading; private static class LoadingCF extends CompletableFuture { - private final Quality aim; - private final boolean ignoreCache; - private final boolean interactive; + private final LoadOptions options; private final boolean sync; private final List args; private ThreadLocal ownThreadCompletion = new ThreadLocal<>(); - public LoadingCF(Quality aim, boolean ignoreCache, boolean interactive, boolean sync, List args) { - this.aim = aim; - this.ignoreCache = ignoreCache; - this.interactive = interactive; + public LoadingCF(LoadOptions options, boolean sync, List args) { + this.options = options; this.sync = sync; this.args = args; } public boolean satisifes(LoadingCF other) { - if (aim.worseThan(other.aim)) { + if (options.getAim().worseThan(other.options.getAim())) { return false; } - if (ignoreCache != other.ignoreCache || interactive != other.interactive || sync != other.sync) { + if (options.isIgnoreCache() != other.options.isIgnoreCache() || options.isInteractive() != other.options.isInteractive() || sync != other.sync) { return false; } return args.equals(other.args); @@ -438,6 +484,17 @@ public GradleProject get() throws InterruptedException, ExecutionException { } } + Instant newestProjectFiletime() { + return getGradleFiles().getProjectFiles().stream().map(f -> { + try { + return Files.getLastModifiedTime(f.toPath()).toInstant(); + } catch (IOException ex) { + // no op + return Instant.now(); + } + }).reduce((a, b) -> a.isAfter(b) ? a : b).orElse(Instant.now()); + } + /** * Loads a project. After load, dispatches reload events. If "sync" is false (= asynchronous), dispatches events * and does possible fixups in {@link #RELOAD_RP}. The returned future completes only after all the event @@ -455,16 +512,29 @@ public GradleProject get() throws InterruptedException, ExecutionException { * @return Future for the new GradleProject state. See notes about sync/async differences. */ /* nonprivate: tests only */CompletableFuture loadOwnProject0(String desc, boolean ignoreCache, boolean interactive, Quality aim, boolean sync, boolean force, String... args) { + return loadOwnProject0(NbGradleProject.loadOptions(aim). + setDescription(desc). + setIgnoreCache(ignoreCache). + setInteractive(interactive). + setForce(force), + sync, args + ); + } + + // NOTE: the optional arguments are only used by ActionProviderImpl, to reload project before / after a project action. If there are more users, + // consider to expose the args... in the LoadOptions. Somehow need to solve the effect of different args to the project loaded data, as they may + // differ significantly and replace other-argumented state in the disk cache etc. + CompletableFuture loadOwnProject0(LoadOptions options, boolean sync, String... args) { GradleProjectLoader loader = getLookup().lookup(GradleProjectLoader.class); if (loader == null) { throw new IllegalStateException("No loader implementation is present!"); } - LoadingCF f = new LoadingCF(aim, ignoreCache, interactive, sync, Arrays.asList(args)); + LoadingCF f = new LoadingCF(options, sync, Arrays.asList(args)); synchronized (this) { if (this.loading != null && this.loading.satisifes(f)) { - if (!force) { + if (!options.isForce()) { LOG.log(Level.FINER, "Project {2} is already loading to quality {0}, now attempted {1}, returning existing handle", new - Object[] { this.loading.aim, aim, this }); + Object[] { this.loading.options.getAim(), options.getAim(), this }); return loading; } } @@ -472,8 +542,11 @@ public GradleProject get() throws InterruptedException, ExecutionException { } int s = currentSerial.incrementAndGet(); // do not block during project load. - LOG.log(Level.FINER, "Starting project {2} load, serial {0}, attempted quality {1}", new Object[] { s, aim, this }); - GradleProject prj = loader.loadProject(aim, desc, ignoreCache, interactive, args); + LOG.log(Level.FINER, "Starting project {2} load, serial {0}, attempted quality {1}", new Object[] { s, options.getAim(), this }); + if (options.isForce()) { + options.setIgnoreCache(true); + } + GradleProject prj = loader.loadProject(options, args); synchronized (this) { if (loadedProjectSerial > s && project != null) { // the load started LATER than this one: return that project, and do not replace anything as this.project is newer @@ -481,9 +554,9 @@ public GradleProject get() throws InterruptedException, ExecutionException { return CompletableFuture.completedFuture(this.project); } loadedProjectSerial = s; - this.attemptedQuality = aim; + this.attemptedQuality = options.getAim(); - boolean replace = project == null || force; + boolean replace = project == null || options.isForce(); if (project != null) { if (prj.getQuality().betterThan(project.getQuality())) { replace = true; @@ -497,7 +570,7 @@ public GradleProject get() throws InterruptedException, ExecutionException { } if (!replace) { // avoid replacing a project when nothing has changed. - LOG.log(Level.FINER, "Current project {1} sufficient for attempted quality {0}", new Object[] { this.project, aim }); + LOG.log(Level.FINER, "Current project {1} sufficient for attempted quality {0}", new Object[] { this.project, options.getAim() }); return CompletableFuture.completedFuture(this.project); } LOG.log(Level.FINER, "Replacing {0} with {1}, attempted quality {2}", new Object[] { this.project, prj, attemptedQuality }); @@ -562,11 +635,7 @@ private CompletableFuture callAccessorReload(LoadingCF f, GradleP * @return Task representing the reloading process */ RequestProcessor.Task forceReloadProject(String reloadReason, boolean interactive, final Quality aim, final String... args) { - return reloadProject(reloadReason, true, interactive, aim, args); - } - - private RequestProcessor.Task reloadProject(String desc, final boolean ignoreCache, final boolean interactive, final Quality aim, final String... args) { - return RELOAD_RP.post(() -> loadOwnProject(desc, ignoreCache, interactive, aim, args)); + return RELOAD_RP.post(() -> loadOwnProject(reloadReason, true, interactive, aim, args)); } @Override diff --git a/extide/gradle/src/org/netbeans/modules/gradle/api/NbGradleProject.java b/extide/gradle/src/org/netbeans/modules/gradle/api/NbGradleProject.java index 1fa59878bc40..c80b0a578ed3 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/api/NbGradleProject.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/api/NbGradleProject.java @@ -201,6 +201,15 @@ private NbGradleProject(NbGradleProjectImpl project) { this.project = project; support = new PropertyChangeSupport(project); } + + /** + * Provides full lookup of the currently loaded project state. This Lookup does NOT refreshes + * as project is reload, the client must eventually watch {@link #PROP_PROJECT_INFO} property change and obtain a fresh lookup. + * @return Lookup that contains the current metadata for the project. + */ + public Lookup curretLookup() { + return project.getGradleProject().getLookup(); + } public T projectLookup(Class clazz) { return project.getGradleProject().getLookup().lookup(clazz); @@ -256,6 +265,15 @@ public boolean isUnloadable() { return getQuality().worseThan(Quality.SIMPLE); } + /** + * Returns the time the project was evaluated. If the project has not been loaded at least in its + * 'fallback' state, it returns a negative value. + * @return evaluation time of the project. + */ + public long getEvaluateTime() { + return project.getEvaluationTime(); + } + /** * Attempts to refresh the project to at least the desired quality. The project information * may be reloaded, if the project is currently loaded with lower {@link Quality} than {@code q}. @@ -277,7 +295,160 @@ public boolean isUnloadable() { * @since 2.11 */ public @NonNull CompletionStage toQuality(@NullAllowed String reason, @NonNull Quality q, boolean forceLoad) { - return project.projectWithQualityTask(reason, q, false, forceLoad).thenApply(p -> this); + return project.projectWithQualityTask(loadOptions(q).setDescription(reason).setForce(forceLoad)).thenApply(p -> this); + } + + /** + * Creates a {@link LoadOptions} object to be used with {@link #toQuality(org.netbeans.modules.gradle.api.NbGradleProject.LoadOptions)}. + * @param aim the target quality + * @return options object. + * @since 2.43 + */ + public static LoadOptions loadOptions(Quality aim) { + return new LoadOptions(aim); + } + + /** + * Describes options for loading a Gradle project. + * @since 2.43 + */ + public static final class LoadOptions { + private final Quality aim; + private boolean force; + private String description; + private boolean ignoreCache; + private boolean interactive; + private boolean offline; + private boolean checkFiles; + + LoadOptions(Quality aim) { + this.aim = aim; + } + + /** + * Instructs to check file timestamps against project loading time when deciding whether to use current data. The default is {@code false}. + * @param b true to check file timestamps. + * @return this options object. + */ + public LoadOptions setCheckFiles(boolean b) { + this.checkFiles = b; + return this; + } + + /** + * Forces offline operation. The default is {@code false}. + * @param b true, if the operation must be offline + * @return this options object + */ + public LoadOptions setOffline(boolean b) { + this.offline = b; + return this; + } + + /** + * Sets an interactive flag. If interactive, the implementation is allowed to ask for confirmation + * or other questions. False means that questions will fail as if cancelled, other prompts will resolve to + * their default options. The default is {@code false}. + * @param b true, if interactive process + * @return this options object + */ + public LoadOptions setInteractive(boolean b) { + this.interactive = b; + return this; + } + + /** + * Sets description of the operation. The description serves as part of a message to the user about project being + * loaded or a progress status indicator. The text should describe the operation that requires a load, e.g. "Creating classpath". + * There's no default description. + * @param desc description + * @return this options object + */ + public LoadOptions setDescription(String desc) { + this.description = desc; + return this; + } + + /** + * Forces the load to bypass the on-disk cache. If set, cached data will be ignored. If false, the implementation + * is allowed to satisfy the load from the cache, if the cached quality is sufficient. The default is {@code false}. + * @param b true to bypass caches + * @return this options object + */ + public LoadOptions setIgnoreCache(boolean b) { + this.ignoreCache = b; + return this; + } + + /** + * Forces the load, even though the quality of current project is OK and no files have been modified. The default is {@code false}. + * @param b true to force load the project. + * @return this options object + */ + public LoadOptions setForce(boolean b) { + this.force = b; + return this; + } + + /** + * @return true to force the load regardless of consistency and quality + */ + public boolean isForce() { + return force; + } + + /** + * @return the desired quality level + */ + public NbGradleProject.Quality getAim() { + return aim; + } + + /** + * @return description of the operation that initiated the load + */ + public String getDescription() { + return description; + } + + /** + * @return true to ignore netbeans caches + */ + public boolean isIgnoreCache() { + return ignoreCache; + } + + /** + * @return true, if the process is interactive + */ + public boolean isInteractive() { + return interactive; + } + + /** + * @return true, if the load must not use online resources. + */ + public boolean isOffline() { + return offline; + } + + /** + * @return true to check timestamps of gradle files + */ + public boolean isCheckFiles() { + return checkFiles; + } + } + + /** + * Unlike {@link #toQuality}, this method loads the project, if the project files have changed since the last load. If project definition + * files did not change, + * @param options load options and requiremens. + * @return Future with the result project. + * @since 2.43 + */ + public @NonNull CompletionStage toQuality(LoadOptions options) { + return project.projectWithQualityTask(options).thenApply(p -> this); } public Preferences getPreferences(boolean shared) { diff --git a/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java b/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java index a195c37523a7..97f3e3145f8e 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java @@ -215,14 +215,20 @@ public static final class QualifiedProjectInfo implements NbProjectInfo { private final Set problems; private final Set reports; private final String gradleException; + private final long timestamp; - public QualifiedProjectInfo(Quality quality, NbProjectInfo pinfo) { + public QualifiedProjectInfo(Quality quality, NbProjectInfo pinfo, long timestamp) { this.quality = quality; info = new TreeMap<>(pinfo.getInfo()); ext = new TreeMap<>(pinfo.getExt()); problems = new LinkedHashSet<>(pinfo.getProblems()); gradleException = pinfo.getGradleException(); reports = makeReports(pinfo.getReports()); + this.timestamp = timestamp; + } + + public long getTimestamp() { + return timestamp; } @Override diff --git a/extide/gradle/src/org/netbeans/modules/gradle/loaders/AbstractProjectLoader.java b/extide/gradle/src/org/netbeans/modules/gradle/loaders/AbstractProjectLoader.java index 61176f0f8cb9..e330c7e360b0 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/loaders/AbstractProjectLoader.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/loaders/AbstractProjectLoader.java @@ -29,6 +29,7 @@ import org.netbeans.modules.gradle.api.GradleBaseProject; import org.netbeans.modules.gradle.api.GradleReport; import org.netbeans.modules.gradle.api.NbGradleProject; +import org.netbeans.modules.gradle.api.NbGradleProject.LoadOptions; import static org.netbeans.modules.gradle.api.NbGradleProject.Quality.EVALUATED; import static org.netbeans.modules.gradle.api.NbGradleProject.Quality.FALLBACK; import org.netbeans.modules.gradle.tooling.internal.NbProjectInfo.Report; @@ -62,19 +63,20 @@ boolean needsTrust() { } static final class ReloadContext { - + final LoadOptions options; final NbGradleProjectImpl project; final GradleProject previous; - final NbGradleProject.Quality aim; final GradleCommandLine cmd; - final String description; - public ReloadContext(NbGradleProjectImpl project, NbGradleProject.Quality aim, GradleCommandLine cmd, String description) { + public ReloadContext(NbGradleProjectImpl project, LoadOptions options, GradleCommandLine cmd) { this.project = project; this.previous = project.isGradleProjectLoaded() ? project.projectWithQuality(null, FALLBACK, false, false) : FallbackProjectLoader.createFallbackProject(project.getGradleFiles()); - this.aim = aim; + this.options = options; this.cmd = cmd; - this.description = description; + } + + public String getDescription() { + return options.getDescription(); } public GradleProject getPrevious() { @@ -82,7 +84,11 @@ public GradleProject getPrevious() { } public NbGradleProject.Quality getAim() { - return aim; + return options.getAim(); + } + + public LoadOptions getOptions() { + return options; } } diff --git a/extide/gradle/src/org/netbeans/modules/gradle/loaders/DiskCacheProjectLoader.java b/extide/gradle/src/org/netbeans/modules/gradle/loaders/DiskCacheProjectLoader.java index 95b94943b8c9..4f51f5981caa 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/loaders/DiskCacheProjectLoader.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/loaders/DiskCacheProjectLoader.java @@ -57,7 +57,7 @@ public GradleProject load() { @Override boolean isEnabled() { - return ctx.aim.betterThan(FALLBACK) && !GradleExperimentalSettings.getDefault().isCacheDisabled(); + return ctx.getAim().betterThan(FALLBACK) && !GradleExperimentalSettings.getDefault().isCacheDisabled(); } @Override diff --git a/extide/gradle/src/org/netbeans/modules/gradle/loaders/GradleProjectLoaderImpl.java b/extide/gradle/src/org/netbeans/modules/gradle/loaders/GradleProjectLoaderImpl.java index 2852062ee602..6a84a7efd2de 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/loaders/GradleProjectLoaderImpl.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/loaders/GradleProjectLoaderImpl.java @@ -26,7 +26,7 @@ import org.netbeans.modules.gradle.GradleProject; import org.netbeans.modules.gradle.GradleProjectLoader; import org.netbeans.modules.gradle.NbGradleProjectImpl; -import org.netbeans.modules.gradle.api.NbGradleProject; +import org.netbeans.modules.gradle.api.NbGradleProject.LoadOptions; import org.netbeans.modules.gradle.api.execute.GradleCommandLine; import org.netbeans.modules.gradle.api.execute.RunUtils; import org.netbeans.modules.gradle.options.GradleExperimentalSettings; @@ -49,15 +49,15 @@ public GradleProjectLoaderImpl(Project project) { @NbBundle.Messages({ "ERR_ProjectNotTrusted=Gradle execution is not trusted on this project." }) - public GradleProject loadProject(NbGradleProject.Quality aim, String descriptionOpt, boolean ignoreCache, boolean interactive, String... args) { - LOGGER.info("Load aiming " +aim + " for "+ project); + public GradleProject loadProject(LoadOptions options, String... args) { + LOGGER.info("Load aiming " +options.getAim() + " for "+ project); GradleCommandLine cmd = new GradleCommandLine(args); - AbstractProjectLoader.ReloadContext ctx = new AbstractProjectLoader.ReloadContext((NbGradleProjectImpl) project, aim, cmd, descriptionOpt); + AbstractProjectLoader.ReloadContext ctx = new AbstractProjectLoader.ReloadContext((NbGradleProjectImpl) project, options, cmd); LOGGER.log(Level.FINER, "Load context: project = {0}, prev = {1}, aim = {2}, args = {3}", new Object[] { - project, ctx.previous, aim, cmd}); + project, ctx.previous, options.getAim(), cmd}); List loaders = new LinkedList<>(); - if (!ignoreCache) loaders.add(new DiskCacheProjectLoader(ctx)); + if (!options.isIgnoreCache()) loaders.add(new DiskCacheProjectLoader(ctx)); if (GradleExperimentalSettings.getDefault().isBundledLoading()) { loaders.add(new BundleProjectLoader(ctx)); loaders.add(new DiskCacheProjectLoader(ctx)); @@ -73,7 +73,7 @@ public GradleProject loadProject(NbGradleProject.Quality aim, String description if (loader.isEnabled()) { if (loader.needsTrust()) { if (trust == null) { - trust = RunUtils.isProjectTrusted(ctx.project, interactive); + trust = RunUtils.isProjectTrusted(ctx.project, options.isInteractive()); } if (trust) { ret = loader.load(); @@ -93,7 +93,7 @@ public GradleProject loadProject(NbGradleProject.Quality aim, String description if (best == null || best.getQuality().notBetterThan(ret.getQuality())) { best = ret; } - if (ret.getQuality().atLeast(aim)) { + if (ret.getQuality().atLeast(options.getAim())) { // We have the quality we are looking for, let's be happy with that break; } diff --git a/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java b/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java index 7fdfb72b129f..739f4d6aba87 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/loaders/LegacyProjectLoader.java @@ -128,7 +128,7 @@ public GradleProject load() { @Override public boolean isEnabled() { - return ctx.aim.betterThan(EVALUATED); + return ctx.getAim().betterThan(EVALUATED); } @NbBundle.Messages({ @@ -140,7 +140,7 @@ public boolean isEnabled() { private static GradleProject loadGradleProject(ReloadContext ctx, CancellationToken token, ProgressListener pl) { long start = System.currentTimeMillis(); NbProjectInfo info = null; - NbGradleProject.Quality quality = ctx.aim; + NbGradleProject.Quality quality = ctx.getAim(); GradleBaseProject base = ctx.previous.getBaseProject(); ProjectConnection pconn = ctx.project.getLookup().lookup(ProjectConnection.class); @@ -157,9 +157,9 @@ private static GradleProject loadGradleProject(ReloadContext ctx, CancellationTo GoOnline goOnline; if (GradleSettings.getDefault().isOffline()) { goOnline = GoOnline.NEVER; - } else if (ctx.aim == FULL_ONLINE) { + } else if (quality == FULL_ONLINE) { goOnline = GoOnline.ALWAYS; - } else { + } else { switch (GradleSettings.getDefault().getDownloadLibs()) { case NEVER: goOnline = GoOnline.NEVER; @@ -171,6 +171,9 @@ private static GradleProject loadGradleProject(ReloadContext ctx, CancellationTo goOnline = GoOnline.ON_DEMAND; } } + if (ctx.getOptions().isOffline()) { + goOnline = GoOnline.NEVER; + } try { errors.clear(); @@ -297,6 +300,14 @@ private static GradleProject loadGradleProject(ReloadContext ctx, CancellationTo List problems = exceptionsToProblems(ctx.project.getGradleFiles().getBuildScript(), ex); errors.openNotification(TIT_LOAD_FAILED(base.getProjectDir()), ex.getMessage(), GradleProjectErrorNotifications.bulletedList(problems)); return ctx.previous.invalidate(problems.toArray(new GradleReport[0])); + } catch (ThreadDeath td) { + throw td; + } catch (Throwable t) { + // catch any possible other errors, report project loading failure - but complete the loading operation. + LOG.log(Level.SEVERE, "Internal error during loading: " + base.getProjectDir(), t); + List problems = exceptionsToProblems(ctx.project.getGradleFiles().getBuildScript(), t); + errors.openNotification(TIT_LOAD_FAILED(base.getProjectDir()), t.getMessage(), GradleProjectErrorNotifications.bulletedList(problems)); + return ctx.previous.invalidate(problems.toArray(new GradleReport[0])); } finally { loadedProjects.incrementAndGet(); } @@ -306,7 +317,7 @@ private static GradleProject loadGradleProject(ReloadContext ctx, CancellationTo if (SwingUtilities.isEventDispatchThread()) { LOG.log(FINE, "Load happened on AWT event dispatcher", new RuntimeException()); } - ProjectInfoDiskCache.QualifiedProjectInfo qinfo = new ProjectInfoDiskCache.QualifiedProjectInfo(quality, info); + ProjectInfoDiskCache.QualifiedProjectInfo qinfo = new ProjectInfoDiskCache.QualifiedProjectInfo(quality, info, start); GradleProject ret = createGradleProject(ctx.project.getGradleFiles(), qinfo); GradleArtifactStore.getDefault().processProject(ret); if (info.getMiscOnly()) { @@ -672,8 +683,8 @@ public ProjectLoaderTask(ReloadContext ctx) { public GradleProject call() throws Exception { tokenSource = GradleConnector.newCancellationTokenSource(); String msg; - if (ctx.description != null) { - msg = Bundle.FMT_ProjectLoadReason(ctx.description, ctx.previous.getBaseProject().getName()); + if (ctx.getDescription() != null) { + msg = Bundle.FMT_ProjectLoadReason(ctx.getDescription(), ctx.previous.getBaseProject().getName()); } else { msg = Bundle.LBL_Loading(ctx.previous.getBaseProject().getName()); } diff --git a/extide/gradle/src/org/netbeans/modules/gradle/loaders/NbProjectInfoCachingDescriptor.java b/extide/gradle/src/org/netbeans/modules/gradle/loaders/NbProjectInfoCachingDescriptor.java index b7fe29402404..426349967b36 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/loaders/NbProjectInfoCachingDescriptor.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/loaders/NbProjectInfoCachingDescriptor.java @@ -59,7 +59,7 @@ public GradleCommandLine gradleCommandLine() { @Override public void onLoad(String target, NbProjectInfo model) { Quality quality = model.hasException() ? SIMPLE : FULL_ONLINE; - ProjectInfoDiskCache.QualifiedProjectInfo qinfo = new ProjectInfoDiskCache.QualifiedProjectInfo(quality, model); + ProjectInfoDiskCache.QualifiedProjectInfo qinfo = new ProjectInfoDiskCache.QualifiedProjectInfo(quality, model, System.currentTimeMillis()); GradleFiles gf = new GradleFiles(structure.getProjectDir(target), true); ProjectInfoDiskCache.get(gf).storeData(qinfo); } diff --git a/extide/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java b/extide/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java index 32832f8f5fcb..fcc47567ceef 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/spi/newproject/TemplateOperation.java @@ -58,6 +58,8 @@ import org.netbeans.modules.gradle.GradleProjectLoader; import org.netbeans.modules.gradle.ProjectTrust; import org.netbeans.modules.gradle.api.GradleProjects; +import org.netbeans.modules.gradle.api.NbGradleProject; +import org.netbeans.modules.gradle.api.NbGradleProject.LoadOptions; import org.netbeans.modules.gradle.api.NbGradleProject.Quality; import org.netbeans.modules.gradle.execute.EscapeProcessingOutputStream; import org.netbeans.modules.gradle.execute.GradlePlainEscapeProcessor; @@ -526,7 +528,7 @@ public Set execute() { //Just load the project into the cache. GradleProjectLoader loader = nbProject.getLookup().lookup(GradleProjectLoader.class); if (loader != null) { - loader.loadProject(Quality.FULL_ONLINE, null, true, false); + loader.loadProject(NbGradleProject.loadOptions(Quality.FULL_ONLINE).setIgnoreCache(true)); } } Set ret = new LinkedHashSet<>();