-
-
Notifications
You must be signed in to change notification settings - Fork 8.9k
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
Changes from 29 commits
d27a501
f5edc8f
0e5aeec
e7609db
69eaa76
4af6099
08771f7
232a012
861ca26
b89f6d1
1cec1e1
0ba7df4
cdc1414
80ab2fe
b45ffbc
ef89494
82dd8f8
2046d36
bfe4d2a
09d7dac
82037c3
5b2b5f0
5f7c40f
53f9978
97146a7
f1fc2f6
7b02701
e2a10f1
af44d41
91e0fa6
7e748d9
44a9b59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,7 +133,7 @@ public void setJobFilters(List<ViewJobFilter> jobFilters) throws IOException { | |
this.jobFilters.replaceBy(jobFilters); | ||
} | ||
|
||
private Object readResolve() { | ||
protected Object readResolve() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this imply that all plugins must make the same change? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I attempted to search for extant plugins using an incorrect |
||
if(includeRegex!=null) { | ||
try { | ||
includePattern = Pattern.compile(includeRegex); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a problem. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I am not sure why There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
||
/** | ||
|
@@ -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; | ||
} | ||
|
||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 + "]"; | ||
} | ||
|
@@ -244,5 +274,4 @@ public static DescriptorExtensionList<ToolInstallation,ToolDescriptor<?>> all() | |
return Jenkins.get().getDescriptorList(ToolInstallation.class); | ||
} | ||
|
||
private static final long serialVersionUID = 1L; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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. | ||
|
@@ -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; | ||
|
||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If merged, we should also archive both https://github.com/jenkinsci/xstream and https://github.com/jenkinsci/xstream-fork.