Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jenkins plugin can't access file on node

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
like image 570
Stéphane Bruckert Avatar asked Jul 18 '13 14:07

Stéphane Bruckert


1 Answers

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.

like image 52
DannyMo Avatar answered Oct 01 '22 02:10

DannyMo