Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JEP-228] Unforking XStream #4944

Merged
merged 32 commits into from
Nov 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d27a501
Unforking XStream
jglick Sep 21, 2020
f5edc8f
Some overrides missing from MapperDelegate
jglick Sep 21, 2020
0e5aeec
[XSTR-762] writeReplace & readResolve must be accessible to be used f…
jglick Sep 23, 2020
e7609db
Merge branch 'master' of github.com:jenkinsci/jenkins into xstream
jglick Sep 23, 2020
69eaa76
DomElement.click overloads changed incompatibly
jglick Sep 23, 2020
4af6099
Giving up on ToolInstallation being Serializable.
jglick Sep 23, 2020
08771f7
Relaxing message assertion in RobustReflectionConverter.randomExcepti…
jglick Sep 24, 2020
232a012
Picking up https://github.com/jenkinsci/matrix-auth-plugin/pull/89 ca…
jglick Sep 24, 2020
861ca26
https://github.com/jenkinsci/jenkins-test-harness/pull/243 released
jglick Sep 24, 2020
b89f6d1
Another case forgotten in 0e5aeec4ccc67207c0912ffe471fe5cf2815cf04
jglick Sep 24, 2020
1cec1e1
XStream has SecurityMapper, but we have our own system already, so su…
jglick Sep 25, 2020
0ba7df4
GetMavenVersion was also serializing its outer class
jglick Sep 25, 2020
cdc1414
Trying to make ToolInstallation serializable compatible
jglick Sep 25, 2020
80ab2fe
Merge branch 'master' of github.com:jenkinsci/jenkins into xstream
jglick Sep 28, 2020
b45ffbc
Avoid throwing IOException from PersistedList.inModified for common c…
jglick Sep 28, 2020
ef89494
Found a way to avoid looking up XStream.converterRegistry by reflection
jglick Sep 28, 2020
82dd8f8
XStream2Test.crashXstream now throwing ConversionException, not Forbi…
jglick Sep 28, 2020
2046d36
Noting which version of the parent POM has https://github.com/jenkins…
jglick Sep 29, 2020
bfe4d2a
Merge branch 'master' of github.com:jenkinsci/jenkins into xstream
jglick Sep 29, 2020
09d7dac
Refreshed Javadoc for XStream2
jglick Sep 29, 2020
82037c3
The simplest solution to JENKINS-19561 seems to be to stop using auto…
jglick Sep 29, 2020
5b2b5f0
Fixing some deprecations in Robust*Converter
jglick Sep 29, 2020
5f7c40f
Display OldDataMonitor errors from RobustReflectionConverter during f…
jglick Sep 30, 2020
53f9978
Merge branch 'master' of github.com:jenkinsci/jenkins into xstream
jglick Oct 1, 2020
97146a7
Merge branch 'master' of github.com:jenkinsci/jenkins into xstream
jglick Oct 8, 2020
f1fc2f6
Stray digit in test name
jglick Oct 8, 2020
7b02701
Added XStream2AnnotationTest to clearly document incompatibility
jglick Oct 8, 2020
e2a10f1
Merge branch 'master' of https://github.com/jenkinsci/jenkins into xs…
jglick Oct 21, 2020
af44d41
Merge branch 'master' of https://github.com/jenkinsci/jenkins into xs…
jglick Oct 26, 2020
91e0fa6
Picking up release of https://github.com/jenkinsci/matrix-auth-plugin…
jglick Nov 3, 2020
7e748d9
Merge branch 'master' of https://github.com/jenkinsci/jenkins into xs…
jglick Nov 3, 2020
44a9b59
Merge branch 'master' of https://github.com/jenkinsci/jenkins into xs…
jglick Nov 6, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,9 @@ THE SOFTWARE.

