提交 c01333c3 编写于 作者: K Kohsuke Kawaguchi

[FIXED JENKINS-18629]

Consult Descriptor.newInstance() even when proccessing is in the middle
of JSON tree.
(cherry picked from commit d15c1550)
上级 793b6826
...@@ -42,7 +42,7 @@ THE SOFTWARE. ...@@ -42,7 +42,7 @@ THE SOFTWARE.
<properties> <properties>
<staplerFork>true</staplerFork> <staplerFork>true</staplerFork>
<stapler.version>1.218</stapler.version> <stapler.version>1.220</stapler.version>
<spring.version>2.5.6.SEC03</spring.version> <spring.version>2.5.6.SEC03</spring.version>
<groovy.version>1.8.9</groovy.version> <groovy.version>1.8.9</groovy.version>
</properties> </properties>
......
...@@ -29,6 +29,7 @@ import hudson.RelativePath; ...@@ -29,6 +29,7 @@ import hudson.RelativePath;
import hudson.XmlFile; import hudson.XmlFile;
import hudson.BulkChange; import hudson.BulkChange;
import hudson.Util; import hudson.Util;
import hudson.init.Initializer;
import hudson.model.listeners.SaveableListener; import hudson.model.listeners.SaveableListener;
import hudson.util.FormApply; import hudson.util.FormApply;
import hudson.util.FormValidation.CheckMethod; import hudson.util.FormValidation.CheckMethod;
...@@ -62,6 +63,7 @@ import java.util.HashMap; ...@@ -62,6 +63,7 @@ import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -985,4 +987,77 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable { ...@@ -985,4 +987,77 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
public static final class Self {} public static final class Self {}
protected static Class self() { return Self.class; } protected static Class self() { return Self.class; }
/**
* Register a global {@link BindInterceptor} that uses {@link Descriptor#newInstance(StaplerRequest, JSONObject)}
* for instantiation
*/
@Initializer
public static void initGlobalBindInterceptor() {
boolean newInstance = WebApp.get(Jenkins.getInstance().servletContext).bindInterceptors.add(new BindInterceptor() {
final class Input {
final Class type;
final JSONObject json;
private Input(Class type, JSONObject json) {
this.type = type;
this.json = json;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Input rhs = (Input) o;
return json==rhs.json && type==rhs.type;
}
@Override
public int hashCode() {
return 31*type.hashCode() + json.hashCode();
}
}
private final ThreadLocal<Stack<Input>> inputs = new ThreadLocal<Stack<Input>>() {
protected Stack<Input> initialValue() {
return new Stack<Input>();
}
};
@Override
public Object instantiate(Class actualType, JSONObject json) {
if (Describable.class.isAssignableFrom(actualType)) {
Descriptor d = Jenkins.getInstance().getDescriptor(actualType);
if (d != null) {
try {
// only when Descriptor.newInstance is overridden
Method m = d.getClass().getMethod("newInstance", StaplerRequest.class, JSONObject.class);
if (m.getDeclaringClass() != Descriptor.class) {
Input newFrame = new Input(actualType,json);
if (!inputs.get().contains(newFrame)) {
// prevent infinite recursion in case Descriptor.newInstance calls right back into
// bindJSON
inputs.get().push(newFrame);
try {
StaplerRequest req = Stapler.getCurrentRequest();
if (req != null)
return d.newInstance(req, json);
} finally {
inputs.get().pop();
}
}
}
} catch (NoSuchMethodException e) {
throw new AssertionError(e); // this can't happen because Descriptor defines such a method
} catch (FormException e) {
throw new IllegalArgumentException(e);
}
}
}
return DEFAULT;
}
});
}
} }
...@@ -27,10 +27,17 @@ package hudson.model; ...@@ -27,10 +27,17 @@ package hudson.model;
import hudson.model.Descriptor.PropertyType; import hudson.model.Descriptor.PropertyType;
import hudson.tasks.Shell; import hudson.tasks.Shell;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import net.sf.json.JSONObject;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import java.util.concurrent.Callable;
public class DescriptorTest { public class DescriptorTest {
...@@ -51,4 +58,42 @@ public class DescriptorTest { ...@@ -51,4 +58,42 @@ public class DescriptorTest {
} }
} }
/**
* Make sure Descriptor.newInstance gets invoked.
*/
@Bug(18629) @Test
public void callDescriptor_newInstance() throws Exception {
rule.executeOnServer(new Callable<Object>() {
@Override
public Object call() throws Exception {
DataBoundBean v = Stapler.getCurrentRequest().bindJSON(DataBoundBean.class, new JSONObject());
assertEquals(5,v.x);
return null;
}
});
}
public static class DataBoundBean extends AbstractDescribableImpl<DataBoundBean> {
int x;
// not @DataBoundConstructor
public DataBoundBean(int x) {
this.x = x;
}
@TestExtension
public static class DescriptorImpl extends Descriptor<DataBoundBean> {
@Override
public String getDisplayName() {
return "";
}
@Override
public DataBoundBean newInstance(StaplerRequest req, JSONObject formData) throws FormException {
return new DataBoundBean(5);
}
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册