diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java new file mode 100644 index 0000000000000000000000000000000000000000..f2fc48e0d505910ebb5f72230d70be6fbceee2f7 --- /dev/null +++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java @@ -0,0 +1,274 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Alan Harder + * + * 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.diagnosis; + +import hudson.XmlFile; +import hudson.model.AdministrativeMonitor; +import hudson.model.Hudson; +import hudson.Extension; +import hudson.model.Item; +import hudson.model.Job; +import hudson.model.Run; +import hudson.model.Saveable; +import hudson.model.listeners.ItemListener; +import hudson.model.listeners.RunListener; +import hudson.model.listeners.SaveableListener; +import hudson.util.RobustReflectionConverter; +import hudson.util.VersionNumber; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import com.thoughtworks.xstream.converters.UnmarshallingContext; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.kohsuke.stapler.HttpRedirect; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.HttpResponses; + +/** + * Tracks whether any data structure changes were corrected when loading XML, + * that could be resaved to migrate that data to the new format. + * + * @author Alan.Harder@Sun.Com + */ +@Extension +public class OldDataMonitor extends AdministrativeMonitor { + private static Logger LOGGER = Logger.getLogger(OldDataMonitor.class.getName()); + + private HashMap data = new HashMap(); + + public OldDataMonitor() { + super("OldData"); + } + + @Override + public String getDisplayName() { + return Messages.OldDataMonitor_DisplayName(); + } + + public boolean isActivated() { + return !data.isEmpty(); + } + + public synchronized Map getData() { + return Collections.unmodifiableMap(data); + } + + private synchronized void remove(Saveable obj) { + data.remove(obj); + if (obj instanceof Job) + for (Run r : ((Job)obj).getBuilds()) + data.remove(r); + } + + // Listeners to remove data here if resaved or deleted in regular Hudson usage + + @Extension + public static final SaveableListener changeListener = new SaveableListener() { + @Override + public void onChange(Saveable obj, XmlFile file) { + ((OldDataMonitor)Hudson.getInstance().getAdministrativeMonitor("OldData")).remove(obj); + } + }; + + @Extension + public static final ItemListener itemDeleteListener = new ItemListener() { + @Override + public void onDeleted(Item item) { + ((OldDataMonitor)Hudson.getInstance().getAdministrativeMonitor("OldData")).remove(item); + } + }; + + @Extension + public static final RunListener runDeleteListener = new RunListener(Run.class) { + @Override + public void onDeleted(Run run) { + ((OldDataMonitor)Hudson.getInstance().getAdministrativeMonitor("OldData")).remove(run); + } + }; + + /** + * Inform monitor that some data in a deprecated format has been loaded, + * and converted in-memory to a new structure. + * @param obj Saveable object; calling save() on this object will persist + * the data in its new format to disk. + * @param version Hudson release when the data structure changed. + */ + public static void report(Saveable obj, String version) { + OldDataMonitor odm = (OldDataMonitor)Hudson.getInstance().getAdministrativeMonitor("OldData"); + synchronized (odm) { + try { + VersionRange vr = odm.data.get(obj); + if (vr != null) vr.add(version); + else odm.data.put(obj, new VersionRange(version, null)); + } catch (IllegalArgumentException ex) { + LOGGER.log(Level.WARNING, "Bad parameter given to OldDataMonitor", ex); + } + } + } + + /** + * Inform monitor that some data in a deprecated format has been loaded, during + * XStream unmarshalling when the Saveable containing this object is not available. + * @param context XStream unmarshalling context + * @param version Hudson release when the data structure changed. + */ + public static void report(UnmarshallingContext context, String version) { + RobustReflectionConverter.addErrorInContext(context, new ReportException(version)); + } + + private static class ReportException extends Exception { + private String version; + private ReportException(String version) { + this.version = version; + } + } + + /** + * Inform monitor that some unreadable data was found while loading. + * @param obj Saveable object; calling save() on this object will discard the unreadable data. + * @param errors Exception(s) thrown while loading, regarding the unreadable classes/fields. + */ + public static void report(Saveable obj, Collection errors) { + StringBuilder buf = new StringBuilder(); + int i = 0; + for (Throwable e : errors) { + if (e instanceof ReportException) { + report(obj, ((ReportException)e).version); + } else { + if (++i > 1) buf.append(", "); + buf.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage()); + } + } + if (buf.length() == 0) return; + OldDataMonitor odm = (OldDataMonitor)Hudson.getInstance().getAdministrativeMonitor("OldData"); + synchronized (odm) { + VersionRange vr = odm.data.get(obj); + if (vr != null) vr.extra = buf.toString(); + else odm.data.put(obj, new VersionRange(null, buf.toString())); + } + } + + public static class VersionRange { + private static VersionNumber currentVersion = new VersionNumber(Hudson.VERSION); + + VersionNumber min, max; + boolean single = true; + public String extra; + + public VersionRange(String version, String extra) { + min = max = version != null ? new VersionNumber(version) : null; + this.extra = extra; + } + + public void add(String version) { + VersionNumber ver = new VersionNumber(version); + if (min==null) { min = max = ver; } + else { + if (ver.isOlderThan(min)) { min = ver; single = false; } + if (ver.isNewerThan(max)) { max = ver; single = false; } + } + } + + @Override + public String toString() { + return min==null ? "" : min.toString() + (single ? "" : " - " + max.toString()); + } + + /** + * Does this version range contain a version more than the given number of releases ago? + * @param threshold Number of releases + * @return True if the major version# differs or the minor# differs by >= threshold + */ + public boolean isOld(int threshold) { + if (min!=null && (currentVersion.digit(0) > min.digit(0) + || (currentVersion.digit(0) == min.digit(0) + && currentVersion.digit(1) - min.digit(1) >= threshold))) + return true; + return false; + } + } + + /** + * Sorted list of unique max-versions in the data set. For select list in jelly. + */ + public synchronized Iterator getVersionList() { + TreeSet set = new TreeSet(); + for (VersionRange vr : data.values()) + if (vr.max!=null) set.add(vr.max); + return set.iterator(); + } + + /** + * Depending on whether the user said "yes" or "no", send him to the right place. + */ + public HttpResponse doAct(StaplerRequest req, StaplerResponse rsp) throws IOException { + if(req.hasParameter("no")) { + disable(true); + return HttpResponses.redirectViaContextPath("/manage"); + } else { + return new HttpRedirect("manage"); + } + } + + /** + * Save all or some of the files to persist data in the new forms. + * Remove those items from the data map. + */ + public synchronized HttpResponse doUpgrade(StaplerRequest req, StaplerResponse rsp) throws IOException { + String thruVerParam = req.getParameter("thruVer"); + VersionNumber thruVer = thruVerParam.equals("all") ? null : new VersionNumber(thruVerParam); + for (Iterator> it = data.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + VersionNumber version = entry.getValue().max; + if (version != null && (thruVer == null || !version.isNewerThan(thruVer))) { + entry.getKey().save(); + it.remove(); + } + } + return HttpResponses.forwardToPreviousPage(); + } + + /** + * Save all files containing only unreadable data (no data upgrades), which discards this data. + * Remove those items from the data map. + */ + public synchronized HttpResponse doDiscard(StaplerRequest req, StaplerResponse rsp) throws IOException { + for (Iterator> it = data.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + if (entry.getValue().max == null) { + entry.getKey().save(); + it.remove(); + } + } + return HttpResponses.forwardToPreviousPage(); + } +} diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 1bebfb569c3e3aa5901ffda86b4aca5f67f70a1e..9ca46c1ab7efeb08c58d3abed7cfd4c662a2dfb2 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Brian Westrich, Erik Ramfelt, Ertan Deniz, Jean-Baptiste Quenot, Luca Domenico Milanesio, R. Tyler Ballance, Stephen Connolly, Tom Huybrechts, id:cactusman + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Brian Westrich, Erik Ramfelt, Ertan Deniz, Jean-Baptiste Quenot, Luca Domenico Milanesio, R. Tyler Ballance, Stephen Connolly, Tom Huybrechts, id:cactusman * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,7 @@ import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.cli.declarative.CLIMethod; +import hudson.diagnosis.OldDataMonitor; import hudson.slaves.WorkspaceList; import hudson.model.Cause.LegacyCodeCause; import hudson.model.Cause.UserCause; @@ -219,9 +220,13 @@ public abstract class AbstractProject

,R extends A } }); - if(triggers==null) + // boolean! Can't tell if xml file contained false.. + if (enableRemoteTrigger) OldDataMonitor.report(this, "1.77"); + if(triggers==null) { // it didn't exist in < 1.28 triggers = new Vector>(); + OldDataMonitor.report(this, "1.28"); + } for (Trigger t : triggers) t.start(this,false); if(scm==null) diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java index 53fcfeaa44024376732acc7f2b44bbd026a01226..3a62ef413a30e0aec51fe269578a6e8b100a8ec5 100644 --- a/core/src/main/java/hudson/model/Cause.java +++ b/core/src/main/java/hudson/model/Cause.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Michael B. Donohue, Seiji Sogabe + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Michael B. Donohue, Seiji Sogabe * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,9 +27,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Queue.*; +import hudson.util.XStream2; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; +import com.thoughtworks.xstream.converters.UnmarshallingContext; /** * Cause object base class. This class hierarchy is used to keep track of why @@ -122,14 +125,17 @@ public abstract class Cause { public String getShortDescription() { return Messages.Cause_UpstreamCause_ShortDescription(upstreamProject, Integer.toString(upstreamBuild)); } - - private Object readResolve() { - if(upstreamCause != null) { - if(upstreamCauses == null) upstreamCauses = new ArrayList(); - upstreamCauses.add(upstreamCause); - upstreamCause=null; + + public static class ConverterImpl extends XStream2.PassthruConverter { + public ConverterImpl(XStream2 xstream) { super(xstream); } + @Override protected void callback(UpstreamCause uc, UnmarshallingContext context) { + if (uc.upstreamCause != null) { + if (uc.upstreamCauses == null) uc.upstreamCauses = new ArrayList(); + uc.upstreamCauses.add(uc.upstreamCause); + uc.upstreamCause = null; + OldDataMonitor.report(context, "1.288"); + } } - return this; } } diff --git a/core/src/main/java/hudson/model/CauseAction.java b/core/src/main/java/hudson/model/CauseAction.java index e3b306e7eb090d832bf06af31f100694ac82b464..0baed2c71c2844588a80cf1056051602f13675d9 100644 --- a/core/src/main/java/hudson/model/CauseAction.java +++ b/core/src/main/java/hudson/model/CauseAction.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Michael B. Donohue + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Michael B. Donohue * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,10 +23,13 @@ */ package hudson.model; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Queue.Task; import hudson.model.queue.FoldableAction; +import hudson.util.XStream2; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; +import com.thoughtworks.xstream.converters.UnmarshallingContext; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -42,7 +45,7 @@ public class CauseAction implements FoldableAction { // there can be multiple causes, so this is deprecated private transient Cause cause; - private List causes = new ArrayList(); + private List causes = new ArrayList(); @Exported(visibility=2) public List getCauses() { @@ -101,13 +104,16 @@ public class CauseAction implements FoldableAction { // no CauseAction found, so add a copy of this one item.getActions().add(new CauseAction(this)); } - - private Object readResolve() { - // if we are being read in from an older version - if(cause != null) { - if(causes == null) causes=new ArrayList(); - causes.add(cause); - } - return this; + + public static class ConverterImpl extends XStream2.PassthruConverter { + public ConverterImpl(XStream2 xstream) { super(xstream); } + @Override protected void callback(CauseAction ca, UnmarshallingContext context) { + // if we are being read in from an older version + if (ca.cause != null) { + if (ca.causes == null) ca.causes = new ArrayList(); + ca.causes.add(ca.cause); + OldDataMonitor.report(context, "1.288"); + } + } } } diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java index 0be7e1b59198574746124f467aacb53ca5fdebb0..09842d1146d9a33886f2f68ab1be7914623d9257 100644 --- a/core/src/main/java/hudson/model/Descriptor.java +++ b/core/src/main/java/hudson/model/Descriptor.java @@ -27,6 +27,7 @@ import hudson.XmlFile; import hudson.BulkChange; import hudson.Util; import static hudson.Util.singleQuote; +import hudson.diagnosis.OldDataMonitor; import hudson.model.listeners.SaveableListener; import hudson.views.ListViewColumn; import net.sf.json.JSONArray; @@ -714,4 +715,10 @@ public abstract class Descriptor> implements Saveable { * Used in {@link #checkMethods} to indicate that there's no check method. */ private static final String NONE = "\u0000"; + + private Object readResolve() { + if (properties!=null) + OldDataMonitor.report(this, "1.62"); + return this; + } } diff --git a/core/src/main/java/hudson/model/FingerprintMap.java b/core/src/main/java/hudson/model/FingerprintMap.java index 38905f783bd8eb5adca4ebc3640b6d4ab79abe83..873f8e4ecc8bf5fa06f63225d95a315c26ab50fa 100644 --- a/core/src/main/java/hudson/model/FingerprintMap.java +++ b/core/src/main/java/hudson/model/FingerprintMap.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,7 @@ package hudson.model; import hudson.Util; +import hudson.diagnosis.OldDataMonitor; import hudson.util.KeyedDataStorage; import java.io.File; @@ -56,7 +57,7 @@ public final class FingerprintMap extends KeyedDataStorage { /** * Fix deserialization of older data. - * - * @return this. */ - private Object readResolve() { - // If we are being read back in from an older version - if (localizibleDescription == null) { - localizibleDescription = new NonLocalizable(description == null ? "" : description); + public static class ConverterImpl extends XStream2.PassthruConverter { + public ConverterImpl(XStream2 xstream) { super(xstream); } + @Override protected void callback(HealthReport hr, UnmarshallingContext context) { + // If we are being read back in from an older version + if (hr.localizibleDescription == null) { + hr.localizibleDescription = new NonLocalizable(hr.description == null ? "" : hr.description); + OldDataMonitor.report(context, "1.256"); + } } - return this; } /** @@ -339,7 +343,7 @@ public class HealthReport implements Serializable, Comparable { */ @Override public String toString(Locale locale) { - return nonLocalizable; //To change body of overridden methods use File | Settings | File Templates. + return nonLocalizable; } /** @@ -347,8 +351,7 @@ public class HealthReport implements Serializable, Comparable { */ @Override public String toString() { - return nonLocalizable; //To change body of overridden methods use File | Settings | File Templates. + return nonLocalizable; } } - } diff --git a/core/src/main/java/hudson/model/ParametersAction.java b/core/src/main/java/hudson/model/ParametersAction.java index c0c0050640483a955c86266fba23d41aa18e0a82..a139bdbd546089cc0d8777059acb1acdd7610a8f 100644 --- a/core/src/main/java/hudson/model/ParametersAction.java +++ b/core/src/main/java/hudson/model/ParametersAction.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Jean-Baptiste Quenot, Seiji Sogabe, Tom Huybrechts + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Jean-Baptiste Quenot, Seiji Sogabe, Tom Huybrechts * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,7 @@ package hudson.model; import hudson.Util; import hudson.EnvVars; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Queue.QueueAction; import hudson.tasks.BuildStep; import hudson.tasks.BuildWrapper; @@ -139,11 +140,17 @@ public class ParametersAction implements Action, Iterable, Queue return !parameters.isEmpty(); } else { // I don't think we need multiple ParametersActions, but let's be defensive - Set parameters = new HashSet(); + Set params = new HashSet(); for (ParametersAction other: others) { - parameters.addAll(other.parameters); + params.addAll(other.parameters); } - return !parameters.equals(new HashSet(this.parameters)); + return !params.equals(new HashSet(this.parameters)); } } + + private Object readResolve() { + if (build != null) + OldDataMonitor.report(build, "1.283"); + return this; + } } diff --git a/core/src/main/java/hudson/model/Project.java b/core/src/main/java/hudson/model/Project.java index c4ae0450cd4b6a38e89e548dd0138cc2e4b03ca0..b3a6c53e1fb043ee4d6b401f89c6ee8b7001d6fe 100644 --- a/core/src/main/java/hudson/model/Project.java +++ b/core/src/main/java/hudson/model/Project.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Jorg Heymans, Stephen Connolly, Tom Huybrechts + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Jorg Heymans, Stephen Connolly, Tom Huybrechts * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,7 @@ package hudson.model; import hudson.Util; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Descriptor.FormException; import hudson.tasks.BuildStep; import hudson.tasks.BuildStepDescriptor; @@ -85,9 +86,11 @@ public abstract class Project

