wenjin272 commented on code in PR #655: URL: https://github.com/apache/flink-agents/pull/655#discussion_r3241672079
########## runtime/src/main/java/org/apache/flink/agents/runtime/skill/repository/ClasspathSkillRepository.java: ########## @@ -0,0 +1,129 @@ +/* + * 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.apache.flink.agents.runtime.skill.repository; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Skill repository backed by a classpath resource — either a directory under {@code + * src/main/resources} (typical dev / Maven layout) or a path inside a JAR on the classpath (typical + * deployment). + * + * <p>For directory resources the underlying {@link FileSystemSkillRepository} uses the real on-disk + * path. For JAR-entry resources, the matching entries are extracted into a process-local temp + * directory (cleaned up at JVM exit). + * + * <p>When a {@link URLClassLoader} is used and the JAR does not contain an explicit directory entry + * for the resource prefix, this class falls back to scanning the classloader's URLs directly to + * synthesize the correct {@code jar:} URL. + */ +public class ClasspathSkillRepository extends FileSystemSkillRepository { + + public ClasspathSkillRepository(String resource) throws IOException { + this(resource, Thread.currentThread().getContextClassLoader()); + } + + /** + * Constructor that accepts an explicit classloader. Useful in tests to inject a {@code + * URLClassLoader} pointing at a freshly-built jar. + */ + public ClasspathSkillRepository(String resource, ClassLoader classLoader) throws IOException { + super(materialize(resource, classLoader)); + } + + private static Path materialize(String resource, ClassLoader classLoader) throws IOException { + URL url = classLoader.getResource(resource); + if (url == null) { + // Fallback for URLClassLoader: JARs without explicit directory entries will not return + // a URL for a prefix via getResource(). Scan the classloader's URLs directly to find + // any JAR entry that starts with the resource prefix. + url = findInUrlClassLoader(resource, classLoader); + } + if (url == null) { + throw new IllegalArgumentException("Classpath resource not found: " + resource); + } + String protocol = url.getProtocol(); + if ("file".equals(protocol)) { + Path p; + try { + p = Paths.get(url.toURI()); + } catch (URISyntaxException e) { + throw new IOException("Bad classpath URL: " + url, e); + } + if (Files.isDirectory(p)) { + return p; + } + if (Files.isRegularFile(p) && p.toString().toLowerCase().endsWith(".zip")) { + return SkillMaterializer.extractZipSafely(p); + } + throw new IllegalArgumentException( + "Classpath resource must be a directory or a .zip: " + p); + } + if ("jar".equals(protocol)) { + return SkillMaterializer.extractClasspathFromJar(url, resource); + } + throw new IllegalArgumentException( Review Comment: What is the significance of highlighting `URLClassLoader` and `ChildFirstClassLoader` here? I understand that ChildFirstClassLoader is also a subclass of URLClassLoader. Since we set the contextClassLoader to Flink's UserCodeClassloader before executing a JavaAction, it is used in most scenarios. However, there was an issue with the initialization of the skill-managed path in Python Actions. I have modified the code to explicitly pass the UserCodeClassloader. Typically, I understand that the Flink user JAR does not use the jar-in-jar approach. `getResourceAsStream` requires a file path as input, but the `resource` here is typically a directory name, so getResourceAsStream cannot be used. -- 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]
