001 package org.LiveGraph.dataFile.write; 002 003 import java.io.File; 004 import java.io.FileOutputStream; 005 import java.io.IOException; 006 007 import org.LiveGraph.dataFile.read.PipedInputStream; 008 009 import com.softnetConsult.utils.files.FileTools; 010 import com.softnetConsult.utils.sys.SystemTools; 011 012 013 014 /** 015 * This class provides static convenience methods for creating dedicated data stream writers. 016 * Given just a directory on the local hard drive, this class can automatically 017 * choose a descriptive and unique name for a data file and return an appropriate 018 * {@link DataStreamWriter} object.<br /> 019 * <br /> 020 * An example of how to use this class can be found in 021 * {@link org.LiveGraph.demoDataSource.LiveGraphDemo}.<br /> 022 * 023 * <p style="font-size:smaller;">This product includes software developed by the 024 * <strong>LiveGraph</strong> project and its contributors.<br /> 025 * (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>)<br /> 026 * Copyright (c) 2007-2008 G. Paperin.<br /> 027 * All rights reserved. 028 * </p> 029 * <p style="font-size:smaller;">File: DataStreamWriterFactory.java</p> 030 * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or 031 * without modification, are permitted provided that the following terms and conditions are met: 032 * </p> 033 * <p style="font-size:smaller;">1. Redistributions of source code must retain the above 034 * acknowledgement of the LiveGraph project and its web-site, the above copyright notice, 035 * this list of conditions and the following disclaimer.<br /> 036 * 2. Redistributions in binary form must reproduce the above acknowledgement of the 037 * LiveGraph project and its web-site, the above copyright notice, this list of conditions 038 * and the following disclaimer in the documentation and/or other materials provided with 039 * the distribution.<br /> 040 * 3. All advertising materials mentioning features or use of this software or any derived 041 * software must display the following acknowledgement:<br /> 042 * <em>This product includes software developed by the LiveGraph project and its 043 * contributors.<br />(http://www.live-graph.org)</em><br /> 044 * 4. All advertising materials distributed in form of HTML pages or any other technology 045 * permitting active hyper-links that mention features or use of this software or any 046 * derived software must display the acknowledgment specified in condition 3 of this 047 * agreement, and in addition, include a visible and working hyper-link to the LiveGraph 048 * homepage (http://www.live-graph.org). 049 * </p> 050 * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 051 * OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 052 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 053 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 054 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 055 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 056 * </p> 057 * 058 * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>) 059 * @version {@value org.LiveGraph.LiveGraph#version} 060 */ 061 public class DataStreamWriterFactory { 062 063 /** 064 * Data file extension used if no other specified. 065 */ 066 public static final String defaultFileExtension = "lgdat"; 067 068 069 /** 070 * Creates a new {@link DataStreamWriter} object to write to the specified {@code PipedInputStream}. 071 * Use this method to create writers to use with the memory stream mode. 072 * @param pipedInputStream The input stream to which the data will be directed. 073 * @return A new data stream writer connected to the specified piped stream. 074 * @throws IOException If an error occurs when creating the piped streams (e.g. when the 075 * {@code PipedInputStream} is already connected or in other cases). 076 */ 077 public static DataStreamWriter createDataWriter(PipedInputStream pipedInputStream) throws IOException { 078 PipedOutputStream outs = new PipedOutputStream(pipedInputStream); 079 DataStreamWriter outw = new DataStreamWriter(outs); 080 return outw; 081 } 082 083 /** 084 * Creates a new {@link DataStreamWriter} object for a unique file whose name is created 085 * on the basis of the specified base name and directory as well as an optional 086 * timestamp and an optional counter to make the file name unique.<br /> 087 * This method is a convenience shorthand for 088 * {@code createDataWriter(dataFileDir, dataFileBaseName, defaultFileExtension)}, where 089 * {@code DataStreamWriterFactory.defaultFileExtension} = {@value #defaultFileExtension}. 090 * See the {@linkplain #createDataWriter(String, String, String) three parameters version} 091 * for details. 092 * 093 * @param dataFileDir The directory where the data file will be created. 094 * @param dataFileBaseName The base name for the data file. 095 * @return A new {@link DataStreamWriter} for the file created from the specifed values. 096 * @throws DataFileAlreadyExistsException If the specified files exists and canot be 097 * overwritten. 098 * @see #createDataWriter(String, String, String) 099 * @see com.softnetConsult.utils.files.FileTools#findUniqueFile(String, String, String, boolean) 100 */ 101 public static DataStreamWriter createDataWriter(String dataFileDir, String dataFileBaseName) { 102 return createDataWriter(dataFileDir, dataFileBaseName, defaultFileExtension); 103 } 104 105 106 /** 107 * Creates a new {@link DataStreamWriter} object for a unique file whose name is created 108 * on the basis of the specified base name, extension and directory as well as an optional 109 * timestamp and an optional counter to make the file name unique.<br /> 110 * If despite of the unique file name an output stream cannot be open (this may happen 111 * with a small probability in the case of several competing processes) the semantics of 112 * this method are the same as for {@link #createDataWriter(File, boolean)} (with 113 * {@code overwrite = false}).<br /> 114 * <br /> 115 * This method uses the library method 116 * {@link com.softnetConsult.utils.files.FileTools#findUniqueFile(String, String, String, boolean)} 117 * for generating a unique file path (with {@code alwaysUseTimestamp = false}). 118 * The semantics are as follows:<br /> 119 * 1) A file name is created on the basis of the specified directory, base name and extension. 120 * (e.g. "{@code directory/simulation.lgdat}").<br /> 121 * 2) If the specified file name contains a substring that defines a timestamp marker 122 * (the string "{@value com.softnetConsult.utils.files.FileTools#FILE_NAME_UNIQUE_MAKER_TAG}"), 123 * the marker is replaced by a timestamp based on the current system time. If the specified file 124 * name does not contain a timestamp marker, no timespamp is inserted. 125 * Executed on 20 Jan 2008 at 18:30:15 the result may look like this: 126 * "{@code directory/simulation.08.01.20-18.30.15.lgdat}".<br /> 127 * 3) If no physical file with the resulting file name exists already, this file name is used. 128 * If a file with the resulting path exists, a counter is inserted just before the extension. 129 * The result may look like this: 130 * "{@code directory/simulation.08.01.20-18.30.15(1).lgdat}".<br /> 131 * 4) The uniqueness counter is increased (starting with 1) until the resulting file name 132 * does not describe a physical file that already exists.<br /> 133 * 134 * @param dataFileDir The directory where the data file will be created. 135 * @param dataFileBaseName The base name for the data file. 136 * @param dataFileExt The extension of the data file. 137 * @return A new {@link DataStreamWriter} for the file created from the specifed values. 138 * @throws DataFileAlreadyExistsException If the specified files exists and canot be 139 * overwritten. 140 * @see com.softnetConsult.utils.files.FileTools#findUniqueFile(String, String, String, boolean) 141 */ 142 public static DataStreamWriter createDataWriter(String dataFileDir, 143 String dataFileBaseName, String dataFileExt) { 144 145 File file = FileTools.findUniqueFile(dataFileDir, dataFileBaseName, dataFileExt, false); 146 return createDataWriter(file, false); 147 } 148 149 150 /** 151 * Creates a new {@link DataStreamWriter} object for the specified file path. <br /> 152 * If the file already exists, it may be overwritten if {@code overwrite} is {@code true}.<br /> 153 * If a file output stream cannot be opened either becasue the file already exists and 154 * {@code overwrite} is {@code false} or for any other reason, the stream creation will be 155 * attempted again several times with a short random delay that increases after each attempt. <br /> 156 * If after several attempts the file stream could not be opened, a 157 * {@code DataFileAlreadyExistsException} will be raised.<br /> 158 * This is equivalent to {@code createDataWriter(new File(filePath), overwrite)}. 159 * 160 * @param filePath Path of the file to which the new {@link DataStreamWriter} is to write its output. 161 * @param overwrite Whether to attempt overwriting the specified file it is exists. 162 * @return A new {@link DataStreamWriter} for the specified file. 163 * @throws NullPointerException If the specified file is {@code null}. 164 * @throws DataFileAlreadyExistsException If the specified files exists and canot be 165 * overwritten. 166 */ 167 public static DataStreamWriter createDataWriter(String filePath, boolean overwrite) { 168 169 if (null == filePath) 170 throw new NullPointerException("Cannot create a DataStreamWriter for a null file"); 171 172 return createDataWriter(new File(filePath), overwrite); 173 } 174 175 176 /** 177 * Creates a new {@link DataStreamWriter} object for the specified file. <br /> 178 * If the file already exists, it may be overwritten if {@code overwrite} is {@code true}.<br /> 179 * If a file output stream cannot be opened either becasue the file already exists and 180 * {@code overwrite} is {@code false} or for any other reason, the stream creation will be 181 * attempted again several times with a short random delay that increases after each attempt. <br /> 182 * If after several attempts the file stream could not be opened, a 183 * {@code DataFileAlreadyExistsException} will be raised. 184 * 185 * @param file A file to which the new {@link DataStreamWriter} is to write its output. 186 * @param overwrite Whether to attempt overwriting the specified file it is exists. 187 * @return A new {@link DataStreamWriter} to the specified file. 188 * @throws NullPointerException If the specified file is {@code null}. 189 * @throws DataFileAlreadyExistsException If the specified files exists and canot be 190 * overwritten. 191 */ 192 public static DataStreamWriter createDataWriter(File file, boolean overwrite) { 193 194 if (null == file) 195 throw new NullPointerException("Cannot create a DataStreamWriter for a null file"); 196 197 final int[] MAX_WAIT = new int[] {1000, 1000, 1000, 1500, 1500, 2000, 2000, 2000, 3000, 3000}; 198 final int[] MIN_WAIT = new int[] { 100, 100, 100, 500, 500, 1000, 1000, 1000, 1500, 2000}; 199 200 IOException failureReason = null; 201 for (int retry = 0; retry < MAX_WAIT.length; retry++) { 202 203 if (overwrite && file.exists()) 204 file.delete(); 205 206 if (!file.exists()) { 207 try { 208 FileOutputStream outs = new FileOutputStream(file); 209 DataStreamWriter writer = new DataStreamWriter(outs); 210 return writer; 211 } catch (IOException e) { } 212 } 213 int w = MIN_WAIT[retry] + (int) (Math.random() * (MAX_WAIT[retry] - MIN_WAIT[retry])); 214 SystemTools.sleep(w); 215 } 216 217 DataFileAlreadyExistsException dfaee = new DataFileAlreadyExistsException(file); 218 if (null != failureReason) 219 dfaee.initCause(failureReason); 220 throw dfaee; 221 } 222 223 } // public class DataStreamWriterFactory