Resources
Examples of using the Resource API to wrap existing Java IO objects
The Resource API can be used to adapt Java IO objects such as InputStreams and Channels. The Resource object provides several methods for wrapping common Java objects. In the .Net implementation the Resource API would wrap .Net IO objects.
Create Resources
import scalax.io._
import java.io._
import java.nio.channels._
import java.net.URL
// see codec examples in scala io core for details on why there is an implicit codec here
implicit val codec = scalax.io.Codec.UTF8
// get various input streams, readers an channels
val inputStream: InputStream = new URL("http://someurl.com").openStream
val in: InputStreamResource[InputStream] = Resource.fromInputStream(inputStream)
val bufferedIn: InputStreamResource[BufferedInputStream] = in.buffered
val readableChannel: Resource[ReadableByteChannel] = in.readableByteChannel
val reader: ReaderResource[Reader] = in.reader
val bufferedReader: ReaderResource[BufferedReader] = reader.buffered
// get various output streams and channels
val outputStream: FileOutputStream = new FileOutputStream("file")
val out: OutputStreamResource[OutputStream] = Resource.fromOutputStream(outputStream)
val bufferedOut: OutputStreamResource[BufferedOutputStream] = out.buffered
val writableChannel: Resource[WritableByteChannel] = out.writableByteChannel
val writer: WriterResource[Writer] = out.writer
val bufferedWriter: WriterResource[BufferedWriter] = writer.buffered
// examples getting ByteChannels
// default is a read/write/create channel
val channel: SeekableByteChannelResource[SeekableByteChannel] = Resource.fromFileString("file")
val channel2: SeekableByteChannelResource[SeekableByteChannel] =
Resource.fromRandomAccessFile(new RandomAccessFile("file","rw"))
val seekable: Seekable = channel2
val inOut: Input with Output = channel
val channel3: ByteChannelResource[FileChannel] =
Resource.fromByteChannel(new RandomAccessFile("file","rw").getChannel)
val inOut2: Input with Output = channel2
val readableByteChannel = Channels.newChannel(new FileInputStream("file"))
val readChannel : ReadableByteChannelResource[ReadableByteChannel] =
Resource.fromReadableByteChannel(readableByteChannel)
val in2:Input = readChannel
val writableByteChannel = Channels.newChannel(new FileOutputStream("file"))
val writeChannel : WritableByteChannelResource[WritableByteChannel] =
Resource.fromWritableByteChannel(writableByteChannel)
val out2:Output = writeChannel
Using Io Resources
The following examples demonstrate using the Resource objects and converting between them.
All resources are also Seekable, Input, Output, ReadChars and/or WriteChars so all normal IO operations are possible but the following examples are Resource only operations
import scalax.io._
import java.io._
import java.nio.channels._
val resource = Resource.fromInputStream(new FileInputStream("file"))
// The Resource objects have methods for converting between the common types
val bufferedInput: InputStreamResource[BufferedInputStream] = resource.buffered
val readChars: ReaderResource[Reader] = resource.reader(Codec.UTF8)
val readableByteChannel: ReadableByteChannelResource[ReadableByteChannel] =
resource.readableByteChannel
val bufferedReader = readChars.buffered
// there are also several ways to obtain the underlying java object
// for certain operations. Typically this is to micro manage how the
// data is read from the input
val availableBytes: Int = bufferedInput.acquireAndGet{
bufferedInputStream => bufferedInputStream.available
}
// If you want to perform an operation and have the option to easily
// get the exception acquireFor is a good solution
val firstLine: Either[scala.List[scala.Throwable], String] = bufferedReader.acquireFor{
reader => reader.readLine
}
Perform Additional Action On Close
import scalax.io._
import nio.SeekableFileChannel
// a close action can be created by passing a function to execute
// to the Closer object's apply method
// '''WARNING''' When defining a CloseAction make its type as generic
// as possible. IE if it can be a CloseAction[Closeable] do not
// make it a CloseAction[InputStream]. The reason has to do
// with contravariance. If you don't know what that means
// don't worry just trust me ;-)
val closer = CloseAction{(channel:Any) =>
println("About to close "+channel)
}
// another option is the extend/implement the CloseAction trait
val closer2 = new CloseAction[Any]{
protected def closeImpl(a: Any):Unit =
println("Message from second closer")
}
// closers can naturally be combined
val closerThenCloser2 = closer +: closer2
val closer2ThenCloser = closer :+ closer2
// we can then create a resource and pass it to the closer parameter
// now each time resource is used (and closed) the closer will also be executed
// just before the actual closing.
val resource = Resource.fromFileString("file")(closer)
// closeActions can also be added to an existing resource
// NOTE: Appended actions still are performed BEFORE
// resource is closed
resource.appendCloseAction(closerThenCloser2)
resource.prependCloseAction(closer2)
// The following are equivalent
Resource.fromFileString("file")(closer :+ closer2)
Resource.fromFileString("file")(closer).appendCloseAction(closer2)
Resource.fromFileString("file").appendCloseAction (closer :+ closer2)
Why Are Close Actions Contravariant
Normally one think in terms of ''Covariance'' (List[String] can be assigned to a List[Any]) but that cannot work for CloseActions so CloseActions have the exact opposite characteristics.
import scalax.io._
import java.io._
// Since CloseAction is Defined as CloseAction[-A], the following compiles
val action:CloseAction[String] = CloseAction[Any]{_ => ()}
//But
// val action:CloseAction[Any] = CloseAction[String]{_ => ()}
// does not.
// If you want to know why consider the following:
val resource:Resource[InputStream] = Resource.fromInputStream(new FileInputStream("file"))
val resource2:Resource[Closeable] = resource
val closeAction:CloseAction[InputStream] = CloseAction{in:InputStream => println(in.available)}
//Given the previous declarations it should be obvious that the following works
val updatedResource:Resource[InputStream] = resource.appendCloseAction(closeAction)
// However since resource2 is a Resource[Closeable] it should be obvious that one cannot
// add a closeAction that requires an InputStream. so the following would fail to compile
// resource2.appendCloseAction(closeAction)