Jenkins plugin can't access file on node
java, jenkins, jenkins-plugins
Solution
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.
Problem
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: - Making your plugin behave in distributed Jenkins - Jenkins API: FilePath.java