提交 07f0405f 编写于 作者: M mindless

Merge r27590:28071 and r28073 from branches/old-data-monitor into trunk.

--Merged-in:------------------------------------------------------------
r27601 | mindless | 2010-02-16 20:21:50 -0800 (Tue, 16 Feb 2010) | 8 lines

Add OldDataMonitor for tracking loaded data that contained old deprecated data,
the Saveable objects that can be save()'ed to persist the data in its new format,
and the Hudson releases where these structure changes took place.
A "Manage Old Data" form explains how Hudson deals with migrating old data
(keeping downgrades possible), lists the old data, and allows upgrade of the data
files with changes up through a selected Hudson release.
Added calls of OldDataMonitor.report() for six old deprecated fields/data changes.

--Merged-in:------------------------------------------------------------
r27683 | mindless | 2010-02-19 11:43:40 -0800 (Fri, 19 Feb 2010) | 5 lines

Expand OldDataMonitor to also track unreadable data found while loading.
These are reported from util.RobustReflectionConverter and RobustCollectionConverter.
"Manage Old Data" screen has additional text about unreadable data, and a form
to resave those files which will discard that data.

--Merged-in:------------------------------------------------------------
r27715 | mindless | 2010-02-20 08:56:51 -0800 (Sat, 20 Feb 2010) | 3 lines

Add Saveable/Item/Run listeners to remove entries tracked by OldDataMonitor
if they are resaved or deleted.

--Merged-in:------------------------------------------------------------
r27970 | mindless | 2010-02-26 09:02:43 -0800 (Fri, 26 Feb 2010) | 6 lines

Add OldDataMonitor reporting for deprecated fields in Mailer and HudsonPrivateSecurityRealm.
Includes fix in RobustReflectionConverter to report against outermost Saveable object
(User object can contain MyViewsProperty which is also Saveable).
Also added XStream2.PassthruConverter which is used to report old data during
unmarshalling when the containing Saveable is not known.

--Merged-in:------------------------------------------------------------
r27982 | mindless | 2010-02-26 11:35:41 -0800 (Fri, 26 Feb 2010) | 3 lines

Fix to show username for User objects instead of "fullDisplayName".
Also increased threshold for possible compatibility removal from 100 to 150 releases.

--Merged-in:------------------------------------------------------------
r27983 | mindless | 2010-02-26 11:43:06 -0800 (Fri, 26 Feb 2010) | 5 lines

Add OldDataMonitor reporting for ParametersAction.build, CauseAction.cause
and UpstreamCause.upstreamCause.
Note: these will make the list of objects on the OldData/manage screen very long
for Hudson installs with lots of builds run on Hudson 1.283 to 1.287.

--Merged-in:------------------------------------------------------------
r27987 | mindless | 2010-02-26 12:33:59 -0800 (Fri, 26 Feb 2010) | 4 lines

Refactored XStream2.PassthruConverter so uses do not need to be registered with
the XStream2 instance, but work for all XStream2 (using ConverterImpl subclass
found by AssociatedConverterImpl).

--Merged-in:------------------------------------------------------------
r27989 | mindless | 2010-02-26 13:38:35 -0800 (Fri, 26 Feb 2010) | 3 lines

Add a cache of ConverterImpl instances in AssociatedConverterImpl to avoid
repeated work in findConverter every time such an object is marshalled/unmarshalled.

--Merged-in:------------------------------------------------------------
r27996 | mindless | 2010-02-26 16:47:19 -0800 (Fri, 26 Feb 2010) | 2 lines

Add OldDataMonitor reporting for HealthReport.description

--Merged-in:------------------------------------------------------------
r27997 | mindless | 2010-02-26 16:49:28 -0800 (Fri, 26 Feb 2010) | 5 lines

Remove readResolve() in 3 classes where a Converter now does that work.
In GlobalMatrixAuthorizationStrategy and AuthorizationMatrixProperty also
add unreadable-data reporting when unable to parse a permission type,
and report when Item.READ permission is added for migration from < 1.301

--Merged-in:------------------------------------------------------------
r28073 | mindless | 2010-02-28 23:18:06 -0800 (Sun, 28 Feb 2010) | 5 lines

Report any old XStream 1.1.x serialized (files from Hudson before 1.106)
to OldDataMonitor, using a ThreadLocal since the unmarshal context is not
available in the mapper.  These use "-" to encode "$" in a class name
instead of "_-" used now.

+-End-of-merged-revisions----------------------------------------------+



