Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIX JENKINS-40266] Allow override UserProperty.setUser(User) (#2655)
UserProperty may contain nested objects that depend on User. 
On User reconfiguration setUser(User) is called so it should be non-final to have ability override it and update references in nested objects.

Signed-off-by: Kanstantsin Shautsou <kanstantsin.sha@gmail.com>
  • Loading branch information
KostyaSha authored and daniel-beck committed Dec 6, 2016
1 parent 43519b1 commit 9172bca
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 3 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/model/UserProperty.java
Expand Up @@ -58,7 +58,7 @@ public abstract class UserProperty implements ReconfigurableDescribable<UserProp
*/
protected transient User user;

/*package*/ final void setUser(User u) {
protected void setUser(User u) {
this.user = u;
}

Expand Down
164 changes: 162 additions & 2 deletions test/src/test/java/hudson/model/UserPropertyTest.java
@@ -1,12 +1,34 @@
package hudson.model;

import static org.junit.Assert.assertNotNull;

import com.google.common.base.Throwables;
import jenkins.model.Jenkins;
import org.apache.commons.io.FileUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import org.jvnet.hudson.test.recipes.LocalData;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;

import static hudson.model.User.idStrategy;
import static hudson.model.UserPropertyTest.InnerUserClass.TEST_FILE;
import static java.lang.System.currentTimeMillis;
import static java.util.Collections.emptyMap;
import static org.apache.commons.io.FileUtils.writeStringToFile;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;

/**
* @author Kohsuke Kawaguchi
Expand Down Expand Up @@ -50,4 +72,142 @@ public UserProperty newInstance(User user) {
}
}
}

@Test
@LocalData
public void nestedUserReference() throws Exception {
// first time it loads from FS into object
User user = User.get("nestedUserReference", false, emptyMap());
assertThat("nested reference should be updated after jenkins start", user, nestedUserSet());

File testFile = new File(j.getInstance().getRootDir() + "/users/nesteduserreference/" + TEST_FILE);
List<String> fileLines = FileUtils.readLines(testFile);
assertThat(fileLines, hasSize(1));

j.configRoundtrip(user);

user = User.get("nestedUserReference", false, Collections.emptyMap());
assertThat("nested reference should exist after user configuration change", user, nestedUserSet());

testFile = new File(j.getInstance().getRootDir() + "/users/nesteduserreference/" + TEST_FILE);
fileLines = FileUtils.readLines(testFile);
assertThat(fileLines, hasSize(1));
}

public static Matcher<User> nestedUserSet() {
return new BaseMatcher<User>() {
@Override
public boolean matches(Object item) {
User user = (User) item;
assertThat(user, notNullValue());
final SetUserUserProperty prop = user.getProperty(SetUserUserProperty.class);
assertThat(prop, notNullValue());
assertThat(prop.getOwner(), notNullValue());
assertThat(prop.getOwner(), is(user));

final InnerUserClass innerUserClass = prop.getInnerUserClass();
assertThat(innerUserClass, notNullValue());
final User innerUser = innerUserClass.getUser();
assertThat(innerUser, notNullValue());
assertThat(innerUser, is(user));
return true;
}

@Override
public void describeTo(Description description) {
description.appendText("User object should contain initialised inner fields");
}
};
}

/**
* User property that need update User object reference for InnerUserClass.
*/
public static class SetUserUserProperty extends UserProperty {
private InnerUserClass innerUserClass = new InnerUserClass();

@DataBoundConstructor
public SetUserUserProperty() {
}

public InnerUserClass getInnerUserClass() {
return innerUserClass;
}

public User getOwner() {
return user;
}

@Override
protected void setUser(User u) {
super.setUser(u);
innerUserClass.setUser(u);
}

public Object readResolve() {
if (innerUserClass == null) {
innerUserClass = new InnerUserClass();
}
return this;
}

@TestExtension
public static class DescriptorImpl extends UserPropertyDescriptor {
@Override
public UserProperty newInstance(User user) {
if (user.getId().equals("nesteduserreference")) {
return new SetUserUserProperty();
}
return null;
}
}
}

/**
* Class that should get setUser(User) object reference update.
*/
public static class InnerUserClass extends AbstractDescribableImpl<InnerUserClass> {
public static final String TEST_FILE = "test.txt";
private transient User user;

@DataBoundConstructor
public InnerUserClass() {
}

public User getUser() {
return user;
}

/**
* Should be initialised separately.
*/
public void setUser(User user) {
this.user = user;
try {
writeStringToFile(getUserFile(), String.valueOf(currentTimeMillis()), true);
} catch (IOException e) {
Throwables.propagate(e);
}
}

private File getUserFile() throws IOException {
final File usersRootDir = new File(Jenkins.getInstance().getRootDir(), "users");
final File userDir = new File(usersRootDir, idStrategy().filenameOf(user.getId()));
final File userFile = new File(userDir, TEST_FILE);
if (!userFile.exists()) {
userFile.createNewFile();
}
return userFile;
}

@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}

@TestExtension
public static class DescriptorImpl extends Descriptor<InnerUserClass> {
}
}

}
@@ -0,0 +1,2 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core"/>
@@ -0,0 +1,3 @@
<?xml version='1.0' encoding='UTF-8'?>
<hudson>
</hudson>
@@ -0,0 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<user>
<fullName>nesteduserreference</fullName>
<properties>
<hudson.model.UserPropertyTest_-SetUserUserProperty>
<innerUserClass/>
</hudson.model.UserPropertyTest_-SetUserUserProperty>
</properties>
</user>

0 comments on commit 9172bca

Please sign in to comment.