diff --git a/core/src/main/java/hudson/util/DescribableList.java b/core/src/main/java/hudson/util/DescribableList.java index 7d90a2ed745a3643b553adbe742353a898ae6b38..18642093c7f4e1fc1a013274797907c9e0ff25d8 100644 --- a/core/src/main/java/hudson/util/DescribableList.java +++ b/core/src/main/java/hudson/util/DescribableList.java @@ -270,10 +270,9 @@ public class DescribableList, D extends Descriptor> } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { - CopyOnWriteList core = copyOnWriteListConverter.unmarshal(reader, context); - try { - DescribableList r = (DescribableList)context.getRequiredType().newInstance(); + DescribableList r = (DescribableList) context.getRequiredType().asSubclass(DescribableList.class).newInstance(); + CopyOnWriteList core = copyOnWriteListConverter.unmarshal(reader, context); r.data.replaceBy(core); return r; } catch (InstantiationException e) { diff --git a/core/src/test/java/hudson/util/DescribableListTest.java b/core/src/test/java/hudson/util/DescribableListTest.java new file mode 100644 index 0000000000000000000000000000000000000000..de4449e7db9fdfc467476bee5dd754df079f5fc2 --- /dev/null +++ b/core/src/test/java/hudson/util/DescribableListTest.java @@ -0,0 +1,99 @@ +/* + * The MIT License + * + * Copyright 2018 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.util; + +import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; +import hudson.model.Describable; +import hudson.model.Descriptor; +import org.junit.Test; +import static org.junit.Assert.*; +import org.jvnet.hudson.test.Issue; + +public class DescribableListTest { + + @Issue("JENKINS-49054") + @Test + public void exceptionDuringUnmarshalling() throws Exception { + Data data = new Data(); + data.list.add(new Datum(1)); + data.list.add(new Datum(2)); + data.list.add(new Datum(3)); + XStream2 xs = new XStream2(); + xs.addCriticalField(Data.class, "list"); + String xml = xs.toXML(data); + data = (Data) xs.fromXML(xml); + assertEquals("[1, 3]", data.toString()); + } + + private static final class Data { + + final DescribableList> list = new DescribableList<>(); + + @Override + public String toString() { + return list.toString(); + } + + } + + private static final class Datum implements Describable { + + final int val; + + Datum(int val) { + this.val = val; + } + + @Override + public Descriptor getDescriptor() { + return new Descriptor(Datum.class) {}; + } + + @Override + public String toString() { + return Integer.toString(val); + } + + public static final class ConverterImpl extends AbstractSingleValueConverter { + + @Override + public boolean canConvert(Class type) { + return type == Datum.class; + } + + @Override + public Object fromString(String str) { + int val = Integer.parseInt(str); + if (val == 2) { + throw new IllegalStateException("oops"); + } + return new Datum(val); + } + + } + + } + +}