git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@28147 71c3de6d-444a-0410-be80-ed276b4c234a
上级 773d1078
/*
* 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<Saveable,VersionRange> data = new HashMap<Saveable,VersionRange>();
public OldDataMonitor() {
super("OldData");
}
@Override
public String getDisplayName() {
return Messages.OldDataMonitor_DisplayName();
}
public boolean isActivated() {
return !data.isEmpty();
}
public synchronized Map<Saveable,VersionRange> 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<Run> runDeleteListener = new RunListener<Run>(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<Throwable> 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<VersionNumber> getVersionList() {
TreeSet<VersionNumber> set = new TreeSet<VersionNumber>();
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<Map.Entry<Saveable,VersionRange>> it = data.entrySet().iterator(); it.hasNext();) {
Map.Entry<Saveable,VersionRange> 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<Map.Entry<Saveable,VersionRange>> it = data.entrySet().iterator(); it.hasNext();) {
Map.Entry<Saveable,VersionRange> entry = it.next();
if (entry.getValue().max == null) {
entry.getKey().save();
it.remove();
}
}
return HttpResponses.forwardToPreviousPage();
}
}
/*
* 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<P extends AbstractProject<P,R>,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<Trigger<?>>();
OldDataMonitor.report(this, "1.28");
}
for (Trigger t : triggers)
t.start(this,false);
if(scm==null)
......
/*
* 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<Cause>();
upstreamCauses.add(upstreamCause);
upstreamCause=null;
public static class ConverterImpl extends XStream2.PassthruConverter<UpstreamCause> {
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<Cause>();
uc.upstreamCauses.add(uc.upstreamCause);
uc.upstreamCause = null;
OldDataMonitor.report(context, "1.288");
}
}
return this;
}
}
......
/*
* 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<Cause> causes = new ArrayList<Cause>();
private List<Cause> causes = new ArrayList<Cause>();
@Exported(visibility=2)
public List<Cause> 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<Cause>();
causes.add(cause);
}
return this;
public static class ConverterImpl extends XStream2.PassthruConverter<CauseAction> {
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<Cause>();
ca.causes.add(ca.cause);
OldDataMonitor.report(context, "1.288");
}
}
}
}
......@@ -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<T extends Describable<T>> 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;
}
}
/*
* 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<Fingerprint,Fingerpri
* Returns true if there's some data in the fingerprint database.
*/
public boolean isReady() {
return new File( Hudson.getInstance().getRootDir(),"fingerprints").exists();
return new File(Hudson.getInstance().getRootDir(),"fingerprints").exists();
}
/**
......@@ -97,6 +98,11 @@ public final class FingerprintMap extends KeyedDataStorage<Fingerprint,Fingerpri
protected Fingerprint load(String key) throws IOException {
return Fingerprint.load(toByteArray(key));
}
private Object readResolve() {
if (core != null) OldDataMonitor.report(Hudson.getInstance(), "1.91");
return this;
}
}
class FingerprintParams {
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
*
* 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,9 @@
*/
package hudson.model;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import hudson.diagnosis.OldDataMonitor;
import hudson.util.XStream2;
import org.jvnet.localizer.Localizable;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
......@@ -304,15 +307,16 @@ public class HealthReport implements Serializable, Comparable<HealthReport> {
/**
* 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<HealthReport> {
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<HealthReport> {
*/
@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<HealthReport> {
*/
@Override
public String toString() {
return nonLocalizable; //To change body of overridden methods use File | Settings | File Templates.
return nonLocalizable;
}
}
}
/*
* 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<ParameterValue>, Queue
return !parameters.isEmpty();
} else {
// I don't think we need multiple ParametersActions, but let's be defensive
Set<ParameterValue> parameters = new HashSet<ParameterValue>();
Set<ParameterValue> params = new HashSet<ParameterValue>();
for (ParametersAction other: others) {
parameters.addAll(other.parameters);
params.addAll(other.parameters);
}
return !parameters.equals(new HashSet<ParameterValue>(this.parameters));
return !params.equals(new HashSet<ParameterValue>(this.parameters));
}
}
private Object readResolve() {
if (build != null)
OldDataMonitor.report(build, "1.283");
return this;
}
}
/*
* 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<P extends Project<P,B>,B extends Build<P,B>>
public void onLoad(ItemGroup<? extends Item> 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<BuildWrapper, Descriptor<BuildWrapper>>(this);
OldDataMonitor.report(this, "1.64");
}
builders.setOwner(this);
publishers.setOwner(this);
buildWrappers.setOwner(this);
......@@ -217,4 +220,9 @@ public abstract class Project<P extends Project<P,B>,B extends Build<P,B>>
*/
@Deprecated
private transient String slave;
private Object readResolve() {
if (slave != null) OldDataMonitor.report(this, "1.60");
return this;
}
}
/*
* 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;
......
/*
* 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<NodeProperty<?>,NodePropertyDescriptor> nodeProperties = new DescribableList<NodeProperty<?>,NodePropertyDescriptor>(Hudson.getInstance());
private /*almost final*/ DescribableList<NodeProperty<?>,NodePropertyDescriptor> nodeProperties = new DescribableList<NodeProperty<?>,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()
......
/*
* 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<Job<?, ?>> {
}
}
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<Job<?, ?>> {
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;
}
}
}
/*
* 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<Permission,Set<String>> grantedPermissions) {
/*package*/ static boolean migrateHudson2324(Map<Permission,Set<String>> grantedPermissions) {
boolean result = false;
if(Hudson.getInstance().isUpgradedFromBefore(new VersionNumber("1.300.*"))) {
Set<String> f = grantedPermissions.get(Hudson.READ);
if(f!=null) {
if (f!=null) {
Set<String> t = grantedPermissions.get(Item.READ);
if(t!=null) t.addAll(f);
else t=new HashSet<String>(f);
if (t!=null)
result = t.addAll(f);
else {
t = new HashSet<String>(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;
}
......
/*
* 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<Details> {
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
......
......@@ -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<Mailer> {
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");
}
}
}
/*
* 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();
}
......
/*
* 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<Throwable>)context.get("ReadError"));
context.put("ReadError", null);
}
return result;
}
public static void addErrorInContext(UnmarshallingContext context, Throwable e) {
ArrayList<Throwable> list = (ArrayList<Throwable>)context.get("ReadError");
if (list == null)
context.put("ReadError", list = new ArrayList<Throwable>());
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());
......
......@@ -99,6 +99,10 @@ public class VersionNumber implements Comparable<VersionNumber> {
}
}
public int digit(int idx) {
return digits[idx];
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
......
......@@ -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<Boolean> oldData = new ThreadLocal<Boolean>();
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<Class,Converter> cache =
new ConcurrentHashMap<Class,Converter>();
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: <pre> public static class ConverterImpl extends XStream2.PassthruConverter&lt;MyType&gt; {
* public ConverterImpl(XStream2 xstream) { super(xstream); }
* @Override protected void callback(MyType obj, UnmarshallingContext context) {
* ...
* </pre>
*/
public static abstract class PassthruConverter<T> 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);
}
}
MemoryUsageMonitor.USED=Used
MemoryUsageMonitor.TOTAL=Total
\ No newline at end of file
MemoryUsageMonitor.TOTAL=Total
OldDataMonitor.DisplayName=Old Data
<!--
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.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout title="${%Manage Old Data}" permission="${app.ADMINISTER}">
<st:include page="sidepanel.jelly" it="${app}"/>
<l:main-panel>
<h1>${%Manage Old Data}</h1>
<p>${%blurb.1}</p>
<p>${%blurb.2}</p>
<table class="pane bigtable" style="width:auto">
<tr><th>${%Type}</th><th>${%Name}</th><th>${%Version}</th><th></th></tr>
<j:forEach var="item" items="${it.data.entrySet()}">
<j:set var="obj" value="${item.key}"/>
<j:set var="range" value="${item.value}"/>
<j:choose>
<j:when test="${range!=''}">
<j:if test="${range.isOld(150)}"><j:set var="range"><b>${range}</b></j:set></j:if>
<tr>
<td>${obj.class.name}</td>
<!-- fullName is first to avoid calling User.get(String) for User object -->
<td>${obj.fullName?:obj.fullDisplayName?:obj.displayName?:obj.name}</td>
<td>${range}</td>
<td style="white-space:normal">${item.value.extra}</td>
</tr>
</j:when>
<j:otherwise>
<j:if test="${item.value.extra!=null}"><j:set var="hasExtra" value="${true}"/></j:if>
</j:otherwise>
</j:choose>
</j:forEach>
</table>
<p>${%blurb.3}</p>
<p>${%blurb.4}</p>
<j:set var="vers" value="${it.versionList}"/>
<j:choose>
<j:when test="${vers.hasNext()}">
<form action="upgrade" method="POST" name="oldDataUpgrade">
${%Resave data files with structure changes no newer than Hudson}
<select name="thruVer">
<j:forEach var="ver" items="${vers}">
<f:option>${ver}</f:option>
</j:forEach>
</select>
${%blurb.5}
<br/>
<f:submit value="${%Upgrade}"/>
</form>
</j:when>
<j:otherwise>
${%No old data was found.}
</j:otherwise>
</j:choose>
<j:if test="${hasExtra}">
<br/>
<h4>${%Unreadable Data}</h4>
<p>${%blurb.6}</p>
<table class="pane bigtable" style="width:auto">
<tr><th>${%Type}</th><th>${%Name}</th><th>${%Error}</th></tr>
<j:forEach var="item" items="${it.data.entrySet()}">
<j:if test="${item.value.extra!=null and item.value==''}">
<j:set var="obj" value="${item.key}"/>
<tr>
<td>${obj.class.name}</td>
<td>${obj.fullName?:obj.fullDisplayName?:obj.displayName?:obj.name}</td>
<td style="white-space:normal">${item.value.extra}</td>
</tr>
</j:if>
</j:forEach>
</table>
<br/>
<form action="discard" method="POST" name="discardUnreadable">
<f:submit value="${%Discard Unreadable Data}"/>
</form>
</j:if>
</l:main-panel>
</l:layout>
</j:jelly>
# 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.
<!--
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.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<div class="warning">
<form method="post" action="${rootURL}/${it.url}/act" name="${it.id}">
<div style="float:right">
<f:submit name="yes" value="${%Manage}"/>
<f:submit name="no" value="${%Dismiss}"/>
</div>
${%You have data stored in an older format and/or unreadable data.}
</form>
</div>
</j:jelly>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册