diff --git a/core/pom.xml b/core/pom.xml index 4455d7b9fc..90a39c3a48 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -246,6 +246,20 @@ THE SOFTWARE. + + + xpp3 + xpp3 + 1.1.4c + + + net.sf.kxml + kxml2 + 2.3.0 + jfree jfreechart @@ -452,11 +466,6 @@ THE SOFTWARE. spring-aop ${spring.version} - - xpp3 - xpp3 - 1.1.4c - junit junit diff --git a/core/src/main/java/hudson/Main.java b/core/src/main/java/hudson/Main.java index 896ae4dd53..11cb516cee 100644 --- a/core/src/main/java/hudson/Main.java +++ b/core/src/main/java/hudson/Main.java @@ -144,7 +144,7 @@ public class Main { int ret; try (OutputStream os = Files.newOutputStream(tmpFile.toPath()); Writer w = new OutputStreamWriter(os,"UTF-8")) { - w.write(""); + w.write(""); w.write(""); w.flush(); diff --git a/core/src/main/java/hudson/XmlFile.java b/core/src/main/java/hudson/XmlFile.java index 243318e229..e2db72a365 100644 --- a/core/src/main/java/hudson/XmlFile.java +++ b/core/src/main/java/hudson/XmlFile.java @@ -26,7 +26,7 @@ package hudson; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.UnmarshallingContext; -import com.thoughtworks.xstream.io.xml.Xpp3Driver; +import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import hudson.diagnosis.OldDataMonitor; import hudson.model.Descriptor; import hudson.util.AtomicFileWriter; @@ -39,7 +39,6 @@ import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.Locator2; import org.xml.sax.helpers.DefaultHandler; - import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import java.io.BufferedInputStream; @@ -187,7 +186,7 @@ public final class XmlFile { mkdirs(); AtomicFileWriter w = new AtomicFileWriter(file); try { - w.write("\n"); + w.write("\n"); beingWritten.put(o, null); writing.set(file); try { @@ -352,13 +351,14 @@ public final class XmlFile { /** * {@link XStream} instance is supposed to be thread-safe. */ - private static final XStream DEFAULT_XSTREAM = new XStream2(); private static final Logger LOGGER = Logger.getLogger(XmlFile.class.getName()); private static final SAXParserFactory JAXP = SAXParserFactory.newInstance(); - private static final Xpp3Driver DEFAULT_DRIVER = new Xpp3Driver(); + private static final HierarchicalStreamDriver DEFAULT_DRIVER = XStream2.getDefaultDriver(); + + private static final XStream DEFAULT_XSTREAM = new XStream2(DEFAULT_DRIVER); static { JAXP.setNamespaceAware(true); diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java index b2fcc6c5d6..2e0a7ce166 100644 --- a/core/src/main/java/hudson/model/Fingerprint.java +++ b/core/src/main/java/hudson/model/Fingerprint.java @@ -1256,7 +1256,7 @@ public class Fingerprint implements ModelObject, Saveable { AtomicFileWriter afw = new AtomicFileWriter(file); try { PrintWriter w = new PrintWriter(afw); - w.println(""); + w.println(""); w.println(""); w.print(" "); w.print(DATE_CONVERTER.toString(timestamp)); diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 83a3682503..721c87476a 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -26,7 +26,6 @@ package hudson.model; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.io.StreamException; -import com.thoughtworks.xstream.io.xml.Xpp3Driver; import hudson.DescriptorExtensionList; import hudson.Extension; import hudson.ExtensionPoint; @@ -1214,7 +1213,7 @@ public abstract class View extends AbstractModelObject implements AccessControll // view in same ViewGroup and might not satisfy Jenkins.checkGoodName. String oldname = name; ViewGroup oldOwner = owner; // oddly, this field is not transient - Object o = Jenkins.XSTREAM2.unmarshal(new Xpp3Driver().createReader(in), this, null, true); + Object o = Jenkins.XSTREAM2.unmarshal(XStream2.getDefaultDriver().createReader(in), this, null, true); if (!o.getClass().equals(getClass())) { // ensure that we've got the same view type. extending this code to support updating // to different view type requires destroying & creating a new view type diff --git a/core/src/main/java/hudson/util/XStream2.java b/core/src/main/java/hudson/util/XStream2.java index 41b462df29..4f90342ea9 100644 --- a/core/src/main/java/hudson/util/XStream2.java +++ b/core/src/main/java/hudson/util/XStream2.java @@ -26,6 +26,7 @@ package hudson.util; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.KXml2Driver; import com.thoughtworks.xstream.mapper.AnnotationMapper; import com.thoughtworks.xstream.mapper.Mapper; import com.thoughtworks.xstream.mapper.MapperWrapper; @@ -97,7 +98,18 @@ public class XStream2 extends XStream { */ private MapperInjectionPoint mapperInjectionPoint; + /** + * Convenience method so we only have to change the driver in one place + * if we switch to something new in the future + * + * @return a new instance of the HierarchicalStreamDriver we want to use + */ + public static HierarchicalStreamDriver getDefaultDriver() { + return new KXml2Driver(); + } + public XStream2() { + super(getDefaultDriver()); init(); classOwnership = null; } @@ -109,6 +121,7 @@ public class XStream2 extends XStream { } XStream2(ClassOwnership classOwnership) { + super(getDefaultDriver()); init(); this.classOwnership = classOwnership; } @@ -293,7 +306,7 @@ public class XStream2 extends XStream { */ public void toXMLUTF8(Object obj, OutputStream out) throws IOException { Writer w = new OutputStreamWriter(out, Charset.forName("UTF-8")); - w.write("\n"); + w.write("\n"); toXML(obj, w); } diff --git a/core/src/main/java/jenkins/util/xstream/XStreamDOM.java b/core/src/main/java/jenkins/util/xstream/XStreamDOM.java index d98f346540..2b2ed1621b 100644 --- a/core/src/main/java/jenkins/util/xstream/XStreamDOM.java +++ b/core/src/main/java/jenkins/util/xstream/XStreamDOM.java @@ -35,10 +35,10 @@ import com.thoughtworks.xstream.io.xml.AbstractXmlReader; import com.thoughtworks.xstream.io.xml.AbstractXmlWriter; import com.thoughtworks.xstream.io.xml.DocumentReader; import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer; -import com.thoughtworks.xstream.io.xml.Xpp3Driver; import hudson.Util; import hudson.util.VariableResolver; +import hudson.util.XStream2; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; @@ -241,11 +241,11 @@ public class XStreamDOM { * Writes this {@link XStreamDOM} into {@link OutputStream}. */ public void writeTo(OutputStream os) { - writeTo(new Xpp3Driver().createWriter(os)); + writeTo(XStream2.getDefaultDriver().createWriter(os)); } public void writeTo(Writer w) { - writeTo(new Xpp3Driver().createWriter(w)); + writeTo(XStream2.getDefaultDriver().createWriter(w)); } public void writeTo(HierarchicalStreamWriter w) { @@ -262,11 +262,11 @@ public class XStreamDOM { } public static XStreamDOM from(InputStream in) { - return from(new Xpp3Driver().createReader(in)); + return from(XStream2.getDefaultDriver().createReader(in)); } public static XStreamDOM from(Reader in) { - return from(new Xpp3Driver().createReader(in)); + return from(XStream2.getDefaultDriver().createReader(in)); } public static XStreamDOM from(HierarchicalStreamReader in) { diff --git a/core/src/test/java/hudson/XmlFileTest.java b/core/src/test/java/hudson/XmlFileTest.java new file mode 100644 index 0000000000..69c85a93f7 --- /dev/null +++ b/core/src/test/java/hudson/XmlFileTest.java @@ -0,0 +1,79 @@ +package hudson; + +import hudson.model.Node; +import hudson.util.XStream2; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +import jenkins.model.Jenkins; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.xml.sax.SAXParseException; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class XmlFileTest { + + @Test + public void canReadXml1_0Test() throws IOException { + URL configUrl = getClass().getResource("/hudson/config_1_0.xml"); + XStream2 xs = new XStream2(); + xs.alias("hudson", Jenkins.class); + + XmlFile xmlFile = new XmlFile(xs, new File(configUrl.getFile())); + if (xmlFile.exists()) { + Node n = (Node) xmlFile.read(); + assertThat(n.getNumExecutors(), is(2)); + assertThat(n.getMode().toString(), is("NORMAL")); + } + } + + // KXml2Driver is able to parse XML 1.0 even if it has control characters which + // should be illegal. Ignoring this test until we switch to a more compliant driver + @Ignore + @Test(expected = SAXParseException.class) + public void xml1_0_withSpecialCharsShouldFail() throws IOException { + URL configUrl = getClass().getResource("/hudson/config_1_0_with_special_chars.xml"); + XStream2 xs = new XStream2(); + xs.alias("hudson", Jenkins.class); + + XmlFile xmlFile = new XmlFile(xs, new File(configUrl.getFile())); + if (xmlFile.exists()) { + Node n = (Node) xmlFile.read(); + assertThat(n.getNumExecutors(), is(2)); + assertThat(n.getMode().toString(), is("NORMAL")); + } + } + + @Test + public void canReadXml1_1Test() throws IOException { + URL configUrl = getClass().getResource("/hudson/config_1_1.xml"); + XStream2 xs = new XStream2(); + xs.alias("hudson", Jenkins.class); + + XmlFile xmlFile = new XmlFile(xs, new File(configUrl.getFile())); + if (xmlFile.exists()) { + Node n = (Node) xmlFile.read(); + assertThat(n.getNumExecutors(), is(2)); + assertThat(n.getMode().toString(), is("NORMAL")); + } + } + + @Test + public void canReadXmlWithControlCharsTest() throws IOException { + URL configUrl = getClass().getResource("/hudson/config_1_1_with_special_chars.xml"); + XStream2 xs = new XStream2(); + xs.alias("hudson", Jenkins.class); + + XmlFile xmlFile = new XmlFile(xs, new File(configUrl.getFile())); + if (xmlFile.exists()) { + Node n = (Node) xmlFile.read(); + assertThat(n.getNumExecutors(), is(2)); + assertThat(n.getMode().toString(), is("NORMAL")); + assertThat(n.getLabelString(), is("LESS_TERMCAP_mb=\u001B[01;31m")); + } + } +} diff --git a/core/src/test/java/hudson/util/XStream2Test.java b/core/src/test/java/hudson/util/XStream2Test.java index f5a038ae79..59192f02a4 100644 --- a/core/src/test/java/hudson/util/XStream2Test.java +++ b/core/src/test/java/hudson/util/XStream2Test.java @@ -29,7 +29,7 @@ import static org.junit.Assert.*; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.thoughtworks.xstream.XStreamException; -import com.thoughtworks.xstream.io.xml.Xpp3Driver; +import com.thoughtworks.xstream.io.xml.KXml2Driver; import com.thoughtworks.xstream.security.ForbiddenClassException; import hudson.model.Result; import hudson.model.Run; @@ -426,7 +426,7 @@ public class XStream2Test { private Object fromXMLNullingOut(String xml, Object root) { // Currently not offering a public convenience API for this: - return Jenkins.XSTREAM2.unmarshal(new Xpp3Driver().createReader(new StringReader(xml)), root, null, true); + return Jenkins.XSTREAM2.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xml)), root, null, true); } public static class WithDefaults { diff --git a/core/src/test/resources/hudson/config_1_0.xml b/core/src/test/resources/hudson/config_1_0.xml new file mode 100644 index 0000000000..c3a76a6eef --- /dev/null +++ b/core/src/test/resources/hudson/config_1_0.xml @@ -0,0 +1,8 @@ + + + + 1.480.1 + 2 + NORMAL + + \ No newline at end of file diff --git a/core/src/test/resources/hudson/config_1_0_with_special_chars.xml b/core/src/test/resources/hudson/config_1_0_with_special_chars.xml new file mode 100644 index 0000000000..f26e502947 --- /dev/null +++ b/core/src/test/resources/hudson/config_1_0_with_special_chars.xml @@ -0,0 +1,9 @@ + + + + 1.480.1 + 2 + NORMAL + + + \ No newline at end of file diff --git a/core/src/test/resources/hudson/config_1_1.xml b/core/src/test/resources/hudson/config_1_1.xml new file mode 100644 index 0000000000..c3fd8c0603 --- /dev/null +++ b/core/src/test/resources/hudson/config_1_1.xml @@ -0,0 +1,8 @@ + + + + 1.480.1 + 2 + NORMAL + + \ No newline at end of file diff --git a/core/src/test/resources/hudson/config_1_1_with_special_chars.xml b/core/src/test/resources/hudson/config_1_1_with_special_chars.xml new file mode 100644 index 0000000000..cb072b5ebb --- /dev/null +++ b/core/src/test/resources/hudson/config_1_1_with_special_chars.xml @@ -0,0 +1,9 @@ + + + + 1.480.1 + 2 + NORMAL + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index b49d34a3e9..7b0f061f03 100644 --- a/pom.xml +++ b/pom.xml @@ -133,7 +133,7 @@ THE SOFTWARE. org.apache.ant ant - 1.8.4 + 1.9.2 diff --git a/test/src/test/java/hudson/XMLFileTest.java b/test/src/test/java/hudson/XMLFileTest.java new file mode 100644 index 0000000000..1b3a9ff3ea --- /dev/null +++ b/test/src/test/java/hudson/XMLFileTest.java @@ -0,0 +1,47 @@ +package hudson; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class XMLFileTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + @LocalData + public void canStartWithXml_1_1_ConfigsTest() { + + assertThat(j.jenkins.getLabelString(),is("LESS_TERMCAP_mb=\u001B[01;31m")); + + } + + /** + * + * This test validates that xml v1.0 configs silently get migrated to xml v1.1 when they are persisted + * + */ + @Test + @LocalData + public void silentlyMigrateConfigsTest() throws Exception { + j.jenkins.save(); + // verify that we did indeed load our test config.xml + assertThat(j.jenkins.getLabelString(), is("I am a label")); + //verify that the persisted top level config.xml is v1.1 + File configFile = new File(j.jenkins.getRootDir(), "config.xml"); + assertThat(configFile.exists(), is(true)); + + try (BufferedReader config = new BufferedReader(new FileReader(configFile))) { + assertThat(config.readLine(), is("")); + config.close(); + } + } +} diff --git a/test/src/test/java/hudson/cli/GetNodeCommandTest.java b/test/src/test/java/hudson/cli/GetNodeCommandTest.java index b2792924aa..71cc405915 100644 --- a/test/src/test/java/hudson/cli/GetNodeCommandTest.java +++ b/test/src/test/java/hudson/cli/GetNodeCommandTest.java @@ -74,7 +74,7 @@ public class GetNodeCommandTest { .invokeWithArgs("MySlave") ; - assertThat(result.stdout(), startsWith("")); + assertThat(result.stdout(), startsWith("")); assertThat(result.stdout(), containsString("MySlave")); assertThat(result, hasNoErrorOutput()); assertThat(result, succeeded()); diff --git a/test/src/test/java/hudson/cli/GetViewCommandTest.java b/test/src/test/java/hudson/cli/GetViewCommandTest.java index d3a6fd9ef7..c08af750d2 100644 --- a/test/src/test/java/hudson/cli/GetViewCommandTest.java +++ b/test/src/test/java/hudson/cli/GetViewCommandTest.java @@ -79,7 +79,7 @@ public class GetViewCommandTest { assertThat(result, succeeded()); assertThat(result, hasNoErrorOutput()); - assertThat(result.stdout(), startsWith("")); + assertThat(result.stdout(), startsWith("")); assertThat(result.stdout(), containsString("aView")); } diff --git a/test/src/test/java/hudson/model/ComputerConfigDotXmlTest.java b/test/src/test/java/hudson/model/ComputerConfigDotXmlTest.java index 737e7b3228..6661d55780 100644 --- a/test/src/test/java/hudson/model/ComputerConfigDotXmlTest.java +++ b/test/src/test/java/hudson/model/ComputerConfigDotXmlTest.java @@ -121,7 +121,7 @@ public class ComputerConfigDotXmlTest { computer.doConfigDotXml(req, rsp); final String out = outputStream.toString(); - assertThat(out, startsWith("")); + assertThat(out, startsWith("")); assertThat(out, containsString("slave0")); assertThat(out, containsString("dummy")); } diff --git a/test/src/test/java/hudson/util/RobustReflectionConverterTest.java b/test/src/test/java/hudson/util/RobustReflectionConverterTest.java index c57acd55cd..d728e053f1 100644 --- a/test/src/test/java/hudson/util/RobustReflectionConverterTest.java +++ b/test/src/test/java/hudson/util/RobustReflectionConverterTest.java @@ -170,7 +170,7 @@ public class RobustReflectionConverterTest { } private static final String CONFIGURATION_TEMPLATE = - "" + "" + "" + "" + "" diff --git a/test/src/test/resources/hudson/XMLFileTest/canStartWithXml_1_1_ConfigsTest/config.xml b/test/src/test/resources/hudson/XMLFileTest/canStartWithXml_1_1_ConfigsTest/config.xml new file mode 100644 index 0000000000..cb072b5ebb --- /dev/null +++ b/test/src/test/resources/hudson/XMLFileTest/canStartWithXml_1_1_ConfigsTest/config.xml @@ -0,0 +1,9 @@ + + + + 1.480.1 + 2 + NORMAL + + + \ No newline at end of file diff --git a/test/src/test/resources/hudson/XMLFileTest/silentlyMigrateConfigsTest/config.xml b/test/src/test/resources/hudson/XMLFileTest/silentlyMigrateConfigsTest/config.xml new file mode 100644 index 0000000000..1cc5aa6799 --- /dev/null +++ b/test/src/test/resources/hudson/XMLFileTest/silentlyMigrateConfigsTest/config.xml @@ -0,0 +1,9 @@ + + + + 1.480.1 + 2 + NORMAL + + + \ No newline at end of file