Skip to content

Commit a14ed45

Browse files
committed
Giving up on jenkinsci/workflow-cps-plugin#668 and tracking code source more conservatively
1 parent f9be590 commit a14ed45

File tree

3 files changed

+31
-24
lines changed

3 files changed

+31
-24
lines changed

pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,6 @@
7575
<scope>import</scope>
7676
<type>pom</type>
7777
</dependency>
78-
<dependency>
79-
<groupId>org.jenkins-ci.plugins.workflow</groupId>
80-
<artifactId>workflow-cps</artifactId>
81-
<version>3625.v4390b_f51b_50f</version> <!-- TODO https://github.com/jenkinsci/workflow-cps-plugin/pull/668 -->
82-
</dependency>
8378
</dependencies>
8479
</dependencyManagement>
8580
<dependencies>

src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryStep.java

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,20 @@
4545
import java.lang.reflect.Method;
4646
import java.lang.reflect.Modifier;
4747
import java.net.URI;
48-
import java.net.URISyntaxException;
4948
import java.net.URL;
50-
import java.nio.file.Paths;
51-
import java.security.CodeSource;
5249
import java.util.ArrayList;
5350
import java.util.Collection;
5451
import java.util.Collections;
5552
import java.util.List;
5653
import java.util.Set;
5754
import java.util.TreeSet;
58-
import java.util.logging.Level;
5955
import java.util.logging.Logger;
6056
import edu.umd.cs.findbugs.annotations.CheckForNull;
6157
import edu.umd.cs.findbugs.annotations.NonNull;
58+
import edu.umd.cs.findbugs.annotations.Nullable;
6259
import groovy.lang.MissingPropertyException;
60+
import java.util.regex.Matcher;
61+
import java.util.regex.Pattern;
6362
import javax.inject.Inject;
6463
import jenkins.model.Jenkins;
6564
import jenkins.scm.impl.SingleSCMSource;
@@ -251,20 +250,20 @@ public static final class LoadedClasses extends GroovyObjectSupport implements S
251250
private final @NonNull String prefix;
252251
/** {@link Class#getName} minus package prefix */
253252
private final @CheckForNull String clazz;
254-
/** {@code jar:file:/…/libs/$hash.jar!/} */
255-
private final @NonNull String srcUrl;
253+
/** {@code /…/libs/$hash.jar}, or null if resuming a pre-dir2Jar build */
254+
private final @Nullable String jar;
256255

257256
LoadedClasses(String library, String libraryDirectoryName, boolean trusted, Boolean changelog, Run<?,?> run) {
258-
this(library, trusted, changelog, "", null, /* cf. LibraryAdder.retrieve */ "jar:" + new File(run.getRootDir(), "libs/" + libraryDirectoryName + ".jar").toURI() + "!/");
257+
this(library, trusted, changelog, "", null, /* cf. LibraryAdder.retrieve */ new File(run.getRootDir(), "libs/" + libraryDirectoryName + ".jar").getAbsolutePath());
259258
}
260259

261-
LoadedClasses(String library, boolean trusted, Boolean changelog, String prefix, String clazz, String srcUrl) {
260+
LoadedClasses(String library, boolean trusted, Boolean changelog, String prefix, String clazz, String jar) {
262261
this.library = library;
263262
this.trusted = trusted;
264263
this.changelog = changelog;
265264
this.prefix = prefix;
266265
this.clazz = clazz;
267-
this.srcUrl = srcUrl;
266+
this.jar = jar;
268267
}
269268

270269
@Override public Object getProperty(String property) {
@@ -288,12 +287,12 @@ public static final class LoadedClasses extends GroovyObjectSupport implements S
288287
String fullClazz = clazz != null ? clazz + '$' + property : property;
289288
loadClass(prefix + fullClazz);
290289
// OK, class really exists, stash it and await methods
291-
return new LoadedClasses(library, trusted, changelog, prefix, fullClazz, srcUrl);
290+
return new LoadedClasses(library, trusted, changelog, prefix, fullClazz, jar);
292291
} else if (clazz != null) {
293292
throw new MissingPropertyException(property, loadClass(prefix + clazz));
294293
} else {
295294
// Still selecting package components.
296-
return new LoadedClasses(library, trusted, changelog, prefix + property + '.', null, srcUrl);
295+
return new LoadedClasses(library, trusted, changelog, prefix + property + '.', null, jar);
297296
}
298297
}
299298

