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