001: /*
002: Copyright (C) 2007 Mobixess Inc. http://www.java-objects-database.com
003:
004: This file is part of the JODB (Java Objects Database) open source project.
005:
006: JODB is free software; you can redistribute it and/or modify it under
007: the terms of version 2 of the GNU General Public License as published
008: by the Free Software Foundation.
009:
010: JODB is distributed in the hope that it will be useful, but WITHOUT ANY
011: WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
013: for more details.
014:
015: You should have received a copy of the GNU General Public License along
016: with this program; if not, write to the Free Software Foundation, Inc.,
017: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: */
019: package com.mobixess.jodb.tests.io.buffers;
020:
021: import java.io.File;
022: import java.io.IOException;
023: import java.io.RandomAccessFile;
024: import java.nio.ByteBuffer;
025: import java.nio.channels.AsynchronousCloseException;
026: import java.nio.channels.ByteChannel;
027: import java.nio.channels.ClosedByInterruptException;
028: import java.nio.channels.ClosedChannelException;
029: import java.nio.channels.NonReadableChannelException;
030: import java.nio.channels.NonWritableChannelException;
031: import java.nio.channels.ReadableByteChannel;
032: import java.nio.channels.WritableByteChannel;
033: import java.util.Iterator;
034: import java.util.Map;
035: import java.util.WeakHashMap;
036:
037: import com.mobixess.jodb.core.io.IRandomAccessDataBuffer;
038:
039: public class RandomAccessTestTransactionBuffer implements
040: IRandomAccessDataBuffer {
041: private File _file;
042:
043: private RandomAccessFile _raFile;
044:
045: private ITransactionBufferListener _listener;
046:
047: private static int _count;
048:
049: private boolean _closed;
050:
051: public static WeakHashMap<RandomAccessTestTransactionBuffer, Throwable> _constructionKeepers = new WeakHashMap<RandomAccessTestTransactionBuffer, Throwable>();
052: private Throwable _constructionThrowable;
053:
054: public RandomAccessTestTransactionBuffer(File file,
055: ITransactionBufferListener listener, boolean write)
056: throws IOException {
057: super ();
058: _file = file;
059: _raFile = new RandomAccessFile(_file, "r" + (write ? "w" : ""));
060: _listener = listener;
061: Map<Thread, StackTraceElement[]> threadsMap = Thread
062: .getAllStackTraces();
063: Iterator<StackTraceElement[]> iterator = threadsMap.values()
064: .iterator();
065: _constructionThrowable = new RuntimeException("unclosed buffer");
066: Throwable additionalThrowable = null;
067: while (iterator.hasNext()) {
068: StackTraceElement[] stackTraceElements = iterator.next();
069: if (hasMethodCall(stackTraceElements,
070: "JODBIOBaseProxy.getIOTicket")) {
071: Throwable link = additionalThrowable;
072: additionalThrowable = new RuntimeException(
073: "Probable Trace");
074: additionalThrowable.setStackTrace(stackTraceElements);
075: if (link != null) {
076: additionalThrowable.initCause(link);
077: }
078: }
079: }
080: if (additionalThrowable != null) {
081: _constructionThrowable.initCause(additionalThrowable);
082: }
083: _constructionKeepers.put(this , _constructionThrowable);
084: _count++;
085:
086: //System.err.println("Count ="+_count);
087: }
088:
089: private boolean hasMethodCall(
090: StackTraceElement[] stackTraceElements, String callPatern) {
091: for (int i = 0; i < stackTraceElements.length; i++) {
092: String element = stackTraceElements[i].toString();
093: if (element.indexOf(callPatern) != -1) {
094: return true;
095: }
096: }
097: return false;
098: }
099:
100: public synchronized void close() throws IOException {
101: if (_closed) {
102: return;
103: }
104: _constructionKeepers.remove(this );
105: _closed = true;
106: _raFile.close();
107: _count--;
108: //System.err.println("Count ="+_count);
109: }
110:
111: public void delete() throws IOException {
112: close();
113: _file.delete();
114: }
115:
116: public ByteChannel getChannel() {
117: return new ByteChanelWrapper(_raFile.getChannel());
118: }
119:
120: public long getCursorOffset() throws IOException {
121: return _raFile.getFilePointer();
122: }
123:
124: public long length() throws IOException {
125: return _raFile.length();
126: }
127:
128: public void seek(long pos) throws IOException {
129: _raFile.seek(pos);
130: }
131:
132: public void setLength(long newLength) throws IOException {
133: _raFile.setLength(newLength);
134: }
135:
136: /**
137: * Transfers bytes into this channel's file from the given readable byte
138: * channel.
139: *
140: * <p>
141: * An attempt is made to read up to <tt>count</tt> bytes from the source
142: * channel and write them to this channel's file starting at the given
143: * <tt>position</tt>. An invocation of this method may or may not
144: * transfer all of the requested bytes; whether or not it does so depends
145: * upon the natures and states of the channels. Fewer than the requested
146: * number of bytes will be transferred if the source channel has fewer than
147: * <tt>count</tt> bytes remaining, or if the source channel is
148: * non-blocking and has fewer than <tt>count</tt> bytes immediately
149: * available in its input buffer.
150: *
151: * <p>
152: * This method does not modify this channel's position. If the given
153: * position is greater than the file's current size then no bytes are
154: * transferred. If the source channel has a position then bytes are read
155: * starting at that position and then the position is incremented by the
156: * number of bytes read.
157: *
158: * <p>
159: * This method is potentially much more efficient than a simple loop that
160: * reads from the source channel and writes to this channel. Many operating
161: * systems can transfer bytes directly from the source channel into the
162: * filesystem cache without actually copying them.
163: * </p>
164: *
165: * @param src
166: * The source channel
167: *
168: * @param position
169: * The position within the file at which the transfer is to
170: * begin; must be non-negative
171: *
172: * @param count
173: * The maximum number of bytes to be transferred; must be
174: * non-negative
175: *
176: * @return The number of bytes, possibly zero, that were actually
177: * transferred
178: *
179: * @throws IllegalArgumentException
180: * If the preconditions on the parameters do not hold
181: *
182: * @throws NonReadableChannelException
183: * If the source channel was not opened for reading
184: *
185: * @throws NonWritableChannelException
186: * If this channel was not opened for writing
187: *
188: * @throws ClosedChannelException
189: * If either this channel or the source channel is closed
190: *
191: * @throws AsynchronousCloseException
192: * If another thread closes either channel while the transfer is
193: * in progress
194: *
195: * @throws ClosedByInterruptException
196: * If another thread interrupts the current thread while the
197: * transfer is in progress, thereby closing both channels and
198: * setting the current thread's interrupt status
199: *
200: * @throws IOException
201: * If some other I/O error occurs
202: */
203: public long transferFrom(ReadableByteChannel src, long position,
204: long count) throws IOException {
205: _listener.writeStart(this , position);
206: long written = _raFile.getChannel().transferFrom(src, position,
207: count);
208: _listener.writeComplete(this , position, position + written);
209: return written;
210: }
211:
212: /**
213: * Transfers bytes from this channel's file to the given writable byte
214: * channel.
215: *
216: * <p>
217: * An attempt is made to read up to <tt>count</tt> bytes starting at the
218: * given <tt>position</tt> in this channel's file and write them to the
219: * target channel. An invocation of this method may or may not transfer all
220: * of the requested bytes; whether or not it does so depends upon the
221: * natures and states of the channels. Fewer than the requested number of
222: * bytes are transferred if this channel's file contains fewer than
223: * <tt>count</tt> bytes starting at the given <tt>position</tt>, or if
224: * the target channel is non-blocking and it has fewer than <tt>count</tt>
225: * bytes free in its output buffer.
226: *
227: * <p>
228: * This method does not modify this channel's position. If the given
229: * position is greater than the file's current size then no bytes are
230: * transferred. If the target channel has a position then bytes are written
231: * starting at that position and then the position is incremented by the
232: * number of bytes written.
233: *
234: * <p>
235: * This method is potentially much more efficient than a simple loop that
236: * reads from this channel and writes to the target channel. Many operating
237: * systems can transfer bytes directly from the filesystem cache to the
238: * target channel without actually copying them.
239: * </p>
240: *
241: * @param position
242: * The position within the file at which the transfer is to
243: * begin; must be non-negative
244: *
245: * @param count
246: * The maximum number of bytes to be transferred; must be
247: * non-negative
248: *
249: * @param target
250: * The target channel
251: *
252: * @return The number of bytes, possibly zero, that were actually
253: * transferred
254: *
255: * @throws IllegalArgumentException
256: * If the preconditions on the parameters do not hold
257: *
258: * @throws NonReadableChannelException
259: * If this channel was not opened for reading
260: *
261: * @throws NonWritableChannelException
262: * If the target channel was not opened for writing
263: *
264: * @throws ClosedChannelException
265: * If either this channel or the target channel is closed
266: *
267: * @throws AsynchronousCloseException
268: * If another thread closes either channel while the transfer is
269: * in progress
270: *
271: * @throws ClosedByInterruptException
272: * If another thread interrupts the current thread while the
273: * transfer is in progress, thereby closing both channels and
274: * setting the current thread's interrupt status
275: *
276: * @throws IOException
277: * If some other I/O error occurs
278: */
279: public long transferTo(long position, long count,
280: WritableByteChannel target) throws IOException {
281: _listener.readStart(this , position);
282: long read = _raFile.getChannel().transferTo(position, count,
283: target);
284: _listener.readComplete(this , position, position + read);
285: return read;
286: }
287:
288: public boolean readBoolean() throws IOException {
289: long start = getCursorOffset();
290: _listener.readStart(this , getCursorOffset());
291: try {
292: return _raFile.readBoolean();
293: } finally {
294: _listener.readComplete(this , start, getCursorOffset());
295: }
296: }
297:
298: public byte readByte() throws IOException {
299: long start = getCursorOffset();
300: _listener.readStart(this , getCursorOffset());
301: try {
302: return _raFile.readByte();
303: } finally {
304: _listener.readComplete(this , start, getCursorOffset());
305: }
306: }
307:
308: public char readChar() throws IOException {
309: long start = getCursorOffset();
310: _listener.readStart(this , getCursorOffset());
311: try {
312: return _raFile.readChar();
313: } finally {
314: _listener.readComplete(this , start, getCursorOffset());
315: }
316: }
317:
318: public double readDouble() throws IOException {
319: long start = getCursorOffset();
320: _listener.readStart(this , getCursorOffset());
321: try {
322: return _raFile.readDouble();
323: } finally {
324: _listener.readComplete(this , start, getCursorOffset());
325: }
326: }
327:
328: public float readFloat() throws IOException {
329: long start = getCursorOffset();
330: _listener.readStart(this , getCursorOffset());
331: try {
332: return _raFile.readFloat();
333: } finally {
334: _listener.readComplete(this , start, getCursorOffset());
335: }
336: }
337:
338: public void readFully(byte[] b) throws IOException {
339: long start = getCursorOffset();
340: _listener.readStart(this , getCursorOffset());
341: try {
342: _raFile.readFully(b);
343: } finally {
344: _listener.readComplete(this , start, getCursorOffset());
345: }
346: }
347:
348: public void readFully(byte[] b, int off, int len)
349: throws IOException {
350: long start = getCursorOffset();
351: _listener.readStart(this , getCursorOffset());
352: try {
353: _raFile.read(b, off, len);
354: } finally {
355: _listener.readComplete(this , start, getCursorOffset());
356: }
357: }
358:
359: public int readInt() throws IOException {
360: long start = getCursorOffset();
361: _listener.readStart(this , getCursorOffset());
362: try {
363: return _raFile.readInt();
364: } finally {
365: _listener.readComplete(this , start, getCursorOffset());
366: }
367: }
368:
369: public String readLine() throws IOException {
370: long start = getCursorOffset();
371: _listener.readStart(this , getCursorOffset());
372: try {
373: return _raFile.readLine();
374: } finally {
375: _listener.readComplete(this , start, getCursorOffset());
376: }
377: }
378:
379: public long readLong() throws IOException {
380: long start = getCursorOffset();
381: _listener.readStart(this , getCursorOffset());
382: try {
383: return _raFile.readLong();
384: } finally {
385: _listener.readComplete(this , start, getCursorOffset());
386: }
387: }
388:
389: public short readShort() throws IOException {
390: long start = getCursorOffset();
391: _listener.readStart(this , getCursorOffset());
392: try {
393: return _raFile.readShort();
394: } finally {
395: _listener.readComplete(this , start, getCursorOffset());
396: }
397: }
398:
399: public String readUTF() throws IOException {
400: long start = getCursorOffset();
401: _listener.readStart(this , getCursorOffset());
402: try {
403: return _raFile.readUTF();
404: } finally {
405: _listener.readComplete(this , start, getCursorOffset());
406: }
407: }
408:
409: public int readUnsignedByte() throws IOException {
410: long start = getCursorOffset();
411: _listener.readStart(this , getCursorOffset());
412: try {
413: return _raFile.readUnsignedByte();
414: } finally {
415: _listener.readComplete(this , start, getCursorOffset());
416: }
417: }
418:
419: public int readUnsignedShort() throws IOException {
420: long start = getCursorOffset();
421: _listener.readStart(this , getCursorOffset());
422: try {
423: return _raFile.readUnsignedShort();
424: } finally {
425: _listener.readComplete(this , start, getCursorOffset());
426: }
427: }
428:
429: public int skipBytes(int n) throws IOException {
430: return _raFile.skipBytes(n);
431: }
432:
433: public void write(int b) throws IOException {
434: long start = getCursorOffset();
435: _listener.writeStart(this , getCursorOffset());
436: try {
437: _raFile.write(b);
438: } finally {
439: _listener.writeComplete(this , start, getCursorOffset());
440: }
441: }
442:
443: public void write(byte[] b) throws IOException {
444: long start = getCursorOffset();
445: _listener.writeStart(this , getCursorOffset());
446: try {
447: _raFile.write(b);
448: } finally {
449: _listener.writeComplete(this , start, getCursorOffset());
450: }
451: }
452:
453: public void write(byte[] b, int off, int len) throws IOException {
454: long start = getCursorOffset();
455: _listener.writeStart(this , getCursorOffset());
456: try {
457: _raFile.write(b, off, len);
458: } finally {
459: _listener.writeComplete(this , start, getCursorOffset());
460: }
461: }
462:
463: public void writeBoolean(boolean v) throws IOException {
464: long start = getCursorOffset();
465: _listener.writeStart(this , getCursorOffset());
466: try {
467: _raFile.writeBoolean(v);
468: } finally {
469: _listener.writeComplete(this , start, getCursorOffset());
470: }
471: }
472:
473: public void writeByte(int v) throws IOException {
474: long start = getCursorOffset();
475: _listener.writeStart(this , getCursorOffset());
476: try {
477: _raFile.writeByte(v);
478: } finally {
479: _listener.writeComplete(this , start, getCursorOffset());
480: }
481: }
482:
483: public void writeBytes(String s) throws IOException {
484: long start = getCursorOffset();
485: _listener.writeStart(this , getCursorOffset());
486: try {
487: _raFile.writeBytes(s);
488: } finally {
489: _listener.writeComplete(this , start, getCursorOffset());
490: }
491: }
492:
493: public void writeChar(int v) throws IOException {
494: long start = getCursorOffset();
495: _listener.writeStart(this , getCursorOffset());
496: try {
497: _raFile.writeChar(v);
498: } finally {
499: _listener.writeComplete(this , start, getCursorOffset());
500: }
501: }
502:
503: public void writeChars(String s) throws IOException {
504: long start = getCursorOffset();
505: _listener.writeStart(this , getCursorOffset());
506: try {
507: _raFile.writeChars(s);
508: } finally {
509: _listener.writeComplete(this , start, getCursorOffset());
510: }
511: }
512:
513: public void writeDouble(double v) throws IOException {
514: long start = getCursorOffset();
515: _listener.writeStart(this , getCursorOffset());
516: try {
517: _raFile.writeDouble(v);
518: } finally {
519: _listener.writeComplete(this , start, getCursorOffset());
520: }
521: }
522:
523: public void writeFloat(float v) throws IOException {
524: long start = getCursorOffset();
525: _listener.writeStart(this , getCursorOffset());
526: try {
527: _raFile.writeFloat(v);
528: } finally {
529: _listener.writeComplete(this , start, getCursorOffset());
530: }
531: }
532:
533: public void writeInt(int v) throws IOException {
534: long start = getCursorOffset();
535: _listener.writeStart(this , getCursorOffset());
536: try {
537: _raFile.writeInt(v);
538: } finally {
539: _listener.writeComplete(this , start, getCursorOffset());
540: }
541: }
542:
543: public void writeLong(long v) throws IOException {
544: long start = getCursorOffset();
545: _listener.writeStart(this , getCursorOffset());
546: try {
547: _raFile.writeLong(v);
548: } finally {
549: _listener.writeComplete(this , start, getCursorOffset());
550: }
551: }
552:
553: public void writeShort(int v) throws IOException {
554: long start = getCursorOffset();
555: _listener.writeStart(this , getCursorOffset());
556: try {
557: _raFile.writeShort(v);
558: } finally {
559: _listener.writeComplete(this , start, getCursorOffset());
560: }
561: }
562:
563: public void writeUTF(String str) throws IOException {
564: long start = getCursorOffset();
565: _listener.writeStart(this , getCursorOffset());
566: try {
567: _raFile.writeUTF(str);
568: } finally {
569: _listener.writeComplete(this , start, getCursorOffset());
570: }
571: }
572:
573: public void resetToStart() throws IOException {
574: _raFile.seek(0);
575: }
576:
577: public void read(byte[] b, int off, int len) throws IOException {
578: long start = getCursorOffset();
579: _listener.readStart(this , getCursorOffset());
580: try {
581: _raFile.read(b, off, len);
582: } finally {
583: _listener.readComplete(this , start, getCursorOffset());
584: }
585: }
586:
587: public void resetToEnd() throws IOException {
588: _raFile.seek(_raFile.length());
589: }
590:
591: public long skip(long n) throws IOException {
592: long totalSkiped = 0;
593: while (n > 0) {
594: int skiped = _raFile.skipBytes((int) Math.min(n,
595: Integer.MAX_VALUE));
596: if (skiped == 0) {
597: return totalSkiped;
598: }
599: totalSkiped += skiped;
600: n -= skiped;
601: }
602: return 0;
603: }
604:
605: @Override
606: public String toString() {
607: try {
608: if (_closed) {
609: return "buffer " + _file + " closed=" + _closed;
610: }
611: return "buffer length=" + length() + " pointer="
612: + getCursorOffset() + " " + _file + " closed="
613: + _closed;
614: } catch (IOException e) {
615: return super .toString();
616: }
617: }
618:
619: public boolean isClosed() {
620: return _closed;
621: }
622:
623: public byte[] getAsByteArray() throws IOException {
624: byte[] result = new byte[(int) _raFile.length()];
625: ByteBuffer buffer = ByteBuffer.wrap(result);
626: buffer.put((byte) 0);
627: _raFile.getChannel().read(buffer, 1);
628: return result;
629: }
630:
631: public static int getOpenBuffersCount() {
632: return _count;
633: }
634:
635: @Override
636: protected void finalize() throws Throwable {
637: // if(_parent!=null){
638: // _parent.close();
639: // }
640: if (!_closed) {
641: RuntimeException exception = new RuntimeException(
642: "finalize of unclosed buffer ");
643: exception.initCause(_constructionThrowable);
644: exception.printStackTrace();
645: _listener.reportError(exception);
646: }
647: }
648:
649: private class ByteChanelWrapper implements ByteChannel {
650:
651: private ByteChannel _chanel;
652:
653: /**
654: * @param chanel
655: */
656: public ByteChanelWrapper(ByteChannel chanel) {
657: super ();
658: _chanel = chanel;
659: }
660:
661: public int read(ByteBuffer dst) throws IOException {
662: long start = getCursorOffset();
663: _listener.readStart(RandomAccessTestTransactionBuffer.this ,
664: getCursorOffset());
665: try {
666: return _chanel.read(dst);
667: } finally {
668: _listener.readComplete(
669: RandomAccessTestTransactionBuffer.this , start,
670: getCursorOffset());
671: }
672: }
673:
674: public void close() throws IOException {
675: _chanel.close();
676: }
677:
678: public boolean isOpen() {
679: return _chanel.isOpen();
680: }
681:
682: public int write(ByteBuffer src) throws IOException {
683: long start = getCursorOffset();
684: _listener.writeStart(
685: RandomAccessTestTransactionBuffer.this ,
686: getCursorOffset());
687: try {
688: return _chanel.write(src);
689: } finally {
690: _listener.writeComplete(
691: RandomAccessTestTransactionBuffer.this , start,
692: getCursorOffset());
693: }
694: }
695:
696: }
697:
698: public void prefetch(long offset) throws IOException {
699: }
700: }
|