All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.XmlFile Maven / Gradle / Ivy

package hudson;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.StreamException;
import com.thoughtworks.xstream.io.xml.XppReader;
import hudson.util.AtomicFileWriter;
import hudson.util.IOException2;
import hudson.util.XStream2;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 * Represents an XML data file that Hudson uses as a data file.
 *
 *
 * 

Evolving data format

*

* Changing data format requires a particular care so that users with * the old data format can migrate to the newer data format smoothly. * *

* Adding a field is the easiest. When you read an old XML that does * not have any data, the newly added field is left to the VM-default * value (if you let XStream create the object, such as * {@link #read()} — which is the majority), or to the value initialized by the * constructor (if the object is created via new and then its * value filled by XStream, such as {@link #unmarshal(Object)}.) * *

* Removing a field requires that you actually leave the field with * transient keyword. When you read the old XML, XStream * will set the value to this field. But when the data is saved, * the field will no longer will be written back to XML. * (It might be possible to tweak XStream so that we can simply * remove fields from the class. Any help appreciated.) * *

* Changing the data structure is usually a combination of the two * above. You'd leave the old data store with transient, * and then add the new data. When you are reading the old XML, * only the old field will be set. When you are reading the new XML, * only the new field will be set. You'll then need to alter the code * so that it will be able to correctly handle both situations, * and that as soon as you see data in the old field, you'll have to convert * that into the new data structure, so that the next save operation * will write the new data (otherwise you'll end up losing the data, because * old fields will be never written back.) * *

* In some limited cases (specifically when the class is the root object * to be read from XML, such as {@link Descriptor}), it is posible * to completely and drastically change the data format. See * {@link Descriptor#load()} for more about this technique. * *

* There's a few other possibilities, such as implementing a custom * {@link Converter} for XStream, or {@link XStream#alias(String, Class) registering an alias}. * * @author Kohsuke Kawaguchi */ public final class XmlFile { private final XStream xs; private final File file; public XmlFile(File file) { this(DEFAULT_XSTREAM,file); } public XmlFile(XStream xs, File file) { this.xs = xs; this.file = file; } /** * Loads the contents of this file into a new object. */ public Object read() throws IOException { Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); try { return xs.fromXML(r); } catch(StreamException e) { throw new IOException2("Unable to read "+file,e); } catch(ConversionException e) { throw new IOException2("Unable to read "+file,e); } finally { r.close(); } } /** * Loads the contents of this file into an existing object. * * @return * The unmarshalled object. Usually the same as o, but would be different * if the XML representation if completely new. */ public Object unmarshal( Object o ) throws IOException { Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8")); try { return xs.unmarshal(new XppReader(r),o); } catch (StreamException e) { throw new IOException2(e); } catch(ConversionException e) { throw new IOException2("Unable to read "+file,e); } finally { r.close(); } } public void write( Object o ) throws IOException { AtomicFileWriter w = new AtomicFileWriter(file); try { w.write("\n"); xs.toXML(o,w); w.commit(); } catch(StreamException e) { throw new IOException2(e); } finally { w.close(); } } public boolean exists() { return file.exists(); } public void mkdirs() { file.getParentFile().mkdirs(); } public String toString() { return file.toString(); } /** * {@link XStream} instance is supposed to be thread-safe. */ private static final XStream DEFAULT_XSTREAM = new XStream2(); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy