NullPointerException and DataBoundConstructor

42 views
Skip to first unread message

Jens Keller

unread,
Apr 26, 2016, 6:26:41 AM4/26/16
to Jenkins Developers
Hi, 

I wrote an SCM plugin some time ago and from time to time it is throwing a NullPointerException at the following location:
 @Override
 
public boolean checkout(AbstractBuild<?, ?> build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws IOException, InterruptedException {
   
return innerScm.checkout(new FilePath(changelogFile), getRevisionStateFileFileForBuild(build), workspace);
 
}


I'm using "innerScm" to have an internal API that is easier to unit-test for me, and in the "outer" scm there is basically only this adapter code.

What is null is the "innerScm".
I also see Null pointer exceptions thrown at this location sometimes:

        @Override

       public ChangeLogParser createChangeLogParser() {

               return new ChangeLogParser() {

                       @SuppressWarnings("rawtypes")

                       @Override

                       public ChangeLogSet<? extends Entry> parse(AbstractBuild build, File changelogFile) throws IOException {

                               return new HanaChangeLogSet(build, innerScm.parseChangeLog(changelogFile));

                       }

               };

       }


I don't understand how this can happen, because I only have the following constructor:

private final transient DatabaseInnerSCM innerScm;

@DataBoundConstructor
public DatabaseSCM(String connection, String objectFilter, boolean shouldDownloadContent, RepositoryBrowser<HanaChangeLogSet.Entry> browser) {
 
this.connection = connection;
 
this.objectFilter = objectFilter;
 
this.shouldDownloadContent = shouldDownloadContent;
 
this.browser = browser;

 Repository repository = new SQLRepository(new SQLConnectionProviderImpl());

 ObjectFilter[] filters = new ObjectFilterParser().parseMultiple(this.objectFilter);

 this.innerScm = new DatabaseInnerSCM(repository, filters, shouldDownloadContent);

}


So from the code I don't see how this could become null when going through the constructor.

Does the Jenkins framework have any other way to assign the field than using the data-bound constructor?


Best regards

Jens

Robert Sandell

unread,
Apr 26, 2016, 8:30:36 AM4/26/16
to jenkin...@googlegroups.com
XStream deserialization is not using the constructor, and since the innerScm field is transient the field won't be on disk.
You should recreate it in a readResolve method.

/B

--
You received this message because you are subscribed to the Google Groups "Jenkins Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-de...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jenkinsci-dev/1c092fb3-893d-4526-80a5-4e68d1136f76%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Robert Sandell
Software Engineer
CloudBees Inc.

Jens Keller

unread,
Apr 26, 2016, 8:43:07 AM4/26/16
to Jenkins Developers
Hi Robert,

I see...this makes sense, thanks a lot!!

Currently I have a hard time reproducing the issue, can you tell me under which circumstances the data-bound constructor is invoked, and when the "readResolved" / XStream deserialization is being used?

Ideally I will also write a regression test for this, therefore it would be good to know.

Best regards
Jens

Slide

unread,
Apr 26, 2016, 9:43:52 AM4/26/16
to Jenkins Developers
DataBoundConstructor is called when the confguration screen is POSTed. XStream deserialization is done when the item is being loaded from disk.

Jens Keller

unread,
Apr 26, 2016, 11:17:17 AM4/26/16
to Jenkins Developers
Thanks a lot to all.
I've created a regression test like this - just create a new job based on another job's config XML, which failed when executing it.
After implementing the readResolve method the test passed.

The following page I found also very helpful, though my issue here is not a backward compatibility/migration topic

Best regards
Jens

@Test
public void givenJobCreatedFromXml_shouldWork() throws Exception {
    createInMemoDB
("MYDB");
   
FreeStyleProject project1 = createProjectWithScmForNamedConnection("project1", "MYDB");
   
InputStream xmlConfig = new FileInputStream(project1.getConfigFile().getFile());
   
FreeStyleProject project2 = (FreeStyleProject) j.hudson.createProjectFromXML("project2", xmlConfig);
    triggerBuildAndAssert
(project2, Result.SUCCESS);

}

Reply all
Reply to author
Forward
0 new messages