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

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


The following commit(s) were added to refs/heads/delivery by this push:
     new 4d6c0e9f2c #4923: access to gradle internal APIs is protected from 
failing the project load, just logs a notification.
     new 31c6618dc5 Merge pull request #4936 from 
sdedic/gradle/internals-wrapped
4d6c0e9f2c is described below

commit 4d6c0e9f2c16b1c8384ada1d03a1dd7f2caa38b7
Author: Svata Dedic <[email protected]>
AuthorDate: Mon Nov 7 18:25:22 2022 +0100

    #4923: access to gradle internal APIs is protected from failing the project 
load, just logs a notification.
---
 .../gradle/tooling/GradleInternalAdapter.java      | 209 +++++++++++++++++++++
 .../gradle/tooling/NbProjectInfoBuilder.java       |  79 ++++----
 2 files changed, 241 insertions(+), 47 deletions(-)

diff --git 
a/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/GradleInternalAdapter.java
 
b/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/GradleInternalAdapter.java
new file mode 100644
index 0000000000..515cfc7f1b
--- /dev/null
+++ 
b/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/GradleInternalAdapter.java
@@ -0,0 +1,209 @@
+/*
+ * 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.tooling;
+
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Supplier;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.internal.plugins.PluginManagerInternal;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.provider.PropertyInternal;
+import org.gradle.api.internal.provider.ProviderInternal;
+import org.gradle.api.internal.provider.ValueSupplier;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.reflect.HasPublicType;
+import org.gradle.plugin.use.PluginId;
+import org.gradle.util.VersionNumber;
+import 
org.netbeans.modules.gradle.tooling.NbProjectInfoBuilder.ExceptionCallable;
+import org.netbeans.modules.gradle.tooling.NbProjectInfoBuilder.ValueAndType;
+
+/**
+ * Adapts to various Gradle implementations. As *.internal.* interfaces may 
change between
+ * releases, operations on them may be customized by this Adapter. The adapter 
should be compiled
+ * against the Gradle distribution shipped with NetBeans, but should use 
reflection to access 
+ * the relevant data if the internal API changes between versions.
+ * 
+ * @author sdedic
+ */
+public class GradleInternalAdapter {
+    private static final Logger LOG =  
Logging.getLogger(NbProjectInfoBuilder.class);
+
+    private final Project project;
+    private final VersionNumber gradleVersion;
+    /**
+     * Accummulates error messages, so that just one problem is logger a given 
type of error.
+     */
+    private Set<String> reportedIncompatibilities = new HashSet<>();
+    
+    protected NbProjectInfoModel model;
+    
+    /**
+     * Guards {@link #pluginManager} and {@link #registry} initialization
+     */
+    protected boolean pluginsInitialized;
+    protected PluginManagerInternal pluginManager;
+    protected PluginRegistry registry;
+
+    public GradleInternalAdapter(Project project) {
+        this.project = project;
+        this.gradleVersion = 
VersionNumber.parse(project.getGradle().getGradleVersion());
+    }
+    
+    boolean initPlugins() {
+        if (!pluginsInitialized) {
+            if (project.getPluginManager() instanceof PluginManagerInternal) {
+                pluginManager = 
(PluginManagerInternal)project.getPluginManager();
+            }
+            if (project instanceof ProjectInternal) {
+                registry = safeCall(() -> 
((ProjectInternal)project).getServices().get(PluginRegistry.class), "plugin 
registry").orElse(null);
+            } else {
+                registry = null;
+            }
+        }
+        return pluginManager != null;
+    }
+    
+    public void setModel(NbProjectInfoModel model) {
+        this.model = model;
+    }
+    
+    protected boolean isFixedValue(String description, 
ValueSupplier.ExecutionTimeValue etv) {
+        return etv.isFixedValue();
+    }
+    
+    public boolean isMutableType(Object potentialValue) {
+        if (potentialValue instanceof PropertyInternal) {
+            return true;
+        } else if ((potentialValue instanceof NamedDomainObjectContainer) && 
(potentialValue instanceof HasPublicType)) {
+            return true;
+        } else if (potentialValue instanceof Iterable || potentialValue 
instanceof Map) {
+            return true;
+        }
+        return false;
+    }
+    
+    public boolean hasPluginManager() {
+        return initPlugins();
+    }
+    
+    public ValueAndType findPropertyValueInternal(String propName, Object val) 
{
+        return safeCall(() -> {
+            if (val instanceof ProviderInternal) {
+                ProviderInternal provided = (ProviderInternal)val;
+                ValueSupplier.ExecutionTimeValue etv;
+                try {
+                    etv = provided.calculateExecutionTimeValue();
+                } catch (RuntimeException ex) {
+                    // probably expected, ignore
+                    return new ValueAndType(provided.getType());
+                }
+                if (isFixedValue("property " + propName, etv)) {
+                    return new ValueAndType(provided.getType(), 
etv.getFixedValue());
+                } else {
+                    return new ValueAndType(provided.getType());
+                }
+            } else {
+                return new ValueAndType(val != null ? val.getClass() : null, 
val);
+            }
+        }, "property " + propName).orElse(null);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private static <T extends Throwable> void sneakyThrow(Throwable exception) 
throws T {
+            throw (T) exception;
+    }        
+    
+    private <T, E extends Throwable> T sinceGradleOrDefault(String version, 
NbProjectInfoBuilder.ExceptionCallable<T, E> c, Supplier<T> def) {
+        if (gradleVersion.compareTo(VersionNumber.parse(version)) >= 0) {
+            try {
+                return c.call();
+            } catch (RuntimeException | Error e) {
+                throw e;
+            } catch (Throwable t) {
+                sneakyThrow(t);
+                return null;
+            }
+        } else {
+            return def.get();
+        }
+    }
+
+    public Optional<PluginId> findPluginId(Class fc) {
+        if (!initPlugins()) {
+            return Optional.empty();
+        }
+        // with Gradle 7.1+, plugins can be better enumerated. Prior to 7.1 I 
can only get IDs for registry-supplied plugins.
+        Optional<PluginId> id = sinceGradleOrDefault("7.1", () -> safeCall(() 
-> (PluginId)pluginManager.findPluginIdForClass(fc).orElse(null), "plugins"), 
Optional::empty); // NOI18N
+        if (id.isPresent() || registry == null) {
+            return id;
+        }
+        return safeCall(() -> registry.findPluginForClass(fc).orElse(null), 
"plugin class " + fc.getName());
+    }
+    
+    private void noteAndLogError(Throwable ex, String description) {
+        String msg = "Error inspecting " + (description == null ? "project" : 
description);
+        model.noteProblem(msg + ": " + ex.toString());
+        LOG.log(LogLevel.LIFECYCLE, msg, ex);
+    }
+    
+    protected <T, E extends Throwable> Optional<T> 
safeCall(ExceptionCallable<T, E> sup, String description) {
+        try {
+            return Optional.ofNullable(sup.call());
+        } catch (RuntimeException ex) {
+            noteAndLogError(ex, description);
+            return Optional.empty();
+        } catch (Error ex) {
+            if (reportedIncompatibilities.add(ex.toString())) {
+                noteAndLogError(ex, description);
+            }
+            return Optional.empty();
+        } catch (Throwable t) {
+            sneakyThrow(t);
+            return null;
+        }
+    }
+
+    public static class Gradle76 extends GradleInternalAdapter {
+        private static Optional<Method> refHasValue;
+
+        public Gradle76(Project project) {
+            super(project);
+        }
+        
+        @Override
+        protected boolean isFixedValue(String description, 
ValueSupplier.ExecutionTimeValue etv) {
+            if (refHasValue == null) {
+                refHasValue = safeCall(() -> 
ValueSupplier.ExecutionTimeValue.class.getMethod("hasFixedValue"), "Gradle 7.6+ 
ExecutionTimeValue");
+            }
+            if (refHasValue.isPresent()) {
+                return safeCall(() -> (Boolean)refHasValue.get().invoke(etv), 
description).orElse(false);
+            } else {
+                return false;
+            }
+        }
+    }
+}
diff --git 
a/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java
 
b/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java
index 63fd8dbf86..dae86ef964 100644
--- 
a/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java
+++ 
b/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java
@@ -85,12 +85,7 @@ import org.gradle.api.distribution.DistributionContainer;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.initialization.IncludedBuild;
-import org.gradle.api.internal.plugins.PluginManagerInternal;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.provider.PropertyInternal;
 import org.gradle.api.internal.provider.ProviderInternal;
-import org.gradle.api.internal.provider.ValueSupplier.ExecutionTimeValue;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.plugins.ExtensionAware;
@@ -183,14 +178,34 @@ class NbProjectInfoBuilder {
 
     final Project project;
     final VersionNumber gradleVersion;
+    final GradleInternalAdapter adapter;
+
+    public static final class ValueAndType {
+        final Class type;
+        final Optional<Object> value;
+
+        public ValueAndType(Class type, Object value) {
+            this.type = type;
+            this.value = Optional.of(value);
+        }
+
+        public ValueAndType(Class type) {
+            this.type = type;
+            this.value = Optional.empty();
+        }
+    }
 
     NbProjectInfoBuilder(Project project) {
         this.project = project;
         this.gradleVersion = 
VersionNumber.parse(project.getGradle().getGradleVersion());
+        // checked that version 7.6.0 > 7.6.0-rc-1 in the VersionNumber order
+        this.adapter = sinceGradleOrDefault("7.6.0-rc-1", () -> new 
GradleInternalAdapter.Gradle76(project), () -> new 
GradleInternalAdapter(project));
     }
+    
+    private NbProjectInfoModel model = new NbProjectInfoModel();
 
     public NbProjectInfo buildAll() {
-        NbProjectInfoModel model = new NbProjectInfoModel();
+        adapter.setModel(model);
         runAndRegisterPerf(model, "meta", this::detectProjectMetadata);
         detectProps(model);
         detectLicense(model);
@@ -217,7 +232,7 @@ class NbProjectInfoBuilder {
         storeGlobalTypes(model);
         return model;
     }
-
+    
     @SuppressWarnings("null")
     private void detectDistributions(NbProjectInfoModel model) {
         if (project.getPlugins().hasPlugin("distribution")) {
@@ -365,29 +380,15 @@ class NbProjectInfoBuilder {
      * @param model 
      */
     private void detectAdditionalPlugins(NbProjectInfoModel model) {
-        final PluginManagerInternal pmi;
-        PluginRegistry reg;
-        if (project.getPluginManager() instanceof PluginManagerInternal) {
-            pmi = (PluginManagerInternal)project.getPluginManager();
-        } else {
+        if (!adapter.hasPluginManager()) {
             return;
         }
-        if (project instanceof ProjectInternal) {
-            reg = 
((ProjectInternal)project).getServices().get(PluginRegistry.class);
-        } else {
-            reg = null;
-        }
         LOG.lifecycle("Detecting additional plugins");
         final Set<String> plugins = new LinkedHashSet<>();
         
         project.getPlugins().matching((Plugin p) -> {
             for (Class c = p.getClass(); c != null && c != Object.class; c = 
c.getSuperclass()) {
-                Class fc = c;
-                // with Gradle 7.1+, plugins can be better enumerated. Prior 
to 7.1 I can only get IDs for registry-supplied plugins.
-                Optional<PluginId> id = sinceGradleOrDefault("7.1", () -> 
pmi.findPluginIdForClass(fc), Optional::empty); // NOI18N
-                if (!id.isPresent() && reg != null) {
-                    id = reg.findPluginForClass(c);
-                }
+                Optional<PluginId> id = adapter.findPluginId(c);
                 if (id.isPresent()) {
                     LOG.info("Plugin: {} -> {}", id.get(), p);
                     plugins.add(id.get().getId());
@@ -537,14 +538,7 @@ class NbProjectInfoBuilder {
     }
     
     private boolean isMutableType(Object potentialValue) {
-        if (potentialValue instanceof PropertyInternal) {
-            return true;
-        } else if ((potentialValue instanceof NamedDomainObjectContainer) && 
(potentialValue instanceof HasPublicType)) {
-            return true;
-        } else if (potentialValue instanceof Iterable || potentialValue 
instanceof Map) {
-            return true;
-        }
-        return false;
+        return adapter.isMutableType(potentialValue);
     }
     
     private void inspectObjectAndValues0(Class clazz, Object object, String 
prefix, Map<String, Map<String, String>> globalTypes, Map<String, String> 
propertyTypes, Map<String, Object> defaultValues, Set<String> excludes, boolean 
type) {
@@ -640,24 +634,15 @@ class NbProjectInfoBuilder {
                 // Provider must NOT be asked for a value, otherwise it might 
run a Task in order to compute
                 // the value.
                 try {
+                    value = mclazz.getProperty(object, propName);
                     if (Provider.class.isAssignableFrom(t)) {
-                        Object potentialValue = mclazz.getProperty(object, 
propName);
-                        if (potentialValue instanceof ProviderInternal) {
-                            ProviderInternal provided = (ProviderInternal) 
potentialValue;
-                            t = provided.getType();
-                            ExecutionTimeValue etv;
-                            etv = provided.calculateExecutionTimeValue();
-                            if (etv.isFixedValue()) {
-                                value = etv.getFixedValue();
-                            }
-                        } else {
-                            value = potentialValue;
-                            if (value != null) {
-                                t = value.getClass();
+                        ValueAndType vt = 
adapter.findPropertyValueInternal(propName, value);
+                        if (vt != null) {
+                            t = vt.type;
+                            if (vt.value.isPresent()) {
+                                value = vt.value.get();
                             }
                         }
-                    } else {
-                        value = mclazz.getProperty(object, propName);
                     }
                 } catch (RuntimeException ex) {
                     // just ignore - the property value cannot be obtained
@@ -1611,7 +1596,7 @@ class NbProjectInfoBuilder {
                 .collect(Collectors.toSet());
     }
     
-    private interface ExceptionCallable<T, E extends Throwable> {
+    interface ExceptionCallable<T, E extends Throwable> {
         public T call() throws E;
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

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

Reply via email to