1 /******************************************************************************* 2 * Copyright (c) 2000, 2014 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 * James Blackburn (Broadcom Corp.) - ongoing development 14 *******************************************************************************/ 15 package org.eclipse.core.internal.localstore; 16 17 import java.io.*; 18 import java.nio.file.Files; 19 import java.nio.file.StandardCopyOption; 20 21 /** 22 * This class should be used when there's a file already in the 23 * destination and we don't want to lose its contents if a 24 * failure writing this stream happens. 25 * Basically, the new contents are written to a temporary location. 26 * If everything goes OK, it is moved to the right place. 27 */ 28 public class SafeFileOutputStream extends OutputStream { 29 protected File temp; 30 protected File target; 31 protected OutputStream output; 32 protected boolean failed; 33 protected static final String EXTENSION = ".bak"; //$NON-NLS-1$ 34 35 /** 36 * Creates an output stream on a file at the given location 37 * @param file The file to be written to 38 */ SafeFileOutputStream(File file)39 public SafeFileOutputStream(File file) throws IOException { 40 this(file.getAbsolutePath(), null); 41 } 42 43 /** 44 * Creates an output stream on a file at the given location 45 * @param targetPath The file to be written to 46 * @param tempPath The temporary location to use, or <code>null</code> to 47 * use the same location as the target path but with a different extension. 48 */ SafeFileOutputStream(String targetPath, String tempPath)49 public SafeFileOutputStream(String targetPath, String tempPath) throws IOException { 50 failed = false; 51 target = new File(targetPath); 52 createTempFile(tempPath); 53 if (!target.exists()) { 54 if (!temp.exists()) { 55 output = new BufferedOutputStream(new FileOutputStream(target)); 56 return; 57 } 58 // If we do not have a file at target location, but we do have at temp location, 59 // it probably means something wrong happened the last time we tried to write it. 60 // So, try to recover the backup file. And, if successful, write the new one. 61 Files.copy(temp.toPath(), target.toPath()); 62 } 63 output = new BufferedOutputStream(new FileOutputStream(temp)); 64 } 65 66 @Override close()67 public void close() throws IOException { 68 try { 69 output.close(); 70 } catch (IOException e) { 71 failed = true; 72 throw e; // rethrow 73 } 74 if (failed) 75 temp.delete(); 76 else 77 commit(); 78 } 79 commit()80 protected void commit() throws IOException { 81 if (!temp.exists()) 82 return; 83 Files.copy(temp.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); 84 temp.delete(); 85 } 86 createTempFile(String tempPath)87 protected void createTempFile(String tempPath) { 88 if (tempPath == null) 89 tempPath = target.getAbsolutePath() + EXTENSION; 90 temp = new File(tempPath); 91 } 92 93 @Override flush()94 public void flush() throws IOException { 95 try { 96 output.flush(); 97 } catch (IOException e) { 98 failed = true; 99 throw e; // rethrow 100 } 101 } 102 getTempFilePath()103 public String getTempFilePath() { 104 return temp.getAbsolutePath(); 105 } 106 107 @Override write(int b)108 public void write(int b) throws IOException { 109 try { 110 output.write(b); 111 } catch (IOException e) { 112 failed = true; 113 throw e; // rethrow 114 } 115 } 116 } 117