Through my own plugin, I need to know about the existence of a file in the workspace of a Jenkins node. But the file can't be found whereas it really exists on the node (D:\workspace\JOB_NAME\test.txt
).
public class MyBuilder extends Builder implements Serializable {
private static final long serialVersionUID = 1L;
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {
FilePath fp = new FilePath(build.getWorkspace(), "test.txt");
String result = fp.act(new FileCallable<String>() {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File file, VirtualChannel channel) throws IOException, InterruptedException {
if (file.getAbsoluteFile().exists()){
return file.getName() + " exists.";
} else {
return file.getName() + " doesn't exist.";
}
}
});
System.out.println("result: " + result);
Result:
FATAL: remote file operation failed: D:\workspace\JOB_NAME\test.txt at hudson.remoting.Channel@182752b:Node
hudson.util.IOException2: remote file operation failed: D:\workspace\JOB_NAME\test.txt at hudson.remoting.Channel@182752b:Node
at hudson.FilePath.act(FilePath.java:900)
at hudson.FilePath.act(FilePath.java:877)
at com.company.tlb.proj.MyBuilder.perform(Unknown Source)
at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:19)
at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:804)
at hudson.model.Build$BuildExecution.build(Build.java:199)
at hudson.model.Build$BuildExecution.doRun(Build.java:160)
at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:586)
at hudson.model.Run.execute(Run.java:1575)
at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:46)
at hudson.model.ResourceController.execute(ResourceController.java:88)
at hudson.model.Executor.run(Executor.java:237)
Caused by: java.io.IOException: Unable to serialize hudson.FilePath$FileCallableWrapper@18b91cb
at hudson.remoting.UserRequest.serialize(UserRequest.java:166)
at hudson.remoting.UserRequest.<init>(UserRequest.java:62)
at hudson.remoting.Channel.call(Channel.java:671)
at hudson.FilePath.act(FilePath.java:893)
... 11 more
Caused by: java.io.NotSerializableException: java.io.PrintStream
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
at hudson.remoting.UserRequest._serialize(UserRequest.java:155)
at hudson.remoting.UserRequest.serialize(UserRequest.java:164)
... 14 more
What am I doing wrong?
Links:
You're passing an anonymous inner-class to act(...)
which has an implicit reference to its parent class, MyBuilder
. My guess is that even though MyBuilder
implements Serializable
, it can't actually be serialized due to some reference to another object that isn't Serializable
(e.g. a java.io.PrintStream
as the stack-trace indicates).
Make your instance of FileCallable<String>
a static inner class to get rid of the implicit reference to its parent:
private static class MyFileCallable implements FileCallable<String> {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File file, VirtualChannel channel) throws IOException, InterruptedException {
if (file.getAbsoluteFile().exists()){
return file.getName() + " exists.";
} else {
return file.getName() + " doesn't exist.";
}
}
}
And then use that instead:
String result = fp.act(new MyFileCallable());
Update: Here's a link to Java's Nested Classes tutorial which outlines the relationship between an instance of an inner class an its enclosing outer class instance:
An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance.
This is also explained in more technical terms in the JLS (§8.1.3 Inner Classes and Enclosing Instances).
With that in mind, this quote from the Serializable
doc explains why you encountered the NotSerializableException
:
When traversing a graph, an object may be encountered that does not support the Serializable interface. In this case the NotSerializableException will be thrown and will identify the class of the non-serializable object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With