1 /*******************************************************************************
2  * Copyright (c) 2003, 2006 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 
15 package org.eclipse.osgi.framework.internal.reliablefile;
16 
17 import java.io.*;
18 import java.util.zip.Checksum;
19 
20 /**
21  * A ReliableFile FileOutputStream replacement class.
22  * This class can be used just like FileOutputStream. The class
23  * is in partnership with ReliableFileInputStream to avoid losing
24  * file data by using multiple files.
25  *
26  * @see			ReliableFileInputStream
27  */
28 public class ReliableFileOutputStream extends FilterOutputStream {
29 	/**
30 	 * ReliableFile object for the file.
31 	 */
32 	private ReliableFile reliable;
33 
34 	/**
35 	 * Checksum calculator
36 	 */
37 	private Checksum crc;
38 
39 	private boolean outputOpen = false;
40 
41 	/**
42 	 * Constructs a new ReliableFileOutputStream on the File <code>file</code>.  If the
43 	 * file exists, it is written over.  See the constructor which can append to
44 	 * the file if so desired.
45 	 *
46 	 * @param		file		the File on which to stream reads.
47 	 * @exception 	java.io.IOException If an error occurs opening the file.
48 	 */
ReliableFileOutputStream(File file)49 	public ReliableFileOutputStream(File file) throws IOException {
50 		this(ReliableFile.getReliableFile(file), false);
51 	}
52 
53 	/**
54 	 * Constructs a new ReliableFileOutputStream on the File <code>file</code>.
55 	 *
56 	 * @param		file		the File on which to stream reads.
57 	 * @param		append		a boolean indicating whether or not to append to an existing file.
58 	 * @exception 	java.io.IOException If an error occurs opening the file.
59 	 */
ReliableFileOutputStream(File file, boolean append)60 	public ReliableFileOutputStream(File file, boolean append) throws IOException {
61 		this(ReliableFile.getReliableFile(file), append);
62 	}
63 
64 	/**
65 	 * Constructs a new ReliableFileOutputStream on the file named <code>name</code>. If
66 	 * the file exists, it is written over.  See the constructor which can append to
67 	 * the file if so desired.
68 	 * The <code>name</code> may be absolute or relative
69 	 * to the System property <code>"user.dir"</code>.
70 	 *
71 	 * @param		name	the file on which to stream writes.
72 	 * @exception 	java.io.IOException If an error occurs opening the file.
73 	 */
ReliableFileOutputStream(String name)74 	public ReliableFileOutputStream(String name) throws IOException {
75 		this(ReliableFile.getReliableFile(name), false);
76 	}
77 
78 	/**
79 	 * Constructs a new ReliableFileOutputStream on the file named <code>name</code>.
80 	 * The <code>name</code> may be absolute or relative
81 	 * to the System property <code>"user.dir"</code>.
82 	 *
83 	 * @param		name	the file on which to stream writes.
84 	 * @param		append		a boolean indicating whether or not to append to an existing file.
85 	 * @exception 	java.io.IOException If an error occurs opening the file.
86 	 */
ReliableFileOutputStream(String name, boolean append)87 	public ReliableFileOutputStream(String name, boolean append) throws IOException {
88 		this(ReliableFile.getReliableFile(name), append);
89 	}
90 
91 	/**
92 	 * Private constructor used by other constructors.
93 	 *
94 	 * @param		reliable		the ReliableFile on which to read.
95 	 * @param		append		a boolean indicating whether or not to append to an existing file.
96 	 * @exception 	java.io.IOException If an error occurs opening the file.
97 	 */
ReliableFileOutputStream(ReliableFile reliable, boolean append)98 	private ReliableFileOutputStream(ReliableFile reliable, boolean append) throws IOException {
99 		super(reliable.getOutputStream(append, ReliableFile.GENERATION_LATEST));
100 
101 		this.reliable = reliable;
102 		outputOpen = true;
103 		if (append)
104 			crc = reliable.getFileChecksum();
105 		else
106 			crc = reliable.getChecksumCalculator();
107 	}
108 
109 	/**
110 	 * Closes this output stream and releases any system resources
111 	 * associated with this stream. The general contract of <code>close</code>
112 	 * is that it closes the output stream. A closed stream cannot perform
113 	 * output operations and cannot be reopened.
114 	 *
115 	 * @exception 	java.io.IOException If an error occurs closing the file.
116 	 */
117 	@Override
close()118 	public synchronized void close() throws IOException {
119 		closeIntermediateFile();
120 		reliable.closeOutputFile(crc);
121 		// if the previouse closeOutpuFile() throws exception,
122 		//  we don't null out reliable to give another opportunity
123 		//  to rename the file.
124 		reliable = null;
125 	}
126 
closeIntermediateFile()127 	public File closeIntermediateFile() throws IOException {
128 		if (reliable == null)
129 			throw new IOException("ReliableFile stream not open"); //$NON-NLS-1$
130 		if (outputOpen) {
131 			// tag on our signature and checksum
132 			reliable.writeChecksumSignature(out, crc);
133 			out.flush();
134 			try {
135 				((FileOutputStream) out).getFD().sync();
136 			} catch (IOException e) {
137 				// just ignore this Exception
138 				//Debug
139 				e.printStackTrace();
140 			}
141 			out.close();
142 			outputOpen = false;
143 		}
144 		return reliable.getOutputFile();
145 	}
146 
147 	/**
148 	 * Override default FilterOutputStream method.
149 	 * @see FilterOutputStream#write(byte[])
150 	 */
151 	@Override
write(byte[] b)152 	public void write(byte[] b) throws IOException {
153 		this.write(b, 0, b.length);
154 	}
155 
156 	/**
157 	 * Override default FilterOutputStream method.
158 	 * @see FilterOutputStream#write(byte[], int, int)
159 	 */
160 	@Override
write(byte[] b, int off, int len)161 	public void write(byte[] b, int off, int len) throws IOException {
162 		out.write(b, off, len);
163 		crc.update(b, off, len);
164 	}
165 
166 	/**
167 	 * Override default FilterOutputStream method.
168 	 * @see FilterOutputStream#write(int)
169 	 */
170 	@Override
write(int b)171 	public void write(int b) throws IOException {
172 		out.write(b);
173 		crc.update((byte) b);
174 	}
175 
abort()176 	public void abort() {
177 		if (reliable == null)
178 			return;
179 		if (outputOpen) {
180 			try {
181 				out.close();
182 			} catch (IOException e) {/*ignore*/
183 			}
184 			outputOpen = false;
185 		}
186 		reliable.abortOutputFile();
187 		reliable = null;
188 	}
189 }
190