<!--XStream-->
<dependency>
<groupId>org.jvnet.hudson</groupId>
<groupId>com.thoughtworks.xstream</groupId>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<artifactId>xstream</artifactId>
<version>1.4.7-jenkins-1</version>
<version>1.4.13</version>
</dependency>
<dependency>
<groupId>net.sf.kxml</groupId>
Expand Down
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ THE SOFTWARE.
<artifactId>antlr</artifactId>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<exclusions>
<exclusion>
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/hudson/diagnosis/OldDataMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.Main;
import hudson.XmlFile;
import hudson.model.AdministrativeMonitor;
import hudson.model.Item;
Expand Down Expand Up @@ -207,6 +208,9 @@ public static void report(Saveable obj, Collection<Throwable> errors) {
if (e instanceof ReportException) {
report(obj, ((ReportException)e).version);
} else {
if (Main.isUnitTest) {
LOGGER.log(Level.INFO, "Trouble loading " + obj, e);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very handy for diagnosing test failures related to XStream!

}
if (++i > 1) buf.append(", ");
buf.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage());
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/model/AbstractItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ public final XmlFile getConfigFile() {
return Items.getConfigFile(this);
}

private Object writeReplace() {
protected Object writeReplace() {
return XmlFile.replaceIfNotAtTopLevel(this, () -> new Replacer(this));
}
private static class Replacer {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/model/ListView.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public void setJobFilters(List<ViewJobFilter> jobFilters) throws IOException {
this.jobFilters.replaceBy(jobFilters);
}

private Object readResolve() {
protected Object readResolve() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this imply that all plugins must make the same change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the class is extended and the parent read resolve needs to be called

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted to search for extant plugins using an incorrect private override, but I might have missed some of course.

if(includeRegex!=null) {
try {
includePattern = Pattern.compile(includeRegex);
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/model/Queue.java
Original file line number Diff line number Diff line change
Expand Up @@ -2383,7 +2383,7 @@ public Api getApi() throws AccessDeniedException {
}
}

private Object readResolve() {
protected Object readResolve() {
this.future = new FutureImpl(task);
return this;
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/model/Run.java
Original file line number Diff line number Diff line change
Expand Up @@ -2082,7 +2082,7 @@ public synchronized void save() throws IOException {
return new XmlFile(XSTREAM,new File(getRootDir(),"build.xml"));
}

private Object writeReplace() {
protected Object writeReplace() {
return XmlFile.replaceIfNotAtTopLevel(this, () -> new Replacer(this));
}
private static class Replacer {
Expand Down
46 changes: 27 additions & 19 deletions core/src/main/java/hudson/tasks/Maven.java
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ public void buildEnvVars(EnvVars env) {
public boolean meetsMavenReqVersion(Launcher launcher, int mavenReqVersion) throws IOException, InterruptedException {
// FIXME using similar stuff as in the maven plugin could be better
// olamy : but will add a dependency on maven in core -> so not so good
String mavenVersion = launcher.getChannel().call(new GetMavenVersion());
String mavenVersion = launcher.getChannel().call(new GetMavenVersion(getHome()));

if (!mavenVersion.equals("")) {
if (mavenReqVersion == MAVEN_20) {
Expand All @@ -572,11 +572,14 @@ else if (mavenReqVersion == MAVEN_30) {
return false;

}
private class GetMavenVersion extends MasterToSlaveCallable<String, IOException> {
private static final long serialVersionUID = -4143159957567745621L;
private static class GetMavenVersion extends MasterToSlaveCallable<String, IOException> {
private final String home;
GetMavenVersion(String home) {
this.home = home;
}
@Override
public String call() throws IOException {
File[] jars = new File(getHomeDir(), "lib").listFiles();
File[] jars = new File(home, "lib").listFiles();
if (jars != null) { // be defensive
for (File jar : jars) {
if (jar.getName().startsWith("maven-")) {
Expand Down Expand Up @@ -608,24 +611,29 @@ public boolean isMaven2_1(Launcher launcher) throws IOException, InterruptedExce
* Gets the executable path of this maven on the given target system.
*/
public String getExecutable(Launcher launcher) throws IOException, InterruptedException {
return launcher.getChannel().call(new GetExecutable());
}
private class GetExecutable extends MasterToSlaveCallable<String, IOException> {
private static final long serialVersionUID = 2373163112639943768L;
@Override
public String call() throws IOException {
File exe = getExeFile("mvn");
if(exe.exists())
return exe.getPath();
exe = getExeFile("maven");
if(exe.exists())
return exe.getPath();
return null;
return launcher.getChannel().call(new GetExecutable(getHome()));
}
private static class GetExecutable extends MasterToSlaveCallable<String, IOException> {
private final String rawHome;
GetExecutable(String rawHome) {
this.rawHome = rawHome;
}
@Override
public String call() throws IOException {
File exe = getExeFile("mvn", rawHome);
if (exe.exists()) {
return exe.getPath();
}
exe = getExeFile("maven", rawHome);
if (exe.exists()) {
return exe.getPath();
}
return null;
}
}

private File getExeFile(String execName) {
String m2Home = Util.replaceMacro(getHome(),EnvVars.masterEnvVars);
private static File getExeFile(String execName, String home) {
String m2Home = Util.replaceMacro(home, EnvVars.masterEnvVars);

if(Functions.isWindows()) {
File exeFile = new File(m2Home, "bin/" + execName + ".bat");
Expand Down
49 changes: 39 additions & 10 deletions core/src/main/java/hudson/tools/ToolInstallation.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,26 @@
import hudson.ExtensionPoint;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.*;
import hudson.remoting.Channel;
import hudson.slaves.NodeSpecific;
import hudson.util.DescribableList;
import hudson.util.XStream2;

import java.io.Serializable;
import java.io.StringReader;
import java.io.IOException;
import java.util.List;

import com.thoughtworks.xstream.annotations.XStreamSerializable;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import jenkins.util.Timer;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
* Formalization of a tool installed in nodes used for builds.
Expand Down Expand Up @@ -76,14 +83,16 @@
* @since 1.286
*/
public abstract class ToolInstallation extends AbstractDescribableImpl<ToolInstallation> implements Serializable, ExtensionPoint {

private static final Logger LOGGER = Logger.getLogger(ToolInstallation.class.getName());

private final String name;
private /*almost final*/ String home;

/**
* {@link ToolProperty}s that are associated with this tool.
*/
@XStreamSerializable
private transient /*almost final*/ DescribableList<ToolProperty<?>,ToolPropertyDescriptor> properties
private /*almost final*/ DescribableList<ToolProperty<?>,ToolPropertyDescriptor> properties
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a problem. DescribableList is not Serializable so removing transient will prevent this class from being serialized over Remoting. Yet keeping transient will prevent tool installations from saving their properties in XML. I have not found any way to get the effect of XStreamSerializable without patching PureJavaReflectionProvider.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I am not sure why ToolInstallation was ever made Serializable to begin with. The normal usage is to have it define some PATH entry on the controller side, which does not require the ToolInstallation itself to ever be sent to the agent.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the current annotation was explicitly rejected upstream: x-stream/xstream#70 (comment)

= new DescribableList<>(Saveable.NOOP);

/**
Expand Down Expand Up @@ -145,8 +154,10 @@ public String getName() {
public void buildEnvVars(EnvVars env) {
}

public DescribableList<ToolProperty<?>,ToolPropertyDescriptor> getProperties() {
assert properties!=null;
public synchronized DescribableList<ToolProperty<?>,ToolPropertyDescriptor> getProperties() {
if (properties == null) {
properties = new DescribableList<>(Saveable.NOOP);
}
return properties;
}

Expand Down Expand Up @@ -210,13 +221,32 @@ protected String translateFor(Node node, TaskListener log) throws IOException, I
* Invoked by XStream when this object is read into memory.
*/
protected Object readResolve() {
if(properties==null)
properties = new DescribableList<>(Saveable.NOOP);
for (ToolProperty<?> p : properties)
_setTool(p, this);
if (properties != null) {
for (ToolProperty<?> p : properties) {
_setTool(p, this);
}
}
return this;
}

protected Object writeReplace() throws Exception {
if (Channel.current() == null) { // XStream
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, not nice, but seems to work well enough. I am trying to fix popular plugins to not need this hack.

return this;
} else { // Remoting
LOGGER.log(Level.WARNING, "Serialization of " + getClass().getSimpleName() + " extends ToolInstallation over Remoting is deprecated", new Throwable());
// Hack: properties is not serializable, so try to serialize as XML (in another thread); delete <properties/>; deserialize; then load a clone
String xml1 = Timer.get().submit(() -> Jenkins.XSTREAM2.toXML(this)).get();
Document dom = new SAXReader().read(new StringReader(xml1));
Element properties = dom.getRootElement().element("properties");
if (properties != null) {
dom.getRootElement().remove(properties);
}
String xml2 = dom.asXML();
ToolInstallation clone = (ToolInstallation) Timer.get().submit(() -> Jenkins.XSTREAM2.fromXML(xml2)).get();
return clone;
}
}

@Override public String toString() {
return getClass().getSimpleName() + "[" + name + "]";
}
Expand Down Expand Up @@ -244,5 +274,4 @@ public static DescriptorExtensionList<ToolInstallation,ToolDescriptor<?>> all()
return Jenkins.get().getDescriptorList(ToolInstallation.class);
}

private static final long serialVersionUID = 1L;
}
33 changes: 32 additions & 1 deletion core/src/main/java/hudson/util/PersistedList.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package hudson.util;

import com.google.common.collect.ImmutableSet;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
Expand All @@ -40,6 +41,10 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Collection whose change is notified to the parent object for persistence.
Expand All @@ -48,6 +53,9 @@
* @since 1.333
*/
public class PersistedList<T> extends AbstractList<T> {

private static final Logger LOGGER = Logger.getLogger(PersistedList.class.getName());

protected final CopyOnWriteList<T> data = new CopyOnWriteList<>();
protected Saveable owner = Saveable.NOOP;

Expand Down Expand Up @@ -170,7 +178,30 @@ public Iterator<T> iterator() {
* Called when a list is mutated.
*/
protected void onModified() throws IOException {
owner.save();
try {
owner.save();
} catch (IOException x) {
Optional<T> ignored = stream().filter(PersistedList::ignoreSerializationErrors).findAny();
if (ignored.isPresent()) {
LOGGER.log(Level.WARNING, "Ignoring serialization errors in " + ignored.get() + "; update your parent POM to 4.8 or newer", x);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful for PCT, until we can get most plugins moved onto new POMs.

} else {
throw x;
}
}
}

// TODO until https://github.com/jenkinsci/jenkins-test-harness/pull/243 is widely adopted:
private static final Set<String> IGNORED_CLASSES = ImmutableSet.of("org.jvnet.hudson.test.TestBuilder", "org.jvnet.hudson.test.TestNotifier");
// (SingleFileSCM & ExtractResourceWithChangesSCM would also be nice to suppress, but they are not kept in a PersistedList.)
private static boolean ignoreSerializationErrors(Object o) {
if (o != null) {
for (Class<?> c = o.getClass(); c != Object.class; c = c.getSuperclass()) {
if (IGNORED_CLASSES.contains(c.getName())) {
return true;
}
}
}
return false;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions core/src/main/java/hudson/util/RobustCollectionConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;
import com.thoughtworks.xstream.core.ClassLoaderReference;

import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
Expand All @@ -48,6 +49,7 @@
*
* @author Kohsuke Kawaguchi
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class RobustCollectionConverter extends CollectionConverter {
private final SerializableConverter sc;

Expand All @@ -57,7 +59,7 @@ public RobustCollectionConverter(XStream xs) {

public RobustCollectionConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
super(mapper);
sc = new SerializableConverter(mapper,reflectionProvider);
sc = new SerializableConverter(mapper, reflectionProvider, new ClassLoaderReference(null));
}

@Override
Expand All @@ -82,7 +84,7 @@ protected void populateCollection(HierarchicalStreamReader reader, Unmarshalling
while (reader.hasMoreChildren()) {
reader.moveDown();
try {
Object item = readItem(reader, context, collection);
Object item = readBareItem(reader, context, collection);
collection.add(item);
} catch (CriticalXStreamException e) {
throw e;
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/util/RobustMapConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ final class RobustMapConverter extends MapConverter {
private Object read(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
reader.moveDown();
try {
return readItem(reader, context, map);
return readBareItem(reader, context, map);
} catch (CriticalXStreamException x) {
throw x;
} catch (XStreamException | LinkageError x) {
Expand Down
Loading