,B extends Build> public void onLoad(ItemGroup parent, String name) throws IOException { super.onLoad(parent, name); - if(buildWrappers==null) + if (buildWrappers==null) { // it didn't exist in < 1.64 buildWrappers = new DescribableList>(this); + OldDataMonitor.report(this, "1.64"); + } builders.setOwner(this); publishers.setOwner(this); buildWrappers.setOwner(this); @@ -217,4 +220,9 @@ public abstract class Project

,B extends Build> */ @Deprecated private transient String slave; + + private Object readResolve() { + if (slave != null) OldDataMonitor.report(this, "1.60"); + return this; + } } diff --git a/core/src/main/java/hudson/model/Result.java b/core/src/main/java/hudson/model/Result.java index 2dd00a56d2b06b61cd809ee382837dc51066474d..a21cbdb8a2c8ba0094f4961499528301c49ef617 100644 --- a/core/src/main/java/hudson/model/Result.java +++ b/core/src/main/java/hudson/model/Result.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -109,13 +109,6 @@ public final class Result implements Serializable, CustomExportedBean { public String toString() { return name; } - - private Object readResolve() { - for (Result r : all) - if (ordinal==r.ordinal) - return r; - return FAILURE; - } public String toExportedObject() { return name; diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 65427e78bf89d73950f3a29cbcac787d184d44a9..09b2281a4c89453cc3b5ec69bcf15fb9c653315f 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Erik Ramfelt, Martin Eigenbrodt, Stephen Connolly, Tom Huybrechts + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Erik Ramfelt, Martin Eigenbrodt, Stephen Connolly, Tom Huybrechts * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,7 @@ import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.Launcher.RemoteLauncher; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Descriptor.FormException; import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; @@ -51,7 +52,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -117,7 +117,7 @@ public abstract class Slave extends Node implements Serializable { */ private String label=""; - private /*almost final*/ DescribableList,NodePropertyDescriptor> nodeProperties = new DescribableList,NodePropertyDescriptor>(Hudson.getInstance()); + private /*almost final*/ DescribableList,NodePropertyDescriptor> nodeProperties = new DescribableList,NodePropertyDescriptor>(Hudson.getInstance()); /** * Lazily computed set of labels from {@link #label}. @@ -156,12 +156,6 @@ public abstract class Slave extends Node implements Serializable { if (name.equals("")) throw new FormException(Messages.Slave_InvalidConfig_NoName(), null); - // this prevents the config from being saved when slaves are offline. - // on a large deployment with a lot of slaves, some slaves are bound to be offline, - // so this check is harmful. - //if (!localFS.exists()) - // throw new FormException("Invalid slave configuration for " + name + ". No such directory exists: " + localFS, null); - // if (remoteFS.equals("")) // throw new FormException(Messages.Slave_InvalidConfig_NoRemoteDir(name), null); @@ -340,6 +334,8 @@ public abstract class Slave extends Node implements Serializable { if(command.length()>0) command += ' '; agentCommand = command+"java -jar ~/bin/slave.jar"; } + if (command!=null || localFS!=null) + OldDataMonitor.report(Hudson.getInstance(), "1.69"); if (launcher == null) { launcher = (agentCommand == null || agentCommand.trim().length() == 0) ? new JNLPLauncher() diff --git a/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java b/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java index ee2b41b8db8699d3fa9ce5d9c5163cb7639f7761..89cc85dc0afae76cce8b701af2f45a993129689b 100644 --- a/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java +++ b/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc., Peter Hayes, Tom Huybrechts + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc., Peter Hayes, Tom Huybrechts * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ */ package hudson.security; +import hudson.diagnosis.OldDataMonitor; import hudson.model.AbstractProject; import hudson.model.Item; import hudson.model.Job; @@ -32,6 +33,7 @@ import hudson.model.Hudson; import hudson.model.Run; import hudson.Extension; import hudson.util.FormValidation; +import hudson.util.RobustReflectionConverter; import java.util.Arrays; import java.util.HashMap; @@ -188,12 +190,6 @@ public class AuthorizationMatrixProperty extends JobProperty> { } } - private Object readResolve() { - GlobalMatrixAuthorizationStrategy.migrateHudson2324(grantedPermissions); - acl = new AclImpl(); - return this; - } - public SidACL getACL() { return acl; } @@ -264,20 +260,22 @@ public class AuthorizationMatrixProperty extends JobProperty> { reader.getValue(); // we used to use this but not any more. reader.moveUp(); } - while (reader.hasMoreChildren()) { - reader.moveDown(); + while (reader.hasMoreChildren()) { + reader.moveDown(); try { as.add(reader.getValue()); } catch (IllegalArgumentException ex) { Logger.getLogger(AuthorizationMatrixProperty.class.getName()) .log(Level.WARNING,"Skipping a non-existent permission",ex); + RobustReflectionConverter.addErrorInContext(context, ex); } - reader.moveUp(); - } + reader.moveUp(); + } - GlobalMatrixAuthorizationStrategy.migrateHudson2324(as.grantedPermissions); + if (GlobalMatrixAuthorizationStrategy.migrateHudson2324(as.grantedPermissions)) + OldDataMonitor.report(context, "1.301"); - return as; - } - } + return as; + } + } } diff --git a/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java b/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java index 79ecc158e5b97b06176746386e79dee748398548..8da2bb45e90f4fa3cde3f69f1fe3802c6699cf91 100644 --- a/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java +++ b/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc. + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! 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 @@ -28,12 +28,14 @@ import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.Item; import hudson.util.FormValidation; -import hudson.util.VersionNumber; import hudson.util.FormValidation.Kind; +import hudson.util.VersionNumber; +import hudson.util.RobustReflectionConverter; import hudson.Functions; import hudson.Extension; import net.sf.json.JSONObject; @@ -56,7 +58,6 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.io.IOException; -import java.io.Serializable; /** * Role-based authorization via a matrix. @@ -113,31 +114,27 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy { return sids; } - /** - * In earlier version of Hudson we used to use reflection converter, which calls this method. - * This is now unmarshaller via {@link ConverterImpl} - */ - private Object readResolve() { - migrateHudson2324(grantedPermissions); - acl = new AclImpl(); - return this; - } - /** * Due to HUDSON-2324, we want to inject Item.READ permission to everyone who has Hudson.READ, - * to remain backward compatible + * to remain backward compatible. * @param grantedPermissions */ - /*package*/ static void migrateHudson2324(Map> grantedPermissions) { + /*package*/ static boolean migrateHudson2324(Map> grantedPermissions) { + boolean result = false; if(Hudson.getInstance().isUpgradedFromBefore(new VersionNumber("1.300.*"))) { Set f = grantedPermissions.get(Hudson.READ); - if(f!=null) { + if (f!=null) { Set t = grantedPermissions.get(Item.READ); - if(t!=null) t.addAll(f); - else t=new HashSet(f); + if (t!=null) + result = t.addAll(f); + else { + t = new HashSet(f); + result = true; + } grantedPermissions.put(Item.READ,t); } } + return result; } /** @@ -221,11 +218,13 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy { } catch (IllegalArgumentException ex) { Logger.getLogger(GlobalMatrixAuthorizationStrategy.class.getName()) .log(Level.WARNING,"Skipping a non-existent permission",ex); + RobustReflectionConverter.addErrorInContext(context, ex); } reader.moveUp(); } - migrateHudson2324(as.grantedPermissions); + if (migrateHudson2324(as.grantedPermissions)) + OldDataMonitor.report(context, "1.301"); return as; } diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index 8f181dc9cd0a77c4f14e3eabe25d11df5c165088..986d9a4aaea8bf0c4d6135df988822c523173ab4 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, David Calavera, Seiji Sogabe + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, David Calavera, Seiji Sogabe * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,9 +23,11 @@ */ package hudson.security; +import com.thoughtworks.xstream.converters.UnmarshallingContext; import groovy.lang.Binding; import hudson.Extension; import hudson.Util; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.ManagementLink; @@ -37,6 +39,7 @@ import hudson.tasks.Mailer; import hudson.util.PluginServletFilter; import hudson.util.Protector; import hudson.util.Scrambler; +import hudson.util.XStream2; import hudson.util.spring.BeanBuilder; import net.sf.json.JSONObject; import org.acegisecurity.Authentication; @@ -405,11 +408,15 @@ public class HudsonPrivateSecurityRealm extends SecurityRealm implements ModelOb return user==null; } - private Object readResolve() { - // If we are being read back in from an older version - if (password!=null && passwordHash==null) - passwordHash = PASSWORD_ENCODER.encodePassword(Scrambler.descramble(password),null); - return this; + public static class ConverterImpl extends XStream2.PassthruConverter

{ + public ConverterImpl(XStream2 xstream) { super(xstream); } + @Override protected void callback(Details d, UnmarshallingContext context) { + // Convert to hashed password and report to monitor if we load old data + if (d.password!=null && d.passwordHash==null) { + d.passwordHash = PASSWORD_ENCODER.encodePassword(Scrambler.descramble(d.password),null); + OldDataMonitor.report(context, "1.283"); + } + } } @Extension diff --git a/core/src/main/java/hudson/tasks/Mailer.java b/core/src/main/java/hudson/tasks/Mailer.java index e363e177363cac98529ebbd22c1e6681f6b0b80a..10412a0d25ae0da2558d072a48586dcacbc2f5c2 100644 --- a/core/src/main/java/hudson/tasks/Mailer.java +++ b/core/src/main/java/hudson/tasks/Mailer.java @@ -27,6 +27,7 @@ import hudson.Launcher; import hudson.Functions; import hudson.Extension; import hudson.Util; +import hudson.diagnosis.OldDataMonitor; import static hudson.Util.fixEmptyAndTrim; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; @@ -36,10 +37,12 @@ import hudson.model.UserPropertyDescriptor; import hudson.model.Hudson; import hudson.util.FormValidation; import hudson.util.Secret; +import hudson.util.XStream2; import org.apache.tools.ant.types.selectors.SelectorUtils; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.export.Exported; +import com.thoughtworks.xstream.converters.UnmarshallingContext; import javax.mail.Authenticator; import javax.mail.Message; @@ -86,7 +89,7 @@ public class Mailer extends Notifier { public boolean sendToIndividuals; // TODO: left so that XStream won't get angry. figure out how to set the error handling behavior - // in XStream. + // in XStream. Deprecated since 2005-04-23. private transient String from; private transient String subject; private transient boolean failureOnly; @@ -483,4 +486,12 @@ public class Mailer extends Notifier { * Debug probe point to be activated by the scripting console. */ public static boolean debug = false; + + public static class ConverterImpl extends XStream2.PassthruConverter { + public ConverterImpl(XStream2 xstream) { super(xstream); } + @Override protected void callback(Mailer m, UnmarshallingContext context) { + if (m.from != null || m.subject != null || m.failureOnly) + OldDataMonitor.report(context, "1.10"); + } + } } diff --git a/core/src/main/java/hudson/util/RobustCollectionConverter.java b/core/src/main/java/hudson/util/RobustCollectionConverter.java index 693bc69d99937e7714f464991ca036068df4bf6a..5d2e145a8c632211bac63e86d0ca5c60b5265833 100644 --- a/core/src/main/java/hudson/util/RobustCollectionConverter.java +++ b/core/src/main/java/hudson/util/RobustCollectionConverter.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,7 +35,6 @@ import com.thoughtworks.xstream.XStream; import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.logging.Level; import java.util.logging.Logger; import static java.util.logging.Level.WARNING; @@ -87,8 +86,10 @@ public class RobustCollectionConverter extends CollectionConverter { collection.add(item); } catch (CannotResolveClassException e) { LOGGER.log(WARNING,"Failed to resolve class",e); + RobustReflectionConverter.addErrorInContext(context, e); } catch (LinkageError e) { LOGGER.log(WARNING,"Failed to resolve class",e); + RobustReflectionConverter.addErrorInContext(context, e); } reader.moveUp(); } diff --git a/core/src/main/java/hudson/util/RobustReflectionConverter.java b/core/src/main/java/hudson/util/RobustReflectionConverter.java index 91b45e37b916697278d9b15818e6f93e57ec75db..dd1d92948848fc1e7e7046b9517fd28f2dcd7e4b 100644 --- a/core/src/main/java/hudson/util/RobustReflectionConverter.java +++ b/core/src/main/java/hudson/util/RobustReflectionConverter.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,11 @@ import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.mapper.Mapper; import com.thoughtworks.xstream.mapper.CannotResolveClassException; +import hudson.diagnosis.OldDataMonitor; +import hudson.model.Saveable; + import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -49,7 +53,6 @@ import java.util.HashSet; import java.util.Collection; import java.util.HashMap; import java.util.logging.Logger; -import java.util.logging.Level; import static java.util.logging.Level.WARNING; @@ -178,6 +181,9 @@ public class RobustReflectionConverter implements Converter { public Object doUnmarshal(final Object result, final HierarchicalStreamReader reader, final UnmarshallingContext context) { final SeenFields seenFields = new SeenFields(); Iterator it = reader.getAttributeNames(); + // Remember outermost Saveable encountered, for reporting below + if (result instanceof Saveable && context.get("Saveable") == null) + context.put("Saveable", result); // Process attributes before recursing into child elements. while (it.hasNext()) { @@ -243,18 +249,33 @@ public class RobustReflectionConverter implements Converter { } } catch (NonExistentFieldException e) { LOGGER.log(WARNING,"Skipping a non-existent field "+e.getFieldName(),e); + addErrorInContext(context, e); } catch (CannotResolveClassException e) { LOGGER.log(WARNING,"Skipping a non-existend type",e); + addErrorInContext(context, e); } catch (LinkageError e) { LOGGER.log(WARNING,"Failed to resolve a type",e); + addErrorInContext(context, e); } reader.moveUp(); } + // Report any class/field errors in Saveable objects + if (context.get("ReadError") != null && context.get("Saveable") == result) { + OldDataMonitor.report((Saveable)result, (ArrayList)context.get("ReadError")); + context.put("ReadError", null); + } return result; } + public static void addErrorInContext(UnmarshallingContext context, Throwable e) { + ArrayList list = (ArrayList)context.get("ReadError"); + if (list == null) + context.put("ReadError", list = new ArrayList()); + list.add(e); + } + private boolean fieldDefinedInClass(Object result, String attrName) { // during unmarshalling, unmarshal into transient fields like XStream 1.1.3 //boolean fieldExistsInClass = reflectionProvider.fieldDefinedInClass(attrName, result.getClass()); diff --git a/core/src/main/java/hudson/util/VersionNumber.java b/core/src/main/java/hudson/util/VersionNumber.java index 35c035f62818f443f65641f1b9d99f25db782e98..ce65bd39c3fad162dc0d4387b2fb58fe7c35d546 100644 --- a/core/src/main/java/hudson/util/VersionNumber.java +++ b/core/src/main/java/hudson/util/VersionNumber.java @@ -99,6 +99,10 @@ public class VersionNumber implements Comparable { } } + public int digit(int idx) { + return digits[idx]; + } + @Override public String toString() { StringBuffer buf = new StringBuffer(); diff --git a/core/src/main/java/hudson/util/XStream2.java b/core/src/main/java/hudson/util/XStream2.java index 0c5d9afe0885dc87afa0bc9ff73dc4ae5d443f31..453c3d87771b62fc5660d244fa8d76cd3a2f1fe0 100644 --- a/core/src/main/java/hudson/util/XStream2.java +++ b/core/src/main/java/hudson/util/XStream2.java @@ -38,17 +38,23 @@ import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.mapper.CannotResolveClassException; +import hudson.diagnosis.OldDataMonitor; import hudson.model.Hudson; import hudson.model.Result; +import hudson.model.Saveable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ConcurrentHashMap; /** * {@link XStream} enhanced for additional Java5 support and improved robustness. * @author Kohsuke Kawaguchi */ public class XStream2 extends XStream { + private Converter reflectionConverter; + private ThreadLocal oldData = new ThreadLocal(); + public XStream2() { init(); } @@ -67,13 +73,19 @@ public class XStream2 extends XStream { setClassLoader(h.pluginManager.uberClassLoader); } - return super.unmarshal(reader,root,dataHolder); + Object o = super.unmarshal(reader,root,dataHolder); + if (oldData.get()!=null) { + oldData.remove(); + if (o instanceof Saveable) OldDataMonitor.report((Saveable)o, "1.106"); + } + return o; } @Override protected Converter createDefaultConverter() { // replace default reflection converter - return new RobustReflectionConverter(getMapper(),new JVM().bestReflectionProvider()); + reflectionConverter = new RobustReflectionConverter(getMapper(),new JVM().bestReflectionProvider()); + return reflectionConverter; } private void init() { @@ -113,7 +125,9 @@ public class XStream2 extends XStream { } catch (CannotResolveClassException e) { // If a "-" is found, retry with mapping this to "$" if (elementName.indexOf('-') >= 0) try { - return super.realClass(elementName.replace('-', '$')); + Class c = super.realClass(elementName.replace('-', '$')); + oldData.set(Boolean.TRUE); + return c; } catch (CannotResolveClassException e2) { } // Throw original exception throw e; @@ -123,15 +137,22 @@ public class XStream2 extends XStream { /** * If a class defines a nested {@code ConverterImpl} subclass, use that as a {@link Converter}. + * Its constructor may have XStream/XStream2 and/or Mapper parameters (or no params). */ private static final class AssociatedConverterImpl implements Converter { private final XStream xstream; + private final ConcurrentHashMap cache = + new ConcurrentHashMap(); private AssociatedConverterImpl(XStream xstream) { this.xstream = xstream; } private Converter findConverter(Class t) { + Converter result = cache.get(t); + if (result != null) + // ConcurrentHashMap does not allow null, so use this object to represent null + return result == this ? null : result; try { if(t==null || t.getClassLoader()==null) return null; @@ -141,7 +162,7 @@ public class XStream2 extends XStream { Class[] p = c.getParameterTypes(); Object[] args = new Object[p.length]; for (int i = 0; i < p.length; i++) { - if(p[i]==XStream.class) + if(p[i]==XStream.class || p[i]==XStream2.class) args[i] = xstream; else if(p[i]== Mapper.class) args[i] = xstream.getMapper(); @@ -150,10 +171,13 @@ public class XStream2 extends XStream { } ConverterMatcher cm = (ConverterMatcher)c.newInstance(args); - return cm instanceof SingleValueConverter + result = cm instanceof SingleValueConverter ? new SingleValueConverterWrapper((SingleValueConverter)cm) : (Converter)cm; + cache.put(t, result); + return result; } catch (ClassNotFoundException e) { + cache.put(t, this); // See above.. this object in cache represents null return null; } catch (IllegalAccessException e) { IllegalAccessError x = new IllegalAccessError(); @@ -182,4 +206,38 @@ public class XStream2 extends XStream { return findConverter(context.getRequiredType()).unmarshal(reader,context); } } + + /** + * Create a nested {@code ConverterImpl} subclass that extends this class to run some + * callback code just after a type is unmarshalled by RobustReflectionConverter. + * Example:
 public static class ConverterImpl extends XStream2.PassthruConverter<MyType> {
+     *   public ConverterImpl(XStream2 xstream) { super(xstream); }
+     *   @Override protected void callback(MyType obj, UnmarshallingContext context) {
+     *     ...
+     * 
+ */ + public static abstract class PassthruConverter implements Converter { + private Converter converter; + + public PassthruConverter(XStream2 xstream) { + converter = xstream.reflectionConverter; + } + + public boolean canConvert(Class type) { + // marshal/unmarshal called directly from AssociatedConverterImpl + return false; + } + + public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { + converter.marshal(source, writer, context); + } + + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + Object obj = converter.unmarshal(reader, context); + callback((T)obj, context); + return obj; + } + + protected abstract void callback(T obj, UnmarshallingContext context); + } } diff --git a/core/src/main/resources/hudson/diagnosis/Messages.properties b/core/src/main/resources/hudson/diagnosis/Messages.properties index 72af5c5d0e59895fd8040ea106249d6feec41d5b..e42a504ac5b64a4b7ffc6444bdec6cc980f802cb 100644 --- a/core/src/main/resources/hudson/diagnosis/Messages.properties +++ b/core/src/main/resources/hudson/diagnosis/Messages.properties @@ -1,2 +1,3 @@ MemoryUsageMonitor.USED=Used -MemoryUsageMonitor.TOTAL=Total \ No newline at end of file +MemoryUsageMonitor.TOTAL=Total +OldDataMonitor.DisplayName=Old Data diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly new file mode 100644 index 0000000000000000000000000000000000000000..c74cb7f60f2d7c09ac0a8a35293a4c3aabcd18b5 --- /dev/null +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly @@ -0,0 +1,99 @@ + + + + + + +

${%Manage Old Data}

+

${%blurb.1}

+

${%blurb.2}

+ + + + + + + + ${range} + + + + + + + + + + + + + +
${%Type}${%Name}${%Version}
${obj.class.name}${obj.fullName?:obj.fullDisplayName?:obj.displayName?:obj.name}${range}${item.value.extra}
+

${%blurb.3}

+

${%blurb.4}

+ + + +
+ ${%Resave data files with structure changes no newer than Hudson} + + ${%blurb.5} +
+ + +
+ + ${%No old data was found.} + +
+ +
+

${%Unreadable Data}

+

${%blurb.6}

+ + + + + + + + + + + + +
${%Type}${%Name}${%Error}
${obj.class.name}${obj.fullName?:obj.fullDisplayName?:obj.displayName?:obj.name}${item.value.extra}
+
+
+ + +
+
+
+
diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.properties new file mode 100644 index 0000000000000000000000000000000000000000..0e82e646fa0375c45afdccedf2b423ddeb91dae7 --- /dev/null +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.properties @@ -0,0 +1,51 @@ +# The MIT License +# +# Copyright (c) 2004-2010, Sun Microsystems, Inc., Alan Harder +# +# 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. + +blurb.1=\ + When there are changes in how data is stored on disk, Hudson uses the following strategy: \ + data is migrated to the new structure when it is loaded, but the file is not resaved in the \ + new format. This allows for downgrading Hudson if needed. However, it can also leave data \ + on disk in the old format indefinitely. The table below lists files containing such data, \ + and the Hudson version(s) where the data structure was changed. +blurb.2=\ + Sometimes errors occur while reading data (if a plugin adds some data and that plugin is \ + later disabled, if migration code is not written for structure changes, or if Hudson is \ + downgraded after it has already written data not readable by the older version). \ + These errors are logged, but the unreadable data is then skipped over, allowing Hudson to \ + startup and function properly. +blurb.3=\ + The form below may be used to resave these files in the current format. Doing so means a \ + downgrade to a Hudson release older than the selected version will not be able to read the \ + data stored in the new format. Note that simply using Hudson to create and configure jobs \ + and run builds can save data that may not be readable by older Hudson releases, even when \ + this form is not used. Also if any unreadable data errors are reported in the right side \ + of the table above, note that this data will be lost when the file is resaved. +blurb.4=\ + Eventually the code supporting these data migrations may be removed. Compatibility will be \ + retained for at least 150 releases since the structure change. Versions older than this are \ + in bold above, and it is recommended to resave these files. +blurb.5=\ + (downgrade as far back as the selected version may still be possible) +blurb.6=\ + It is acceptable to leave unreadable data in these files, as Hudson will safely ignore it. \ + To avoid the log messages at Hudson startup you can permanently delete the unreadable data \ + by resaving these files using the button below. diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/message.jelly b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/message.jelly new file mode 100644 index 0000000000000000000000000000000000000000..84bc14f6f1e1ae5f21b0d6f9a5d7ce7ac3a819d1 --- /dev/null +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/message.jelly @@ -0,0 +1,35 @@ + + + +
+
+
+ + +
+ ${%You have data stored in an older format and/or unreadable data.} +
+
+