Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   ***** BEGIN LICENSE BLOCK *****
   * Version: CPL 1.0/GPL 2.0/LGPL 2.1
   *
   * The contents of this file are subject to the Common Public
   * License Version 1.0 (the "License"); you may not use this file
   * except in compliance with the License. You may obtain a copy of
   * the License at http://www.eclipse.org/legal/cpl-v10.html
   *
  * Software distributed under the License is distributed on an "AS
  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  * implied. See the License for the specific language governing
  * rights and limitations under the License.
  *
  * Copyright (C) 2008 Charles O Nutter <headius@headius.com>
  * 
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the CPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the CPL, the GPL or the LGPL.
  ***** END LICENSE BLOCK *****/
 package org.jruby.util.io;
 
 import static org.jruby.util.io.ModeFlags.RDONLY;
 import static org.jruby.util.io.ModeFlags.RDWR;
 import static org.jruby.util.io.ModeFlags.WRONLY;
 
 import java.io.File;
 import java.net.URL;
 import java.util.Map;
 
ChannelDescriptor provides an abstraction similar to the concept of a "file descriptor" on any POSIX system. In our case, it's a numbered object (fileno) enclosing a Channel (@see java.nio.channels.Channel), FileDescriptor (@see java.io.FileDescriptor), and flags under which the original open occured (@see org.jruby.util.io.ModeFlags). Several operations you would normally expect to use with a POSIX file descriptor are implemented here and used by higher-level classes to implement higher-level IO behavior. Note that the channel specified when constructing a ChannelDescriptor will be reference-counted; that is, until all known references to it through this class have gone away, it will be left open. This is to support operations like "dup" which must produce two independent ChannelDescriptor instances that can be closed separately without affecting the other. At present there's no way to simulate the behavior on some platforms where POSIX dup also allows independent positioning information.
 
 public class ChannelDescriptor {
 
     private static final Logger LOG = LoggerFactory.getLogger("ChannelDescriptor");

    
Whether to log debugging information
 
     private static final boolean DEBUG = false;
    
    
The java.nio.channels.Channel this descriptor wraps.
 
     private Channel channel;
    
The file number (equivalent to the int file descriptor value in POSIX) for this descriptor. This is generated new for most ChannelDescriptor instances, except when they need to masquerade as another fileno.
 
     private int internalFileno;
    
The java.io.FileDescriptor object for this descriptor.
 
     private FileDescriptor fileDescriptor;
    
The original org.jruby.util.io.ModeFlags with which the specified channel was opened.
    private ModeFlags originalModes;
    
The reference count for the provided channel. Only counts references through ChannelDescriptor instances.
    private AtomicInteger refCounter;

    
Used to work-around blocking problems with STDIN. In most cases null. See ChannelDescriptor(java.io.InputStream,org.jruby.util.io.ModeFlags,java.io.FileDescriptor) for more details. You probably should not use it.
    private InputStream baseInputStream;
    
    
Process streams get Channel.newChannel()ed into FileChannel but are not actually seekable. So instead of just the isSeekable check doing instanceof FileChannel, we must also add this boolean to check, which we set to false when it's known that the incoming channel is from a process. FIXME: This is gross, and it's NIO's fault for not providing a nice way to tell if a channel is "really" seekable.
    private boolean canBeSeekable = true;
    
    
If the incoming channel is already in append mode (i.e. it will do the requisite seeking), we don't want to do our own additional seeks.
    private boolean isInAppendMode = false;
    
    
Whether the current channe is writable or not.
    private boolean readableChannel;
    
    
Whether the current channel is readable or not.
    private boolean writableChannel;
    
    
Whether the current channel is seekable or not.
    private boolean seekableChannel;
    
    
Construct a new ChannelDescriptor with the specified channel, file number, mode flags, file descriptor object and reference counter. This constructor is only used when constructing a new copy of an existing ChannelDescriptor with an existing reference count, to allow the two instances to safely share and appropriately close a given channel.

Parameters:
channel The channel for the new descriptor, which will be shared with another
fileno The new file number for the new descriptor
originalModes The mode flags to use as the "origina" set for this descriptor
fileDescriptor The java.io.FileDescriptor object to associate with this ChannelDescriptor
refCounter The reference counter from another ChannelDescriptor being duped.
canBeSeekable If the underlying channel can be considered seekable.
isInAppendMode If the underlying channel is already in append mode.
    private ChannelDescriptor(Channel channelint filenoModeFlags originalModesFileDescriptor fileDescriptorAtomicInteger refCounterboolean canBeSeekableboolean isInAppendMode) {
        this. = refCounter;
        this. = channel;
        this. = fileno;
        this. = originalModes;
        this. = fileDescriptor;
        this. = canBeSeekable;
        this. = isInAppendMode;
        
        this. = channel instanceof ReadableByteChannel;
        this. = channel instanceof WritableByteChannel;
        this. = channel instanceof FileChannel;
        registerDescriptor(this);
    }
    private ChannelDescriptor(Channel channelint filenoModeFlags originalModesFileDescriptor fileDescriptor) {
        this(channelfilenooriginalModesfileDescriptornew AtomicInteger(1), truefalse);
    }

    
Construct a new ChannelDescriptor with the given channel, file number, mode flags, and file descriptor object. The channel will be kept open until all ChannelDescriptor references to it have been closed.

Parameters:
channel The channel for the new descriptor
originalModes The mode flags for the new descriptor
fileDescriptor The java.io.FileDescriptor object for the new descriptor
    public ChannelDescriptor(Channel channelModeFlags originalModesFileDescriptor fileDescriptor) {
        this(channelgetNewFileno(), originalModesfileDescriptornew AtomicInteger(1), truefalse);
    }

    
Construct a new ChannelDescriptor with the given channel, file number, mode flags, and file descriptor object. The channel will be kept open until all ChannelDescriptor references to it have been closed.

Parameters:
channel The channel for the new descriptor
originalModes The mode flags for the new descriptor
fileDescriptor The java.io.FileDescriptor object for the new descriptor
    public ChannelDescriptor(Channel channelModeFlags originalModesFileDescriptor fileDescriptorboolean isInAppendMode) {
        this(channelgetNewFileno(), originalModesfileDescriptornew AtomicInteger(1), trueisInAppendMode);
    }

    
Construct a new ChannelDescriptor with the given channel, file number, mode flags, and file descriptor object. The channel will be kept open until all ChannelDescriptor references to it have been closed.

Parameters:
channel The channel for the new descriptor
originalModes The mode flags for the new descriptor
    public ChannelDescriptor(Channel channelModeFlags originalModes) {
        this(channelgetNewFileno(), originalModesnew FileDescriptor(), new AtomicInteger(1), truefalse);
    }

    
Special constructor to create the ChannelDescriptor out of the stream, file number, mode flags, and file descriptor object. The channel will be created from the provided stream. The channel will be kept open until all ChannelDescriptor references to it have been closed. Note: in most cases, you should not use this constructor, it's reserved mostly for STDIN.

Parameters:
baseInputStream The stream to create the channel for the new descriptor
originalModes The mode flags for the new descriptor
fileDescriptor The java.io.FileDescriptor object for the new descriptor
    public ChannelDescriptor(InputStream baseInputStreamModeFlags originalModesFileDescriptor fileDescriptor) {
        // The reason why we need the stream is to be able to invoke available() on it.
        // STDIN in Java is non-interruptible, non-selectable, and attempt to read
        // on such stream might lead to thread being blocked without *any* way to unblock it.
        // That's where available() comes it, so at least we could check whether
        // anything is available to be read without blocking.
        this(Channels.newChannel(baseInputStream), getNewFileno(), originalModesfileDescriptornew AtomicInteger(1), truefalse);
        this. = baseInputStream;
    }

    
Special constructor to create the ChannelDescriptor out of the stream, file number, mode flags, and file descriptor object. The channel will be created from the provided stream. The channel will be kept open until all ChannelDescriptor references to it have been closed. Note: in most cases, you should not use this constructor, it's reserved mostly for STDIN.

Parameters:
baseInputStream The stream to create the channel for the new descriptor
originalModes The mode flags for the new descriptor
    public ChannelDescriptor(InputStream baseInputStreamModeFlags originalModes) {
        // The reason why we need the stream is to be able to invoke available() on it.
        // STDIN in Java is non-interruptible, non-selectable, and attempt to read
        // on such stream might lead to thread being blocked without *any* way to unblock it.
        // That's where available() comes it, so at least we could check whether
        // anything is available to be read without blocking.
        this(Channels.newChannel(baseInputStream), getNewFileno(), originalModesnew FileDescriptor(), new AtomicInteger(1), truefalse);
        this. = baseInputStream;
    }

    
Construct a new ChannelDescriptor with the given channel, file number, and file descriptor object. The channel will be kept open until all ChannelDescriptor references to it have been closed. The channel's capabilities will be used to determine the "original" set of mode flags.

Parameters:
channel The channel for the new descriptor
fileDescriptor The java.io.FileDescriptor object for the new descriptor
    public ChannelDescriptor(Channel channelFileDescriptor fileDescriptorthrows InvalidValueException {
        this(channelgetModesFromChannel(channel), fileDescriptor);
    }
    
    @Deprecated
    public ChannelDescriptor(Channel channelint filenoFileDescriptor fileDescriptorthrows InvalidValueException {
        this(channelgetModesFromChannel(channel), fileDescriptor);
    }

    
Construct a new ChannelDescriptor with the given channel, file number, and file descriptor object. The channel will be kept open until all ChannelDescriptor references to it have been closed. The channel's capabilities will be used to determine the "original" set of mode flags. This version generates a new fileno.

Parameters:
channel The channel for the new descriptor
    public ChannelDescriptor(Channel channelthrows InvalidValueException {
        this(channelgetModesFromChannel(channel), new FileDescriptor());
    }

    
Get this descriptor's file number.

Returns:
the fileno for this descriptor
    public int getFileno() {
        return ;
    }
    
    
Get the FileDescriptor object associated with this descriptor. This is not guaranteed to be a "valid" descriptor in the terms of the Java implementation, but is provided for completeness and for cases where it is possible to get a valid FileDescriptor for a given channel.

Returns:
the java.io.FileDescriptor object associated with this descriptor
    public FileDescriptor getFileDescriptor() {
        return ;
    }

    
The channel associated with this descriptor. The channel will be reference counted through ChannelDescriptor and kept open until all ChannelDescriptor objects have been closed. References that leave ChannelDescriptor through this method will not be counted.

Returns:
the java.nio.channels.Channel associated with this descriptor
    public Channel getChannel() {
        return ;
    }

    
This is intentionally non-public, since it should not be really used outside of very limited use case (handling of STDIN). See ChannelDescriptor(java.io.InputStream,org.jruby.util.io.ModeFlags,java.io.FileDescriptor) for more info.
    /*package-protected*/ InputStream getBaseInputStream() {
        return ;
    }

    
Whether the channel associated with this descriptor is seekable (i.e. whether it is instanceof FileChannel).

Returns:
true if the associated channel is seekable, false otherwise
    public boolean isSeekable() {
        return  && ;
    }
    
    
Set the channel to be explicitly seekable or not, for streams that appear to be seekable with the instanceof FileChannel check.

Parameters:
canBeSeekable Whether the channel is seekable or not.
    public void setCanBeSeekable(boolean canBeSeekable) {
        this. = canBeSeekable;
    }
    
    
Whether the channel associated with this descriptor is a NullChannel, for which many operations are simply noops.
    public boolean isNull() {
        return  instanceof NullChannel;
    }

    
Whether the channel associated with this descriptor is writable (i.e. whether it is instanceof WritableByteChannel).

Returns:
true if the associated channel is writable, false otherwise
    public boolean isWritable() {
        return ;
    }

    
Whether the channel associated with this descriptor is readable (i.e. whether it is instanceof ReadableByteChannel).

Returns:
true if the associated channel is readable, false otherwise
    public boolean isReadable() {
        return ;
    }
    
    
Whether the channel associated with this descriptor is open.

Returns:
true if the associated channel is open, false otherwise
    public boolean isOpen() {
        return .isOpen();
    }
    
    
Check whether the isOpen returns true, raising a BadDescriptorException if it returns false.

Throws:
BadDescriptorException if isOpen returns false
    public void checkOpen() throws BadDescriptorException {
        if (!isOpen()) {
            throw new BadDescriptorException();
        }
    }
    
    
Get the original mode flags for the descriptor.

Returns:
the original mode flags for the descriptor
    public ModeFlags getOriginalModes() {
        return ;
    }
    
    
Check whether a specified set of mode flags is a superset of this descriptor's original set of mode flags.

Parameters:
newModes The modes to confirm as superset
Throws:
InvalidValueException if the modes are not a superset
    public void checkNewModes(ModeFlags newModesthrows InvalidValueException {
        if (!newModes.isSubsetOf()) {
            throw new InvalidValueException();
        }
    }
    
    
Mimics the POSIX dup(2) function, returning a new descriptor that references the same open channel.

Returns:
A duplicate ChannelDescriptor based on this one
    public ChannelDescriptor dup() {
        synchronized () {
            .incrementAndGet();
            int newFileno = getNewFileno();
            
            if (.info("Reopen fileno {}, refs now: {}"newFileno.get());
            return new ChannelDescriptor(newFileno);
        }
    }
    
    
Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open channel but with a specified fileno.

Parameters:
fileno The fileno to use for the new descriptor
Returns:
A duplicate ChannelDescriptor based on this one
    public ChannelDescriptor dup2(int fileno) {
        synchronized () {
            .incrementAndGet();
            if (.info("Reopen fileno {}, refs now: {}"fileno.get());
            return new ChannelDescriptor(fileno);
        }
    }
    
    
Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open channel but with a specified fileno. This differs from the fileno version by making the target descriptor into a new reference to the current descriptor's channel, closing what it originally pointed to and preserving its original fileno.

Parameters:
other the descriptor to dup this one into
    public void dup2Into(ChannelDescriptor otherthrows BadDescriptorExceptionIOException {
        synchronized () {
            .incrementAndGet();
            if (.info("Reopen fileno {}, refs now: {}".get());
            other.close();
            
            other.channel = ;
            other.originalModes = ;
            other.fileDescriptor = ;
            other.refCounter = ;
            other.canBeSeekable = ;
        }
    }
    public ChannelDescriptor reopen(Channel channelModeFlags modes) {
        return new ChannelDescriptor(channelmodes);
    }
    public ChannelDescriptor reopen(RandomAccessFile fileModeFlags modesthrows IOException {
        return new ChannelDescriptor(file.getChannel(), modesfile.getFD());
    }
    
    
Perform a low-level seek operation on the associated channel if it is instanceof FileChannel, or raise PipeException if it is not a FileChannel. Calls checkOpen to confirm the target channel is open. This is equivalent to the lseek(2) POSIX function, and like that function it bypasses any buffer flushing or invalidation as in ChannelStream.fseek.

Parameters:
offset the offset value to use
whence whence to seek
Returns:
the new offset into the FileChannel.
Throws:
java.io.IOException If there is an exception while seeking
InvalidValueException If the value specified for offset or whence is invalid
PipeException If the target channel is not seekable
BadDescriptorException If the target channel is already closed.
    public long lseek(long offsetint whencethrows IOExceptionInvalidValueExceptionPipeExceptionBadDescriptorException {
        if () {
            checkOpen();
            
            FileChannel fileChannel = (FileChannel);
            try {
                long pos;
                switch (whence) {
                case .:
                    pos = offset;
                    fileChannel.position(pos);
                    break;
                case .:
                    pos = fileChannel.position() + offset;
                    fileChannel.position(pos);
                    break;
                case .:
                    pos = fileChannel.size() + offset;
                    fileChannel.position(pos);
                    break;
                default:
                    throw new InvalidValueException();
                }
                return pos;
            } catch (IllegalArgumentException e) {
                throw new InvalidValueException();
            } catch (IOException ioe) {
                // "invalid seek" means it's an ESPIPE, so we rethrow as a PipeException()
                if (ioe.getMessage().equals("Illegal seek")) {
                    throw new PipeException();
                }
                throw ioe;
            }
        } else {
            throw new PipeException();
        }
    }

    
Perform a low-level read of the specified number of bytes into the specified byte list. The incoming bytes will be appended to the byte list. This is equivalent to the read(2) POSIX function, and like that function it ignores read and write buffers defined elsewhere.

Parameters:
number the number of bytes to read
byteList the byte list on which to append the incoming bytes
Returns:
the number of bytes actually read
Throws:
java.io.IOException if there is an exception during IO
BadDescriptorException if the associated channel is already closed.
See also:
org.jruby.util.ByteList
    public int read(int numberByteList byteListthrows IOExceptionBadDescriptorException {
        checkOpen();
        
        byteList.ensure(byteList.length() + number);
        int bytesRead = read(ByteBuffer.wrap(byteList.getUnsafeBytes(),
                byteList.begin() + byteList.length(), number));
        if (bytesRead > 0) {
            byteList.length(byteList.length() + bytesRead);
        }
        return bytesRead;
    }
    
    
Perform a low-level read of the remaining number of bytes into the specified byte buffer. The incoming bytes will be used to fill the remaining space in the target byte buffer. This is equivalent to the read(2) POSIX function, and like that function it ignores read and write buffers defined elsewhere.

Parameters:
buffer the java.nio.ByteBuffer in which to put the incoming bytes
Returns:
the number of bytes actually read
Throws:
java.io.IOException if there is an exception during IO
BadDescriptorException if the associated channel is already closed
See also:
java.nio.ByteBuffer
    public int read(ByteBuffer bufferthrows IOExceptionBadDescriptorException {
        checkOpen();
        // TODO: It would be nice to throw a better error for this
        if (!isReadable()) {
            throw new BadDescriptorException();
        }
        ReadableByteChannel readChannel = (ReadableByteChannel;
        int bytesRead = 0;
        bytesRead = readChannel.read(buffer);
        return bytesRead;
    }
    
    
Write the bytes in the specified byte list to the associated channel.

Parameters:
buffer the byte list containing the bytes to be written
Returns:
the number of bytes actually written
Throws:
java.io.IOException if there is an exception during IO
BadDescriptorException if the associated channel is already closed
    public int internalWrite(ByteBuffer bufferthrows IOExceptionBadDescriptorException {
        checkOpen();
        // TODO: It would be nice to throw a better error for this
        if (!isWritable()) {
            throw new BadDescriptorException();
        }
        
        WritableByteChannel writeChannel = (WritableByteChannel);
        
        // if appendable, we always seek to the end before writing
        if (isSeekable() && .isAppendable()) {
            // if already in append mode, we don't do our own seeking
            if (!) {
                FileChannel fileChannel = (FileChannel);
                fileChannel.position(fileChannel.size());
            }
        }
        
        return writeChannel.write(buffer);
    }
    
    
Write the bytes in the specified byte list to the associated channel.

Parameters:
buffer the byte list containing the bytes to be written
Returns:
the number of bytes actually written
Throws:
java.io.IOException if there is an exception during IO
BadDescriptorException if the associated channel is already closed
    public int write(ByteBuffer bufferthrows IOExceptionBadDescriptorException {
        checkOpen();
        
        return internalWrite(buffer);
    }
    
    
Write the bytes in the specified byte list to the associated channel.

Parameters:
buf the byte list containing the bytes to be written
Returns:
the number of bytes actually written
Throws:
java.io.IOException if there is an exception during IO
BadDescriptorException if the associated channel is already closed
    public int write(ByteList bufthrows IOExceptionBadDescriptorException {
        checkOpen();
        
        return internalWrite(ByteBuffer.wrap(buf.getUnsafeBytes(), buf.begin(), buf.length()));
    }
    
    private final ByteBuffer directBuffer = ByteBuffer.allocateDirect(8192);

    
Write the bytes in the specified byte list to the associated channel.

Parameters:
buf the byte list containing the bytes to be written
offset the offset to start at. this is relative to the begin variable in the but
len the amount of bytes to write. this should not be longer than the buffer
Returns:
the number of bytes actually written
Throws:
java.io.IOException if there is an exception during IO
BadDescriptorException if the associated channel is already closed
    public int write(ByteList bufint offsetint lenthrows IOExceptionBadDescriptorException {
        checkOpen();
        
        return internalWrite(ByteBuffer.wrap(buf.getUnsafeBytes(), buf.begin()+offsetlen));
    }
    
    
Write the byte represented by the specified int to the associated channel.

Parameters:
c The byte to write
Returns:
1 if the byte was written, 0 if not and -1 if there was an error (@see java.nio.channels.WritableByteChannel.write(java.nio.ByteBuffer))
Throws:
java.io.IOException If there was an exception during IO
BadDescriptorException if the associated channel is already closed
    public int write(int cthrows IOExceptionBadDescriptorException {
        checkOpen();
        
        ByteBuffer buf = ByteBuffer.allocate(1);
        buf.put((byte)c);
        buf.flip();
        
        return internalWrite(buf);
    }

    
Open a new descriptor using the given working directory, file path, mode flags, and file permission. This is equivalent to the open(2) POSIX function. See org.jruby.util.io.ChannelDescriptor.open(String, String, ModeFlags, int, POSIX) for the version that also sets file permissions.

Parameters:
cwd the "current working directory" to use when opening the file
path the file path to open
flags the mode flags to use for opening the file
Returns:
a new ChannelDescriptor based on the specified parameters
Throws:
java.io.FileNotFoundException if the target file could not be found and the create flag was not specified
DirectoryAsFileException if the target file is a directory being opened as a file
FileExistsException if the target file should be created anew, but already exists
java.io.IOException if there is an exception during IO
        return open(cwdpathflags, 0, nullnull);
    }
    
    
Open a new descriptor using the given working directory, file path, mode flags, and file permission. This is equivalent to the open(2) POSIX function. See org.jruby.util.io.ChannelDescriptor.open(String, String, ModeFlags, int, POSIX) for the version that also sets file permissions.

Parameters:
cwd the "current working directory" to use when opening the file
path the file path to open
flags the mode flags to use for opening the file
classLoader a ClassLoader to use for classpath: resources
Returns:
a new ChannelDescriptor based on the specified parameters
Throws:
java.io.FileNotFoundException if the target file could not be found and the create flag was not specified
DirectoryAsFileException if the target file is a directory being opened as a file
FileExistsException if the target file should be created anew, but already exists
java.io.IOException if there is an exception during IO
    public static ChannelDescriptor open(String cwdString pathModeFlags flagsClassLoader classLoaderthrows FileNotFoundExceptionDirectoryAsFileExceptionFileExistsExceptionIOException {
        return open(cwdpathflags, 0, nullclassLoader);
    }

    
Open a new descriptor using the given working directory, file path, mode flags, and file permission. This is equivalent to the open(2) POSIX function.

Parameters:
cwd the "current working directory" to use when opening the file
path the file path to open
flags the mode flags to use for opening the file
perm the file permissions to use when creating a new file (currently unobserved)
posix a POSIX api implementation, used for setting permissions; if null, permissions are ignored
Returns:
a new ChannelDescriptor based on the specified parameters
Throws:
java.io.FileNotFoundException if the target file could not be found and the create flag was not specified
DirectoryAsFileException if the target file is a directory being opened as a file
FileExistsException if the target file should be created anew, but already exists
java.io.IOException if there is an exception during IO
    public static ChannelDescriptor open(String cwdString pathModeFlags flagsint permPOSIX posixthrows FileNotFoundExceptionDirectoryAsFileExceptionFileExistsExceptionIOException {
        return open(cwdpathflagspermposixnull);
    }
    
    
Open a new descriptor using the given working directory, file path, mode flags, and file permission. This is equivalent to the open(2) POSIX function.

Parameters:
cwd the "current working directory" to use when opening the file
path the file path to open
flags the mode flags to use for opening the file
perm the file permissions to use when creating a new file (currently unobserved)
posix a POSIX api implementation, used for setting permissions; if null, permissions are ignored
classLoader a ClassLoader to use for classpath: resources
Returns:
a new ChannelDescriptor based on the specified parameters
Throws:
java.io.FileNotFoundException if the target file could not be found and the create flag was not specified
DirectoryAsFileException if the target file is a directory being opened as a file
FileExistsException if the target file should be created anew, but already exists
java.io.IOException if there is an exception during IO
    public static ChannelDescriptor open(String cwdString pathModeFlags flagsint permPOSIX posixClassLoader classLoaderthrows FileNotFoundExceptionDirectoryAsFileExceptionFileExistsExceptionIOException {
        boolean fileCreated = false;
        if (path.equals("/dev/null") || path.equalsIgnoreCase("nul:") || path.equalsIgnoreCase("nul")) {
            Channel nullChannel = new NullChannel();
            // FIXME: don't use RubyIO for this
            return new ChannelDescriptor(nullChannelflags);
        } else if (path.startsWith("file:")) {
            int bangIndex = path.indexOf("!");
            if (bangIndex > 0) {
                String filePath = path.substring(5, bangIndex);
                String internalPath = path.substring(bangIndex + 2);
                if (!new File(filePath).exists()) {
                    throw new FileNotFoundException(path);
                }
                JarFile jf = new JarFile(filePath);
                ZipEntry entry = RubyFile.getFileEntry(jfinternalPath);
                if (entry == null) {
                    throw new FileNotFoundException(path);
                }
                InputStream is = jf.getInputStream(entry);
                // FIXME: don't use RubyIO for this
                return new ChannelDescriptor(Channels.newChannel(is), flags);
            } else {
                // raw file URL, just open directly
                URL url = new URL(path);
                InputStream is = url.openStream();
                // FIXME: don't use RubyIO for this
                return new ChannelDescriptor(Channels.newChannel(is), flags);
            }
        } else if (path.startsWith("classpath:/") && classLoader != null) {
            path = path.substring("classpath:/".length());
            InputStream is = classLoader.getResourceAsStream(path);
            // FIXME: don't use RubyIO for this
            return new ChannelDescriptor(Channels.newChannel(is), flags);
        } else {
            JRubyFile theFile = JRubyFile.create(cwd,path);
            if (theFile.isDirectory() && flags.isWritable()) {
                throw new DirectoryAsFileException();
            }
            if (flags.isCreate()) {
                if (theFile.exists() && flags.isExclusive()) {
                    throw new FileExistsException(path);
                }
                try {
                    fileCreated = theFile.createNewFile();
                } catch (IOException ioe) {
                    // See JRUBY-4380.
                    // MRI behavior: raise Errno::ENOENT in case
                    // when the directory for the file doesn't exist.
                    // Java in such cases just throws IOException.
                    File parent = theFile.getParentFile();
                    if (parent != null && parent != theFile && !parent.exists()) {
                        throw new FileNotFoundException(path);
                    } else if (!theFile.canWrite()) {
                        throw new PermissionDeniedException(path);
                    } else {
                        // for all other IO errors, just re-throw the original exception
                        throw ioe;
                    }
                }
            } else {
                if (!theFile.exists()) {
                    throw new FileNotFoundException(path);
                }
            }
            FileDescriptor fileDescriptor;
            FileChannel fileChannel;
            boolean isInAppendMode;
            if (flags.isWritable() && !flags.isReadable()) {
                FileOutputStream fos = new FileOutputStream(theFileflags.isAppendable());
                fileChannel = fos.getChannel();
                fileDescriptor = fos.getFD();
                isInAppendMode = true;
            } else {
                RandomAccessFile raf = new RandomAccessFile(theFileflags.toJavaModeString());
                fileChannel = raf.getChannel();
                fileDescriptor = raf.getFD();
                isInAppendMode = false;
            }
            // call chmod after we created the RandomAccesFile
            // because otherwise, the file could be read-only
            if (fileCreated) {
                // attempt to set the permissions, if we have been passed a POSIX instance,
                // perm is > 0, and only if the file was created in this call.
                if (posix != null && perm > 0) {
                    posix.chmod(theFile.getPath(), perm);
                }
            }
            try {
                if (flags.isTruncate()) fileChannel.truncate(0);
            } catch (IOException ioe) {
                if (ioe.getMessage().equals("Illegal seek")) {
                    // ignore; it's a pipe or fifo that can't be truncated
                } else {
                    throw ioe;
                }
            }
            // TODO: append should set the FD to end, no? But there is no seek(int) in libc!
            //if (modes.isAppendable()) seek(0, Stream.SEEK_END);
            return new ChannelDescriptor(fileChannelflagsfileDescriptorisInAppendMode);
        }
    }
    
    
Close this descriptor. If in closing the last ChannelDescriptor reference to the associate channel is closed, the channel itself will be closed.

Throws:
BadDescriptorException if the associated channel is already closed
java.io.IOException if there is an exception during IO
    public void close() throws BadDescriptorExceptionIOException {
        // tidy up
        finish(true);
    }
    void finish(boolean closethrows BadDescriptorExceptionIOException {
        synchronized () {
            // if refcount is at or below zero, we're no longer valid
            if (.get() <= 0) {
                throw new BadDescriptorException();
            }
            // if channel is already closed, we're no longer valid
            if (!.isOpen()) {
                throw new BadDescriptorException();
            }
            // otherwise decrement and possibly close as normal
            int count = .decrementAndGet();
            if (.info("Descriptor for fileno {} refs: {}"count);
            if (count <= 0) {
                // if we're the last referrer, close the channel
                try {
                    if (close.close();
                } finally {
                    unregisterDescriptor();
                }
            }
        }
    }
    
    
Build a set of mode flags using the specified channel's actual capabilities.

Parameters:
channel the channel to examine for capabilities
Returns:
the mode flags
Throws:
InvalidValueException
    private static ModeFlags getModesFromChannel(Channel channelthrows InvalidValueException {
        ModeFlags modes;
        if (channel instanceof ReadableByteChannel) {
            if (channel instanceof WritableByteChannel) {
                modes = new ModeFlags();
            } else {
                modes = new ModeFlags();
            }
        } else if (channel instanceof WritableByteChannel) {
            modes = new ModeFlags();
        } else {
            // FIXME: I don't like this
            modes = new ModeFlags();
        }
        
        return modes;
    }
    // FIXME shouldn't use static; would interfere with other runtimes in the same JVM
    protected static final AtomicInteger internalFilenoIndex = new AtomicInteger(2);
    public static int getNewFileno() {
        return .incrementAndGet();
    }
    private static void registerDescriptor(ChannelDescriptor descriptor) {
        .put(descriptor.getFileno(), descriptor);
    }
    private static void unregisterDescriptor(int aFileno) {
        .remove(aFileno);
    }
    public static ChannelDescriptor getDescriptorByFileno(int aFileno) {
        return .get(aFileno);
    }
    
New to GrepCode? Check out our FAQ X