@@ -339,6 +338,8 @@ private static boolean isSandboxed() {
339338

340339
// TODO putProperty for static field set
341340

341+
private static final Pattern JAR_URL = Pattern.compile("jar:(file:/.+[.]jar)!/.+");
342+
342343
private Class<?> loadClass(String name) {
343344
CpsFlowExecution exec = CpsThread.current().getExecution();
344345
GroovyClassLoader loader = (trusted ? exec.getTrustedShell() : exec.getShell()).getClassLoader();
@@ -351,14 +352,22 @@ private Class<?> loadClass(String name) {
351352
if (definingLoader != loader) {
352353
throw new IllegalAccessException("cannot access " + c + " via library handle: " + definingLoader + " is not " + loader);
353354
}
354-
// Note that this goes through GroovyCodeSource.<init>(File, String), which unlike (say) URLClassLoader set the “location” to the actual file, *not* the root.
355-
CodeSource codeSource = c.getProtectionDomain().getCodeSource();
356-
if (codeSource == null) {
357-
throw new IllegalAccessException(name + " had no defined code source");
358-
}
359-
String loc = codeSource.getLocation().toString();
360-
if (!loc.startsWith(srcUrl)) {
361-
throw new IllegalAccessException(name + " was defined in " + loc + " which was not inside " + srcUrl);
355+
if (jar != null) {
356+
URL res = loader.getResource(name.replaceFirst("[$][^.]+$", "").replace('.', '/') + ".groovy");
357+
if (res == null) {
358+
throw new IllegalAccessException("Unknown where " + name + " (" + c.getProtectionDomain().getCodeSource().getLocation() + ") was loaded from");
359+
}
360+
Matcher m = JAR_URL.matcher(res.toString());
361+
if (!m.matches()) {
362+
throw new IllegalAccessException("Unexpected URL " + res);
363+
}
364+
File actual = new File(URI.create(m.group(1)));
365+
if (!actual.equals(new File(jar))) {
366+
throw new IllegalAccessException(name + " was defined in " + actual + " rather than the expected " + jar);
367+
}
368+
LOGGER.fine(() -> "loaded " + name + " from " + res + " ~ " + actual + " as expected");
369+
} else {
370+
LOGGER.fine(() -> "loaded " + name + " but resuming from an old build which did not properly record JAR location");
362371
}
363372
if (!Modifier.isPublic(c.getModifiers())) { // unlikely since Groovy makes classes implicitly public
364373
throw new IllegalAccessException(c + " is not public");

src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryStepTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.Arrays;
3737
import java.util.Collections;
3838
import java.util.List;
39+
import java.util.logging.Level;
3940

4041
import jenkins.plugins.git.GitSCMSource;
4142
import jenkins.plugins.git.GitSampleRepoRule;
@@ -56,6 +57,7 @@
5657
import org.jvnet.hudson.test.BuildWatcher;
5758
import org.jvnet.hudson.test.Issue;
5859
import org.jvnet.hudson.test.JenkinsRule;
60+
import org.jvnet.hudson.test.LoggerRule;
5961

6062
@Issue("JENKINS-39450")
6163
public class LibraryStepTest {
@@ -64,6 +66,7 @@ public class LibraryStepTest {
6466
@Rule public JenkinsRule r = new JenkinsRule();
6567
@Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
6668
@Rule public GitSampleRepoRule sampleRepo2 = new GitSampleRepoRule();
69+
@Rule public LoggerRule logging = new LoggerRule().record(LibraryStep.class, Level.FINE);
6770

6871
@Test public void configRoundtrip() throws Exception {
6972
StepConfigTester stepTester = new StepConfigTester(r);

0 commit comments

Comments
 (0)