001: package dalma.impl;
002:
003: import com.thoughtworks.xstream.XStream;
004: import com.thoughtworks.xstream.io.StreamException;
005: import com.thoughtworks.xstream.io.xml.XppReader;
006:
007: import java.io.BufferedReader;
008: import java.io.BufferedWriter;
009: import java.io.File;
010: import java.io.FileInputStream;
011: import java.io.FileOutputStream;
012: import java.io.IOException;
013: import java.io.InputStreamReader;
014: import java.io.OutputStreamWriter;
015: import java.io.Reader;
016: import java.io.Writer;
017:
018: /**
019: * Represents an XML data file that Hudson uses as a data file.
020: *
021: * @author Kohsuke Kawaguchi
022: */
023: public final class XmlFile {
024: private final XStream xs;
025: private final File file;
026:
027: public XmlFile(File file) {
028: this (new XStream(), file);
029: }
030:
031: public XmlFile(XStream xs, File file) {
032: this .xs = xs;
033: this .file = file;
034: }
035:
036: /**
037: * Loads the contents of this file into a new object.
038: */
039: public Object read(ClassLoader cl) throws IOException {
040: xs.setClassLoader(cl);
041: return read();
042: }
043:
044: /**
045: * Loads the contents of this file into a new object.
046: */
047: public Object read() throws IOException {
048: Reader r = new BufferedReader(new InputStreamReader(
049: new FileInputStream(file), "UTF-8"));
050: try {
051: return xs.fromXML(r);
052: } catch (StreamException e) {
053: throw new IOException2(e);
054: } finally {
055: r.close();
056: }
057: }
058:
059: /**
060: * Loads the contents of this file into an existing object.
061: */
062: public void unmarshal(Object o) throws IOException {
063: Reader r = new BufferedReader(new InputStreamReader(
064: new FileInputStream(file), "UTF-8"));
065: try {
066: xs.unmarshal(new XppReader(r), o);
067: } catch (StreamException e) {
068: throw new IOException2(e);
069: } finally {
070: r.close();
071: }
072: }
073:
074: public void write(Object o) throws IOException {
075: AtomicFileWriter w = new AtomicFileWriter(file);
076: try {
077: w.write("<?xml version='1.0' encoding='UTF-8'?>\n");
078: xs.toXML(o, w);
079: w.commit();
080: } catch (StreamException e) {
081: throw new IOException2(e);
082: } finally {
083: w.close();
084: }
085: }
086:
087: public boolean exists() {
088: return file.exists();
089: }
090:
091: /**
092: * Implements the atomic write operation in which
093: * either the original file is left intact, or the file is completely rewritten.
094: */
095: private static final class AtomicFileWriter extends Writer {
096:
097: private final Writer core;
098: private final File tmpFile;
099: private final File destFile;
100:
101: public AtomicFileWriter(File f) throws IOException {
102: try {
103: tmpFile = File.createTempFile("atomic", null, f
104: .getParentFile());
105: } catch (IOException e) {
106: throw new IOException2(
107: "Failed to create a temp dir in "
108: + f.getParentFile(), e);
109: }
110: destFile = f;
111: core = new BufferedWriter(new OutputStreamWriter(
112: new FileOutputStream(tmpFile), "UTF-8"));
113: }
114:
115: public void write(int c) throws IOException {
116: core.write(c);
117: }
118:
119: public void write(String str, int off, int len)
120: throws IOException {
121: core.write(str, off, len);
122: }
123:
124: public void write(char cbuf[], int off, int len)
125: throws IOException {
126: core.write(cbuf, off, len);
127: }
128:
129: public void flush() throws IOException {
130: core.flush();
131: }
132:
133: public void close() throws IOException {
134: core.close();
135: }
136:
137: public void commit() throws IOException {
138: close();
139: if (destFile.exists() && !destFile.delete())
140: throw new IOException("Unable to delete " + destFile);
141: tmpFile.renameTo(destFile);
142: }
143: }
144:
145: private static final class IOException2 extends IOException {
146: private final Exception cause;
147:
148: public IOException2(String msg, Exception cause) {
149: super (msg);
150: this .cause = cause;
151: }
152:
153: public IOException2(Exception cause) {
154: this (cause.getMessage(), cause);
155: }
156:
157: public Throwable getCause() {
158: return cause;
159: }
160: }
161:
162: }
|