KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > XmlFile


1 package hudson;
2
3 import com.thoughtworks.xstream.XStream;
4 import com.thoughtworks.xstream.converters.ConversionException;
5 import com.thoughtworks.xstream.converters.Converter;
6 import com.thoughtworks.xstream.io.StreamException;
7 import com.thoughtworks.xstream.io.xml.XppReader;
8 import hudson.model.Descriptor;
9 import hudson.util.AtomicFileWriter;
10 import hudson.util.IOException2;
11 import hudson.util.XStream2;
12
13 import java.io.BufferedReader JavaDoc;
14 import java.io.File JavaDoc;
15 import java.io.FileInputStream JavaDoc;
16 import java.io.IOException JavaDoc;
17 import java.io.InputStreamReader JavaDoc;
18 import java.io.Reader JavaDoc;
19 import java.util.logging.Logger JavaDoc;
20
21 /**
22  * Represents an XML data file that Hudson uses as a data file.
23  *
24  *
25  * <h2>Evolving data format</h2>
26  * <p>
27  * Changing data format requires a particular care so that users with
28  * the old data format can migrate to the newer data format smoothly.
29  *
30  * <p>
31  * Adding a field is the easiest. When you read an old XML that does
32  * not have any data, the newly added field is left to the VM-default
33  * value (if you let XStream create the object, such as
34  * {@link #read()} &mdash; which is the majority), or to the value initialized by the
35  * constructor (if the object is created via <tt>new</tt> and then its
36  * value filled by XStream, such as {@link #unmarshal(Object)}.)
37  *
38  * <p>
39  * Removing a field requires that you actually leave the field with
40  * <tt>transient</tt> keyword. When you read the old XML, XStream
41  * will set the value to this field. But when the data is saved,
42  * the field will no longer will be written back to XML.
43  * (It might be possible to tweak XStream so that we can simply
44  * remove fields from the class. Any help appreciated.)
45  *
46  * <p>
47  * Changing the data structure is usually a combination of the two
48  * above. You'd leave the old data store with <tt>transient</tt>,
49  * and then add the new data. When you are reading the old XML,
50  * only the old field will be set. When you are reading the new XML,
51  * only the new field will be set. You'll then need to alter the code
52  * so that it will be able to correctly handle both situations,
53  * and that as soon as you see data in the old field, you'll have to convert
54  * that into the new data structure, so that the next <tt>save</tt> operation
55  * will write the new data (otherwise you'll end up losing the data, because
56  * old fields will be never written back.)
57  *
58  * <p>
59  * In some limited cases (specifically when the class is the root object
60  * to be read from XML, such as {@link Descriptor}), it is possible
61  * to completely and drastically change the data format. See
62  * {@link Descriptor#load()} for more about this technique.
63  *
64  * <p>
65  * There's a few other possibilities, such as implementing a custom
66  * {@link Converter} for XStream, or {@link XStream#alias(String, Class) registering an alias}.
67  *
68  * @author Kohsuke Kawaguchi
69  */

70 public final class XmlFile {
71     private final XStream xs;
72     private final File JavaDoc file;
73
74     public XmlFile(File JavaDoc file) {
75         this(DEFAULT_XSTREAM,file);
76     }
77
78     public XmlFile(XStream xs, File JavaDoc file) {
79         this.xs = xs;
80         this.file = file;
81     }
82
83     public File JavaDoc getFile() {
84         return file;
85     }
86
87     /**
88      * Loads the contents of this file into a new object.
89      */

90     public Object JavaDoc read() throws IOException JavaDoc {
91         LOGGER.fine("Reading "+file);
92         Reader r = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(new FileInputStream JavaDoc(file), "UTF-8"));
93         try {
94             return xs.fromXML(r);
95         } catch(StreamException e) {
96             throw new IOException2("Unable to read "+file,e);
97         } catch(ConversionException e) {
98             throw new IOException2("Unable to read "+file,e);
99         } finally {
100             r.close();
101         }
102     }
103
104     /**
105      * Loads the contents of this file into an existing object.
106      *
107      * @return
108      * The unmarshalled object. Usually the same as <tt>o</tt>, but would be different
109      * if the XML representation if completely new.
110      */

111     public Object JavaDoc unmarshal( Object JavaDoc o ) throws IOException JavaDoc {
112         Reader r = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(new FileInputStream JavaDoc(file),"UTF-8"));
113         try {
114             return xs.unmarshal(new XppReader(r),o);
115         } catch (StreamException e) {
116             throw new IOException2(e);
117         } catch(ConversionException e) {
118             throw new IOException2("Unable to read "+file,e);
119         } finally {
120             r.close();
121         }
122     }
123
124     public void write( Object JavaDoc o ) throws IOException JavaDoc {
125         AtomicFileWriter w = new AtomicFileWriter(file);
126         try {
127             w.write("<?xml version='1.0' encoding='UTF-8'?>\n");
128             xs.toXML(o,w);
129             w.commit();
130         } catch(StreamException e) {
131             throw new IOException2(e);
132         } finally {
133             w.close();
134         }
135     }
136
137     public boolean exists() {
138         return file.exists();
139     }
140
141     public void mkdirs() {
142         file.getParentFile().mkdirs();
143     }
144
145     public String JavaDoc toString() {
146         return file.toString();
147     }
148
149     /**
150      * {@link XStream} instance is supposed to be thread-safe.
151      */

152     private static final XStream DEFAULT_XSTREAM = new XStream2();
153
154     private static final Logger JavaDoc LOGGER = Logger.getLogger(XmlFile.class.getName());
155 }
156
Popular Tags