001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.tools.file;
031:
032: import java.io.*;
033: import java.text.*;
034: import java.util.*;
035:
036: /**
037: * Manage the data stream dumps written to a directory.
038: * <p>
039: * This class acts as a factory for files.
040: *
041: */
042: public class DumpDirectory {
043: //
044: private static Map dumpDirs = new HashMap();
045:
046: // the number format used for archiving
047: private static final NumberFormat theFormat;
048:
049: static {
050: theFormat = NumberFormat.getNumberInstance();
051: theFormat.setMaximumFractionDigits(0);
052: theFormat.setMinimumIntegerDigits(19);
053: theFormat.setGroupingUsed(false);
054: }
055:
056: File dir;
057:
058: int currentCount;
059:
060: int dirCount;
061:
062: long lastDump = System.currentTimeMillis();
063:
064: /**
065: * DumpDirectory constructor comment.
066: *
067: * @param dir
068: * docme
069: */
070: public DumpDirectory(File dir) {
071: super ();
072: setDir(dir);
073: }
074:
075: /**
076: * docme
077: *
078: * @param filename
079: * docme
080: *
081: * @return docme
082: */
083: public static DumpDirectory get(Filename filename) {
084: return get(filename.getFilenameAbsolute());
085: }
086:
087: /**
088: * docme
089: *
090: * @param file
091: * docme
092: *
093: * @return docme
094: */
095: public static DumpDirectory get(File file) {
096: return get(file.getAbsolutePath());
097: }
098:
099: /**
100: * docme
101: *
102: * @param name
103: * docme
104: *
105: * @return docme
106: */
107: public static synchronized DumpDirectory get(String name) {
108: DumpDirectory dd = (DumpDirectory) dumpDirs.get(name);
109: if (dd == null) {
110: dd = new DumpDirectory(new File(name)); // just for locking;
111: dd.prepare();
112: dumpDirs.put(name, dd);
113: }
114: return dd;
115: }
116:
117: /**
118: * Insert the method's description here. Creation date: (22.03.2002
119: * 12:45:09)
120: *
121: * @return java.io.File
122: */
123: public java.io.File getDir() {
124: return dir;
125: }
126:
127: /**
128: * docme
129: *
130: * @param name
131: * docme
132: * @param max
133: * docme
134: *
135: * @return docme
136: *
137: * @throws IOException
138: * docme
139: */
140: public synchronized File getDumpFile(String name, int max)
141: throws IOException {
142: File newFile = new File(getDir(), getUniquePrefix() + "."
143: + name);
144:
145: // check directory every time, maybe deleted by some nerd...
146: checkDir();
147: if (!newFile.createNewFile()) {
148: throw new IOException("can not create file "
149: + newFile.getAbsolutePath());
150: }
151: currentCount++;
152: try {
153: checkFiles(max);
154: } catch (IOException e) {
155: // ignore if we can not hold the correct level in dump directory
156: }
157: return newFile;
158: }
159:
160: /**
161: * Insert the method's description here. Creation date: (10.12.2001
162: * 13:23:47)
163: *
164: * @return long
165: */
166: public long getLastDump() {
167: return lastDump;
168: }
169:
170: /**
171: * Insert the method's description here. Creation date: (06.12.2001
172: * 15:37:12)
173: *
174: * @return java.text.NumberFormat
175: */
176: public static java.text.NumberFormat getNf() {
177: return theFormat;
178: }
179:
180: /**
181: * docme
182: *
183: * @return docme
184: */
185: protected long getUniqueMillis() {
186: // ensure unique counter values, millisecondsbased
187: long current = System.currentTimeMillis();
188: while (getLastDump() >= current) {
189: current++;
190: }
191: setLastDump(current);
192: return current;
193: }
194:
195: /**
196: * docme
197: *
198: * @return docme
199: */
200: protected String getUniquePrefix() {
201: return getNf().format(getUniqueMillis());
202: }
203:
204: /**
205: * docme
206: *
207: * @throws IOException
208: * docme
209: */
210: protected void checkDir() throws IOException {
211: // check and create directory
212: if (!getDir().exists()) {
213: if (!getDir().mkdirs()) {
214: throw new IOException(
215: " can't create temporary directory " + getDir());
216: }
217: }
218: }
219:
220: /**
221: * docme
222: *
223: * @param max
224: * docme
225: *
226: * @throws IOException
227: * docme
228: */
229: protected void checkFiles(int max) throws IOException {
230: // todo execute check in low priority daemon
231: //
232: // clean up archive directory, delete oldest files if more than count
233: // available
234: // files must be stored with dump archive to guarantee that preifx sorts
235: // correctly
236: // this implementation relies on the fact, that only the receiver object
237: // manipulates files in the directory
238: if ((max <= 0) || (currentCount <= max)) {
239: return;
240: }
241:
242: String[] fileNames = dir.list();
243: if (fileNames == null) {
244: throw new IOException("can not list directory "
245: + dir.getAbsolutePath());
246: }
247: if (fileNames.length != (dirCount + currentCount)) {
248: prepare(fileNames);
249: }
250: Arrays.sort(fileNames);
251:
252: int delete = Math.min(fileNames.length, currentCount - max);
253: for (int i = 0; i < delete; i++) {
254: File deleteFile = new File(dir, fileNames[i]);
255: if (deleteFile.isFile()) {
256: if (!deleteFile.delete()) {
257: throw new IOException("can not delete file "
258: + deleteFile.getAbsolutePath());
259: }
260: currentCount--;
261: }
262: }
263: }
264:
265: /**
266: * docme
267: */
268: protected void prepare() {
269: String[] fileNames = dir.list();
270: if (fileNames == null) {
271: // error, ignore
272: return;
273: }
274: prepare(fileNames);
275: }
276:
277: /**
278: * docme
279: *
280: * @param fileNames
281: * docme
282: */
283: protected void prepare(String[] fileNames) {
284: currentCount = 0;
285: dirCount = 0;
286: if (fileNames != null) {
287: for (int i = 0; i < fileNames.length; i++) {
288: File f = new File(dir, fileNames[i]);
289: if (f.isFile()) {
290: currentCount++;
291: } else {
292: dirCount++;
293: }
294: }
295: }
296: }
297:
298: /**
299: * docme
300: *
301: * @param newDir
302: * docme
303: */
304: private void setDir(java.io.File newDir) {
305: dir = newDir;
306: }
307:
308: /**
309: * docme
310: *
311: * @param newLastDump
312: * docme
313: */
314: private void setLastDump(long newLastDump) {
315: lastDump = newLastDump;
316: }
317: }
|