001: /*
002: * $Id: AxionFileSystem.java,v 1.8 2005/12/20 18:32:58 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2005 Axion Development Team. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above
011: * copyright notice, this list of conditions and the following
012: * disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
020: * not be used to endorse or promote products derived from this
021: * software without specific prior written permission.
022: *
023: * 4. Products derived from this software may not be called "Axion", nor
024: * may "Tigris" or "Axion" appear in their names without specific prior
025: * written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038: * =======================================================================
039: */
040:
041: package org.axiondb.io;
042:
043: import java.io.BufferedInputStream;
044: import java.io.BufferedOutputStream;
045: import java.io.DataInputStream;
046: import java.io.DataOutputStream;
047: import java.io.File;
048: import java.io.FileInputStream;
049: import java.io.FileNotFoundException;
050: import java.io.FileOutputStream;
051: import java.io.IOException;
052: import java.io.InputStream;
053: import java.io.ObjectInputStream;
054: import java.io.ObjectOutputStream;
055: import java.io.OutputStream;
056: import java.io.RandomAccessFile;
057:
058: import org.apache.commons.collections.primitives.ArrayIntList;
059: import org.apache.commons.collections.primitives.ArrayUnsignedIntList;
060: import org.apache.commons.collections.primitives.IntList;
061: import org.apache.commons.collections.primitives.LongList;
062: import org.axiondb.AxionException;
063: import org.axiondb.util.ExceptionConverter;
064:
065: /**
066: * Axion File System, creates file input/output streams and wraps then into a custom
067: * BufferedDataStream, which improves perfermance significantly.
068: *
069: * @version $Revision: 1.8 $ $Date: 2005/12/20 18:32:58 $
070: * @author Ahimanikya Satapathy
071: */
072: public class AxionFileSystem {
073:
074: public AxionFileSystem() {
075: }
076:
077: public void closeInputStream(InputStream in) {
078: if (in != null) {
079: try {
080: in.close();
081: } catch (Exception e) {
082: }
083: }
084: }
085:
086: public void closeOutputStream(OutputStream out) {
087: if (out != null) {
088: try {
089: out.close();
090: } catch (Exception t) {
091: }
092: }
093: }
094:
095: /**
096: * create a new file and wrap wrap the stream with BufferedDataOutputStream which
097: * improves perfermance significantly.
098: */
099: public BufferedDataOutputStream createBufferedDOS(File file)
100: throws AxionException {
101: try {
102: return new BufferedDataOutputStream(open(file, true));
103: } catch (IOException e) {
104: throw new AxionException(e);
105: }
106: }
107:
108: public boolean createNewFile(File file) throws AxionException {
109: try {
110: return file.createNewFile();
111: } catch (IOException e) {
112: throw new AxionException(e);
113: }
114: }
115:
116: public ObjectOutputStream createObjectOutputSteam(File file)
117: throws IOException {
118: return new ObjectOutputStream(new BufferedOutputStream(
119: new FileOutputStream(file)));
120: }
121:
122: public DataOutputStream createDataOutputSteam(File file)
123: throws IOException {
124: return new DataOutputStream(new BufferedOutputStream(
125: new FileOutputStream(file)));
126: }
127:
128: public PidxList newPidxList(int count, File file, boolean readonly)
129: throws AxionException {
130: return new PidxList(count, file, readonly);
131: }
132:
133: /**
134: * Open the file in read only mode.
135: */
136: public AxionInputStream open(File file) throws IOException {
137: if (!file.exists()) {
138: throw new FileNotFoundException(file.toString());
139: }
140: return new AxionFileInputStream(file);
141: }
142:
143: /**
144: * Open file in append mode if overwrite is false, otherwise create new file.
145: */
146: public AxionOutputStream open(File file, boolean overwrite)
147: throws IOException {
148: if (file.exists() && overwrite) {
149: file.delete();
150: file.createNewFile();
151: }
152: File parent = file.getParentFile();
153: if (parent != null) {
154: parent.mkdirs();
155: }
156: return new AxionFileOutputStream(file);
157: }
158:
159: /**
160: * Open file in append mode, position will be set to the end of file. Creates the file
161: * if does not exist yet.
162: */
163: public AxionOutputStream openAppend(File file) throws IOException {
164: return open(file, false);
165: }
166:
167: /**
168: * Open file in read only mode, position will be set to 0. seek can be used to perferm
169: * random access. This will wrap the stream with BufferedDataInputStream which
170: * improves perfermance significantly.
171: */
172: public BufferedDataInputStream openBufferedDIS(File file)
173: throws AxionException {
174: try {
175: return new BufferedDataInputStream(open(file));
176: } catch (IOException e) {
177: throw new AxionException(e);
178: }
179: }
180:
181: /**
182: * Open a outputsteam and points the file pointer to a given start position in the
183: * file.
184: */
185: public BufferedDataOutputStream openBufferedDOS(File file,
186: long startPos) throws AxionException {
187: try {
188: AxionOutputStream out = open(file, true);
189: out.seek(startPos);
190: return new BufferedDataOutputStream(out);
191: } catch (IOException e) {
192: throw new AxionException(e);
193: }
194: }
195:
196: /**
197: * Open file in append mode, position will be set to the end of file. Creates the file
198: * if does not exist yet. This will wrap the stream with BufferedDataOutputStream
199: * which improves perfermance significantly.
200: */
201: public BufferedDataOutputStream openBufferedDOSAppend(File file,
202: int bufferSize) throws AxionException {
203: try {
204: AxionOutputStream out = open(file, false);
205: out.seek(file.length());
206: return new BufferedDataOutputStream(out, bufferSize);
207: } catch (IOException e) {
208: throw new AxionException(e);
209: }
210: }
211:
212: public ObjectInputStream openObjectInputSteam(File file)
213: throws IOException {
214: return new ObjectInputStream(new BufferedInputStream(
215: new FileInputStream(file)));
216: }
217:
218: public DataInputStream openDataInputSteam(File file)
219: throws IOException {
220: return new DataInputStream(new BufferedInputStream(
221: new FileInputStream(file)));
222: }
223:
224: /**
225: * Reads a list of int values from a file.
226: *
227: * @param file the {@link File}to read from
228: */
229: public IntList parseIntFile(File file) throws AxionException {
230: int count = (int) (file.length() / 4L);
231: IntList list = new ArrayIntList(count);
232: DataInputStream in = null;
233: try {
234: in = openBufferedDIS(file);
235: for (int i = 0; i < count; i++) {
236: list.add(in.readInt());
237: }
238: return list;
239: } catch (IOException e) {
240: throw new AxionException("Unable to parse " + file, e);
241: } finally {
242: closeInputStream(in);
243: }
244: }
245:
246: /**
247: * Reads a list of long values from a file.
248: *
249: * @param file the {@link File}to read from
250: */
251: public PidxList parseLongPidx(File file, boolean readOnly)
252: throws AxionException {
253: int count = (int) (file.length() / 8L);
254: byte[] rawdata = new byte[count * 8];
255: readAll(file, rawdata);
256:
257: LongList list = new ArrayUnsignedIntList(count);
258: for (int i = 0, m = count * 8; i < m; i += 8) {
259: long val = ((((long) rawdata[i + 7]) & 0xFF)
260: + ((((long) rawdata[i + 6]) & 0xFF) << 8)
261: + ((((long) rawdata[i + 5]) & 0xFF) << 16)
262: + ((((long) rawdata[i + 4]) & 0xFF) << 24)
263: + ((((long) rawdata[i + 3]) & 0xFF) << 32)
264: + ((((long) rawdata[i + 2]) & 0xFF) << 40)
265: + ((((long) rawdata[i + 1]) & 0xFF) << 48) + ((((long) rawdata[i + 0]) & 0xFF) << 56));
266: list.add(val);
267: }
268: FileUtil.truncate(file, 0); // truncate to zero
269: PidxList pidxList = new PidxList(list, file, readOnly);
270: pidxList.writeAll(); // rewrite the new pidx format
271: return pidxList;
272: }
273:
274: public PidxList parseUnsignedIntPidx(File file, boolean readOnly)
275: throws AxionException {
276: int count = (int) (file.length() / 4L);
277: byte[] rawdata = new byte[count * 4];
278: readAll(file, rawdata);
279:
280: LongList list = new ArrayUnsignedIntList(count);
281: for (int i = 0, m = count * 4; i < m; i += 4) {
282: long val = ((((long) rawdata[i + 3]) & 0xFF)
283: + ((((long) rawdata[i + 2]) & 0xFF) << 8)
284: + ((((long) rawdata[i + 1]) & 0xFF) << 16) + ((((long) rawdata[i + 0]) & 0xFF) << 24))
285: & MAX_UNSIGNED_INT;
286: list.add(val);
287: }
288: return new PidxList(list, file, readOnly);
289: }
290:
291: public void readAll(File file, byte[] rawdata)
292: throws AxionException {
293: InputStream in = null;
294: try {
295: in = open(file);
296: in.read(rawdata);
297: } catch (IOException e) {
298: throw new AxionException(e);
299: } finally {
300: closeInputStream(in);
301: }
302: }
303:
304: /**
305: * Writes a list of <tt>int</tt> values to a file.
306: *
307: * @param file the {@link File}to write to
308: */
309: public void writeIntFile(File file, IntList list)
310: throws AxionException {
311: DataOutputStream out = null;
312: try {
313: out = createBufferedDOS(file);
314: for (int i = 0, I = list.size(); i < I; i++) {
315: out.writeInt(list.get(i));
316: }
317: } catch (IOException e) {
318: throw new AxionException("Unable to write to " + file, e);
319: } finally {
320: closeOutputStream(out);
321: }
322: }
323:
324: /**
325: * Updates an UnsignedInt value to a file.
326: *
327: * @param raf the {@link File}to append to
328: * @param offset the pidx file offset to write
329: * @param value data file pointer for a given pidx offset
330: */
331: public void writeUnsignedInt(BufferedDataOutputStream out,
332: long offset, int value) throws AxionException {
333: try {
334: out.seek(offset);
335: out.writeInt(value);
336: } catch (IOException e) {
337: throw new AxionException("Unable to write to " + out, e);
338: }
339: }
340:
341: /**
342: * Writes a list of <tt>long</tt> values to a file.
343: *
344: * @param file the {@link File}to write to
345: */
346: public void writeUnsignedIntFile(File file, LongList list)
347: throws AxionException {
348: DataOutputStream out = null;
349: try {
350: out = createBufferedDOS(file);
351: for (int i = 0, I = list.size(); i < I; i++) {
352: out.writeInt((int) (list.get(i) & MAX_UNSIGNED_INT));
353: }
354: } catch (IOException e) {
355: throw new AxionException("Unable to write to " + file, e);
356: } finally {
357: closeOutputStream(out);
358: }
359: }
360:
361: public class PidxList {
362: private BufferedDataOutputStream _out = null;
363: private LongList _pidx = null;
364:
365: public PidxList(int count, File pidxFile, boolean readOnly)
366: throws AxionException {
367: openPidxFile(pidxFile, readOnly);
368: _pidx = new ArrayUnsignedIntList(count);
369: }
370:
371: public PidxList(LongList list, File pidxFile, boolean readOnly)
372: throws AxionException {
373: openPidxFile(pidxFile, readOnly);
374: _pidx = new ArrayUnsignedIntList(list);
375: }
376:
377: public void add(long dataFileOffset) {
378: try {
379: writeUnsignedInt(_out, this .size() * (4L),
380: (int) (dataFileOffset & MAX_UNSIGNED_INT));
381: _pidx.add(dataFileOffset);
382: } catch (AxionException e) {
383: throw ExceptionConverter.convertToRuntimeException(e);
384: }
385: }
386:
387: public void close() throws IOException {
388: _out.close();
389: _out = null;
390: _pidx = null;
391: }
392:
393: public void flush() throws IOException {
394: _out.flush();
395: }
396:
397: public long get(int index) {
398: return _pidx.get(index);
399: }
400:
401: public long set(int rowid, long dataFileOffset) {
402: try {
403: writeUnsignedInt(_out, rowid * (4L),
404: (int) (dataFileOffset & MAX_UNSIGNED_INT));
405: return _pidx.set(rowid, dataFileOffset);
406: } catch (AxionException e) {
407: throw ExceptionConverter.convertToRuntimeException(e);
408: }
409: }
410:
411: public int size() {
412: return _pidx.size();
413: }
414:
415: public void writeAll() throws AxionException {
416: try {
417: for (int i = 0, I = this .size(); i < I; i++) {
418: _out
419: .writeInt((int) (this .get(i) & MAX_UNSIGNED_INT));
420: }
421: } catch (IOException e) {
422: throw new AxionException(e);
423: }
424: }
425:
426: private void openPidxFile(File pidxFile, boolean readOnly)
427: throws AxionException {
428: if (!readOnly) {
429: _out = openBufferedDOSAppend(pidxFile, 512);
430: }
431: }
432: }
433:
434: // TODO: Experiment with ByteBuffer, that might improve performance.
435: // @see FileChannel.read(ByteBuffer src, long position)
436: class AxionFileInputStream extends AxionInputStream {
437:
438: FileInputStream _fis;
439:
440: public AxionFileInputStream(File f) throws IOException {
441: this ._fis = new FileInputStream(f);
442: }
443:
444: public int available() throws IOException {
445: return _fis.available();
446: }
447:
448: public void close() throws IOException {
449: _fis.close();
450: }
451:
452: public long getPos() throws IOException {
453: return _fis.getChannel().position();
454: }
455:
456: public int read() throws IOException {
457: return _fis.read();
458: }
459:
460: public int read(byte[] b) throws IOException {
461: return _fis.read(b);
462: }
463:
464: public int read(byte[] b, int off, int len) throws IOException {
465: return _fis.read(b, off, len);
466: }
467:
468: public void seek(long pos) throws IOException {
469: _fis.getChannel().position(pos);
470: }
471:
472: public long skip(long n) throws IOException {
473: return _fis.skip(n);
474: }
475: }
476:
477: // TODO: Experiment with ByteBuffer, that might improve performance.
478: // @see FileChannel.write(ByteBuffer src, long position)
479: class AxionFileOutputStream extends AxionOutputStream {
480: private RandomAccessFile _raf;
481:
482: public AxionFileOutputStream(File file) throws IOException {
483: _raf = new RandomAccessFile(file, "rw");
484: }
485:
486: public void close() throws IOException {
487: _raf.close();
488: }
489:
490: public void flush() throws IOException {
491: }
492:
493: public long getPos() throws IOException {
494: return _raf.getFilePointer();
495: }
496:
497: public void seek(long pos) throws IOException {
498: _raf.seek(pos);
499: }
500:
501: public void truncate(long length) throws IOException {
502: _raf.setLength(length);
503: }
504:
505: public void write(byte[] b) throws IOException {
506: _raf.write(b);
507: }
508:
509: public void write(byte[] b, int off, int len)
510: throws IOException {
511: _raf.write(b, off, len);
512: }
513:
514: public void write(int b) throws IOException {
515: _raf.write(b);
516: }
517: }
518:
519: private static final long MAX_UNSIGNED_INT = 0xFFFFFFFFL;
520:
521: }
|