001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.nio.internal;
019:
020: import java.io.Closeable;
021: import java.io.FileDescriptor;
022: import java.io.IOException;
023: import java.nio.ByteBuffer;
024: import java.nio.MappedByteBuffer;
025: import java.nio.channels.ClosedChannelException;
026: import java.nio.channels.FileChannel;
027: import java.nio.channels.FileLock;
028: import java.nio.channels.NonWritableChannelException;
029: import java.nio.channels.ReadableByteChannel;
030: import java.nio.channels.WritableByteChannel;
031:
032: import org.apache.harmony.luni.platform.IFileSystem;
033: import org.apache.harmony.luni.platform.Platform;
034: import org.apache.harmony.luni.platform.PlatformAddress;
035: import org.apache.harmony.luni.platform.PlatformAddressFactory;
036: import org.apache.harmony.nio.internal.nls.Messages;
037:
038: /*
039: * The file channel impl class is the bridge between the logical channels
040: * described by the NIO channel framework, and the file system implementation
041: * provided by the port layer.
042: *
043: * This class is non-API, but implements the API of the FileChannel interface.
044: */
045: public abstract class FileChannelImpl extends FileChannel {
046:
047: // Reference to the portable file system code.
048: private static final IFileSystem fileSystem = Platform
049: .getFileSystem();
050:
051: private static final int ALLOC_GRANULARITY;
052:
053: static {
054: try {
055: ALLOC_GRANULARITY = fileSystem.getAllocGranularity();
056: } catch (IOException e) {
057: throw new Error(e);
058: }
059:
060: }
061:
062: // Handle to the open file
063: private final long handle;
064:
065: // The object that will track all outstanding locks on this channel.
066: private final LockManager lockManager = new LockManager();
067:
068: private static class RepositioningLock {
069: }
070:
071: private final Object repositioningLock = new RepositioningLock();
072:
073: private final Object stream;
074:
075: /*
076: * Create a new file channel implementation class that wraps the given file
077: * handle and operates in the specified mode.
078: *
079: */
080: public FileChannelImpl(Object stream, long handle) {
081: super ();
082: this .handle = handle;
083: this .stream = stream;
084: }
085:
086: /*
087: * Helper method to throw an exception if the channel is already closed.
088: * Note that we don't bother to synchronize on this test since the file may
089: * be closed by operations beyond our control anyways.
090: */
091: protected final void openCheck() throws ClosedChannelException {
092: if (!isOpen()) {
093: throw new ClosedChannelException();
094: }
095: }
096:
097: /*
098: * (non-Javadoc)
099: *
100: * @see java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel()
101: */
102: protected void implCloseChannel() throws IOException {
103: if (stream instanceof Closeable) {
104: ((Closeable) stream).close();
105: }
106: }
107:
108: protected FileLock basicLock(long position, long size,
109: boolean shared, boolean wait) throws IOException {
110: if ((position < 0) || (size < 0)) {
111: // nio.0A=Lock position and size must be non-negative.
112: throw new IllegalArgumentException(Messages
113: .getString("nio.0A")); //$NON-NLS-1$
114: }
115: int lockType = shared ? IFileSystem.SHARED_LOCK_TYPE
116: : IFileSystem.EXCLUSIVE_LOCK_TYPE;
117: FileLock pendingLock = new FileLockImpl(this , position, size,
118: shared);
119: lockManager.addLock(pendingLock);
120:
121: if (fileSystem.lock(handle, position, size, lockType, wait)) {
122: return pendingLock;
123: }
124:
125: // Lock acquisition failed
126: lockManager.removeLock(pendingLock);
127: return null;
128: }
129:
130: /*
131: * Acquire a lock on the receiver, blocks if the lock cannot be obtained
132: * immediately.
133: *
134: * @see java.nio.channels.FileChannel#lock(long, long, boolean)
135: */
136: public final FileLock lock(long position, long size, boolean shared)
137: throws IOException {
138: openCheck();
139: FileLock resultLock = null;
140: {
141: boolean completed = false;
142: try {
143: begin();
144: resultLock = basicLock(position, size, shared, true);
145: completed = true;
146: } finally {
147: end(completed);
148: }
149: }
150: return resultLock;
151: }
152:
153: /*
154: * Attempts to acquire the given lock, but does not block. If the lock
155: * cannot be acquired the method returns null.
156: *
157: * @see java.nio.channels.FileChannel#tryLock(long, long, boolean)
158: */
159: public final FileLock tryLock(long position, long size,
160: boolean shared) throws IOException {
161: openCheck();
162: return basicLock(position, size, shared, false);
163: }
164:
165: /*
166: * Non-API method to release a given lock on a file channel. Assumes that
167: * the lock will mark itself invalid after successful unlocking.
168: */
169: void release(FileLock lock) throws IOException {
170: openCheck();
171: fileSystem.unlock(handle, lock.position(), lock.size());
172: lockManager.removeLock(lock);
173: }
174:
175: /*
176: * Flush the contents of the file to disk, and the metadata if asked.
177: */
178: public void force(boolean metadata) throws IOException {
179: openCheck();
180: // Forcing data-only on a read-only file is a no-op.
181: if (metadata) {
182: fileSystem.fflush(handle, metadata);
183: }
184: }
185:
186: public abstract MappedByteBuffer map(MapMode mode, long position,
187: long size) throws IOException;
188:
189: protected final MappedByteBuffer mapImpl(int mapMode,
190: long position, long size) throws IOException {
191: if (position + size > size()) {
192: fileSystem.truncate(handle, position + size);
193: }
194: long alignment = position - position % ALLOC_GRANULARITY;
195: int offset = (int) (position - alignment);
196: PlatformAddress address = PlatformAddressFactory.allocMap(
197: handle, alignment, size + offset, mapMode);
198: MappedByteBuffer buffer = null;
199: try {
200: buffer = MappedByteBufferFactory.getBuffer(address,
201: mapMode, size, offset);
202: } catch (Exception e) {
203: throw new IOException(e.getMessage());
204: }
205: return buffer;
206: }
207:
208: /*
209: * Answers the current file position.
210: */
211: public long position() throws IOException {
212: openCheck();
213: return fileSystem.seek(handle, 0L, IFileSystem.SEEK_CUR);
214: }
215:
216: /*
217: * Sets the file pointer.
218: */
219: public FileChannel position(long newPosition) throws IOException {
220: openCheck();
221: if (newPosition < 0) {
222: // nio.0B=New position must be non-negative.
223: throw new IllegalArgumentException(Messages
224: .getString("nio.0B")); //$NON-NLS-1$
225: }
226:
227: synchronized (repositioningLock) {
228: fileSystem.seek(handle, newPosition, IFileSystem.SEEK_SET);
229: }
230: return this ;
231: }
232:
233: public int read(ByteBuffer buffer, long position)
234: throws IOException {
235: if (null == buffer) {
236: throw new NullPointerException();
237: }
238: if (position < 0) {
239: throw new IllegalArgumentException();
240: }
241: openCheck();
242: if (!buffer.hasRemaining()) {
243: return 0;
244: }
245: synchronized (repositioningLock) {
246: int bytesRead = 0;
247: long preReadPosition = position();
248: position(position);
249: try {
250: bytesRead = read(buffer);
251: } finally {
252: position(preReadPosition);
253: }
254: return bytesRead;
255: }
256: }
257:
258: public int read(ByteBuffer buffer) throws IOException {
259: openCheck();
260: if (!buffer.hasRemaining()) {
261: return 0;
262: }
263: boolean completed = false;
264: int bytesRead = 0;
265: synchronized (repositioningLock) {
266: if (buffer.isDirect()) {
267: DirectBuffer directBuffer = (DirectBuffer) buffer;
268: long address = directBuffer.getEffectiveAddress()
269: .toLong();
270: try {
271: begin();
272: /*
273: * if (bytesRead <= EOF) dealt by read completed = false;
274: */
275: bytesRead = (int) fileSystem.readDirect(handle,
276: address, buffer.position(), buffer
277: .remaining());
278: completed = true;
279: } finally {
280: end(completed && bytesRead >= 0);
281: }
282: } else {
283: try {
284: begin();
285: /*
286: * if (bytesRead <= EOF) dealt by read completed = false;
287: */
288: bytesRead = (int) fileSystem.read(handle, buffer
289: .array(), buffer.arrayOffset()
290: + buffer.position(), buffer.remaining());
291: completed = true;
292: } finally {
293: end(completed && bytesRead >= 0);
294: }
295: }
296: if (bytesRead > 0) {
297: buffer.position(buffer.position() + bytesRead);
298: }
299: }
300: return bytesRead;
301: }
302:
303: public long read(ByteBuffer[] buffers, int offset, int length)
304: throws IOException {
305: int count = 0;
306: if (offset < 0 || length < 0
307: || offset + length > buffers.length) {
308: throw new IndexOutOfBoundsException();
309: }
310: openCheck();
311: for (int i = offset; i < offset + length; i++) {
312: count += buffers[i].remaining();
313: }
314: if (0 == count) {
315: return 0;
316: }
317: if (size() == 0) {
318: return -1;
319: }
320: ByteBuffer[] directBuffers = new ByteBuffer[length];
321: long[] handles = new long[length];
322: int[] offsets = new int[length];
323: int[] lengths = new int[length];
324: for (int i = 0; i < length; i++) {
325: ByteBuffer buffer = buffers[i + offset];
326: if (!buffer.isDirect()) {
327: buffer = ByteBuffer.allocateDirect(buffer.remaining());
328: directBuffers[i] = buffer;
329: offsets[i] = 0;
330: } else {
331: offsets[i] = buffer.position();
332: }
333: handles[i] = ((DirectBuffer) buffer).getEffectiveAddress()
334: .toLong();
335: lengths[i] = buffer.remaining();
336: }
337: long bytesRead = 0;
338: {
339: boolean completed = false;
340: try {
341: begin();
342: synchronized (repositioningLock) {
343: bytesRead = fileSystem.readv(handle, handles,
344: offsets, lengths, length);
345:
346: }
347: completed = true;
348: /*
349: * if (bytesRead < EOF) //dealt by readv? completed = false;
350: */
351: } finally {
352: end(completed);
353: }
354: }
355: int end = offset + length;
356: long bytesRemaining = bytesRead;
357: for (int i = offset; i < end && bytesRemaining > 0; i++) {
358: if (buffers[i].isDirect()) {
359: if (lengths[i] < bytesRemaining) {
360: int pos = buffers[i].limit();
361: buffers[i].position(pos);
362: bytesRemaining -= lengths[i];
363: } else {
364: int pos = (int) bytesRemaining;
365: buffers[i].position(pos);
366: break;
367: }
368: } else {
369: ByteBuffer buf = directBuffers[i - offset];
370: if (bytesRemaining < buf.remaining()) {
371: // this is the last step.
372: int pos = buf.position();
373: buffers[i].put(buf);
374: buffers[i].position(pos + (int) bytesRemaining);
375: bytesRemaining = 0;
376: } else {
377: bytesRemaining -= buf.remaining();
378: buffers[i].put(buf);
379: }
380: }
381: }
382: return bytesRead;
383: }
384:
385: /*
386: * Answers the current file size, as an integer number of bytes.
387: */
388: public long size() throws IOException {
389: openCheck();
390: synchronized (repositioningLock) {
391: long currentPosition = fileSystem.seek(handle, 0L,
392: IFileSystem.SEEK_CUR);
393: long endOfFilePosition = fileSystem.seek(handle, 0L,
394: IFileSystem.SEEK_END);
395: fileSystem.seek(handle, currentPosition,
396: IFileSystem.SEEK_SET);
397: return endOfFilePosition;
398: }
399: }
400:
401: public long transferFrom(ReadableByteChannel src, long position,
402: long count) throws IOException {
403: openCheck();
404: if (!src.isOpen()) {
405: throw new ClosedChannelException();
406: }
407: if (position < 0 || count < 0 || position > Integer.MAX_VALUE
408: || count > Integer.MAX_VALUE) {
409: throw new IllegalArgumentException();
410: }
411: if (position > size()) {
412: return 0;
413: }
414:
415: ByteBuffer buffer = null;
416:
417: try {
418: if (src instanceof FileChannel) {
419: FileChannel fileSrc = (FileChannel) src;
420: long size = fileSrc.size();
421: long filePosition = fileSrc.position();
422: count = Math.min(count, size - filePosition);
423: buffer = fileSrc.map(MapMode.READ_ONLY, filePosition,
424: count);
425: fileSrc.position(filePosition + count);
426: } else {
427: buffer = ByteBuffer.allocateDirect((int) count);
428: src.read(buffer);
429: buffer.flip();
430: }
431: return write(buffer, position);
432: } finally {
433: // unmap the buffer
434: if (buffer != null) {
435: // all children of FileChannelImpl currently returns
436: // an instance of DirectBuffer from map() method
437: ((DirectBuffer) buffer).free();
438: }
439: }
440: }
441:
442: public long transferTo(long position, long count,
443: WritableByteChannel target) throws IOException {
444: openCheck();
445: if (!target.isOpen()) {
446: throw new ClosedChannelException();
447: }
448: if (target instanceof ReadOnlyFileChannel) {
449: throw new NonWritableChannelException();
450: }
451: if (position < 0 || count < 0 || position > Integer.MAX_VALUE
452: || count > Integer.MAX_VALUE) {
453: throw new IllegalArgumentException();
454: }
455:
456: if (count == 0 || position >= size()) {
457: return 0;
458: }
459: ByteBuffer buffer = null;
460: count = Math.min(count, size() - position);
461: if (target instanceof SocketChannelImpl) {
462: // only socket can be transfered by system call
463: return kernelTransfer(handle, ((SocketChannelImpl) target)
464: .getFD(), position, count);
465: }
466:
467: try {
468: buffer = map(MapMode.READ_ONLY, position, count);
469: return target.write(buffer);
470: } finally {
471: // unmap the buffer
472: if (buffer != null) {
473: // all children of FileChannelImpl currently returns
474: // an instance of DirectBuffer from map() method
475: ((DirectBuffer) buffer).free();
476: }
477: }
478: }
479:
480: private long kernelTransfer(long l, FileDescriptor fd,
481: long position, long count) throws IOException {
482: boolean completed = false;
483: try {
484: begin();
485: long ret = fileSystem.transfer(l, fd, position, count);
486: completed = true;
487: return ret;
488: } finally {
489: end(completed);
490: }
491: }
492:
493: public FileChannel truncate(long size) throws IOException {
494: openCheck();
495: if (size < 0) {
496: throw new IllegalArgumentException();
497: }
498: if (size < size()) {
499: synchronized (repositioningLock) {
500: long position = position();
501: fileSystem.truncate(handle, size);
502: /*
503: * FIXME: currently the port library always modifies the
504: * position to given size. not sure it is a bug or intended
505: * behaviour, so I always reset the position to proper value as
506: * Java Spec.
507: */
508: position(position > size ? size : position);
509: }
510: }
511: return this ;
512: }
513:
514: /*
515: * (non-Javadoc)
516: *
517: * @see java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
518: */
519:
520: public int write(ByteBuffer buffer, long position)
521: throws IOException {
522: if (null == buffer) {
523: throw new NullPointerException();
524: }
525: if (position < 0) {
526: throw new IllegalArgumentException();
527: }
528: openCheck();
529: if (!buffer.hasRemaining()) {
530: return 0;
531: }
532: int bytesWritten = 0;
533: synchronized (repositioningLock) {
534: long preWritePosition = position();
535: position(position);
536: try {
537: bytesWritten = writeImpl(buffer);
538: } finally {
539: position(preWritePosition);
540: }
541: }
542: return bytesWritten;
543: }
544:
545: public int write(ByteBuffer buffer) throws IOException {
546: openCheck();
547: return writeImpl(buffer);
548: }
549:
550: private int writeImpl(ByteBuffer buffer) throws IOException {
551: int bytesWritten;
552: boolean completed = false;
553: synchronized (repositioningLock) {
554: if (buffer.isDirect()) {
555: DirectBuffer directBuffer = (DirectBuffer) buffer;
556: long address = directBuffer.getEffectiveAddress()
557: .toLong();
558: try {
559: begin();
560: bytesWritten = (int) fileSystem.writeDirect(handle,
561: address, buffer.position(), buffer
562: .remaining());
563: completed = true;
564: } finally {
565: end(completed);
566: }
567: } else {
568: try {
569: begin();
570: bytesWritten = (int) fileSystem.write(handle,
571: buffer.array(), buffer.arrayOffset()
572: + buffer.position(), buffer
573: .remaining());
574: completed = true;
575: } finally {
576: end(completed);
577: }
578: }
579: if (bytesWritten > 0) {
580: buffer.position(buffer.position() + bytesWritten);
581: }
582: }
583: return bytesWritten;
584: }
585:
586: public long write(ByteBuffer[] buffers, int offset, int length)
587: throws IOException {
588: if (offset < 0 || length < 0
589: || (offset + length) > buffers.length) {
590: throw new IndexOutOfBoundsException();
591: }
592: openCheck();
593: int count = 0;
594: for (int i = offset; i < offset + length; i++) {
595: count += buffers[i].remaining();
596: }
597: if (0 == count) {
598: return 0;
599: }
600: long[] handles = new long[length];
601: int[] offsets = new int[length];
602: int[] lengths = new int[length];
603: ByteBuffer[] bufferRefs = new ByteBuffer[length];
604: for (int i = 0; i < length; i++) {
605: ByteBuffer buffer = buffers[i + offset];
606: if (!buffer.isDirect()) {
607: ByteBuffer directBuffer = ByteBuffer
608: .allocateDirect(buffer.remaining());
609: directBuffer.put(buffer);
610: directBuffer.flip();
611: buffer = directBuffer;
612: offsets[i] = 0;
613: } else {
614: offsets[i] = buffer.position();
615: }
616: handles[i] = ((DirectBuffer) buffer).getEffectiveAddress()
617: .toLong();
618: lengths[i] = buffer.remaining();
619: bufferRefs[i] = buffer;
620: }
621:
622: long bytesWritten = 0;
623: boolean completed = false;
624: synchronized (repositioningLock) {
625: try {
626: begin();
627: bytesWritten = fileSystem.writev(handle, handles,
628: offsets, lengths, length);
629: completed = true;
630: } finally {
631: end(completed);
632: }
633: }
634:
635: long bytesRemaining = bytesWritten;
636: for (int i = offset; i < length + offset; i++) {
637: if (bytesRemaining > buffers[i].remaining()) {
638: int pos = buffers[i].limit();
639: buffers[i].position(pos);
640: bytesRemaining -= buffers[i].remaining();
641: } else {
642: int pos = buffers[i].position() + (int) bytesRemaining;
643: buffers[i].position(pos);
644: break;
645: }
646: }
647: return bytesWritten;
648: }
649:
650: public long getHandle() {
651: return handle;
652: }
653: }
|