Copilot commented on code in PR #4032:
URL: 
https://github.com/apache/incubator-kie-tools/pull/4032#discussion_r4166819955


##########
packages/drools-lsp/drools-lsp-server/src/main/java/org/drools/lsp/server/ProjectEngine.java:
##########
@@ -0,0 +1,224 @@
+/*
+ * 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.drools.lsp.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The project's own Drools engine, driven through the bridge class.
+ *
+ * <p>The project loader sees the project's classpath and the platform classes
+ * only. The server's loader is deliberately not its parent: the server jar
+ * carries a DRL parser and an ANTLR runtime of its own, and the project's
+ * versions must win inside the engine. The bridge class is defined from this
+ * server's own jar into a child of the project loader, so its references to
+ * kie-api resolve against the project.
+ */
+final class ProjectEngine implements Engine {
+
+    static final String BRIDGE_CLASS = "org.drools.lsp.bridge.EngineBridge";
+    static final String COMPILER_CLASS = 
"org.drools.compiler.kie.builder.impl.KieServicesImpl";
+    static final String MVEL_CLASS = "org.drools.mvel.MVELConstraint";
+    private static final String BRIDGE_PACKAGE_PREFIX = 
"org.drools.lsp.bridge.";
+    private static final Logger logger = 
Logger.getLogger(ProjectEngine.class.getName());
+
+    private final Set<Path> entries;
+    private final URLClassLoader projectLoader;
+    private final Class<?> bridge;
+    private final Method build;
+    private volatile boolean closed;
+
+    private ProjectEngine(Set<Path> entries, URLClassLoader projectLoader, 
Class<?> bridge, Method build) {
+        this.entries = entries;
+        this.projectLoader = projectLoader;
+        this.bridge = bridge;
+        this.build = build;
+    }
+
+    static ProjectEngine over(Collection<Path> classpathEntries) {
+        Set<Path> entries = new LinkedHashSet<>(classpathEntries);
+        List<URL> urls = new ArrayList<>(entries.size());
+        for (Path entry : entries) {
+            try {
+                urls.add(entry.toUri().toURL());
+            } catch (MalformedURLException e) {
+                logger.fine(() -> "Skipping classpath entry " + entry + ": " + 
e.getMessage());
+            }
+        }
+        URLClassLoader projectLoader = new URLClassLoader("drools-lsp-project",
+                urls.toArray(new URL[0]), 
ClassLoader.getPlatformClassLoader());
+        Class<?> bridge = null;
+        Method build = null;
+        if (hasCompiler(projectLoader)) {
+            try {
+                bridge = Class.forName(BRIDGE_CLASS, true, new 
BridgeClassLoader(projectLoader));
+                build = bridge.getMethod("build", Map.class);
+            } catch (ReflectiveOperationException | LinkageError e) {
+                logger.log(Level.WARNING, "A Drools engine is on the project 
classpath but the compile bridge did not link", e);
+            }
+        }
+        ProjectEngine engine = new ProjectEngine(entries, projectLoader, 
bridge, build);
+        if (build == null) {
+            // Permanently unavailable: release the loader's open jar handles 
now
+            // rather than holding them until the next classpath change 
replaces it.
+            engine.close();
+        }
+        return engine;
+    }
+
+    Set<Path> entries() {
+        return entries;
+    }
+
+    @Override
+    public boolean available() {
+        return build != null && !closed;
+    }
+
+    @Override
+    public List<Map<String, Object>> build(Map<String, String> drlByPath, 
Duration timeout) throws Exception {
+        if (!available()) {
+            throw new IllegalStateException("No Drools engine available");
+        }
+        AtomicReference<Object> outcome = new AtomicReference<>();
+        Thread worker = new Thread(() -> {
+            
Thread.currentThread().setContextClassLoader(bridge.getClassLoader());
+            try {
+                outcome.set(build.invoke(null, drlByPath));
+            } catch (Throwable t) {
+                outcome.set(t);
+            }
+        }, "drools-lsp-engine-build");
+        worker.setDaemon(true);
+        worker.start();
+        worker.join(Math.max(1, timeout.toMillis()));
+        if (worker.isAlive()) {
+            worker.interrupt();
+            throw new TimeoutException("The Drools engine did not finish the 
build within " + timeout.toSeconds()
+                    + " seconds; raise drools.lsp.compile.timeoutSeconds if 
the group needs longer");

Review Comment:
   Interrupting the worker does not guarantee that the Drools build has 
stopped. If it ignores or delays interruption, this method returns while the 
daemon still uses the project loader; the compile gate then permits another 
build, and an engine replacement can close that loader underneath the first 
one. This breaks the one-build-at-a-time guarantee and can retain replaced 
loaders indefinitely. Keep the gate/loader owned until termination is 
confirmed, or run timed builds behind an isolation boundary that can actually 
be terminated.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to