All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fireflysource.net.http.common.content.provider.AbstractFileContentProvider.kt Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
package com.fireflysource.net.http.common.content.provider

import com.fireflysource.common.coroutine.asVoidFuture
import com.fireflysource.common.coroutine.event
import com.fireflysource.common.coroutine.pollAll
import com.fireflysource.common.exception.UnsupportedOperationException
import com.fireflysource.common.io.InputChannel
import com.fireflysource.common.io.closeJob
import com.fireflysource.common.io.openFileChannelAsync
import com.fireflysource.common.io.readAwait
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import java.nio.ByteBuffer
import java.nio.file.Files
import java.nio.file.OpenOption
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
import java.util.concurrent.atomic.AtomicBoolean

abstract class AbstractFileContentProvider(
    val path: Path,
    val options: Set,
    var position: Long,
    val length: Long
) : InputChannel {

    private val readChannel: Channel = Channel(Channel.UNLIMITED)
    private val readJob: Job
    private val closed = AtomicBoolean(false)
    private val lastPosition = position + length

    constructor(path: Path, vararg options: OpenOption) : this(path, options.toSet(), 0, Files.size(path))

    init {
        readJob = event {
            val fileChannel = openFileChannelAsync(path, options).await()

            readMessageLoop@ while (true) {
                when (val readFileMessage = readChannel.receive()) {
                    is ReadFileRequest -> {
                        val (buf, future) = readFileMessage

                        suspend fun endRead() {
                            fileChannel.closeJob().join()
                            closed.set(true)
                            future.complete(-1)
                        }

                        if (position >= lastPosition) {
                            endRead()
                            break@readMessageLoop
                        }
                        try {
                            val len = fileChannel.readAwait(buf, position)
                            if (len < 0) {
                                endRead()
                                break@readMessageLoop
                            } else {
                                position += len
                                future.complete(len)
                            }
                        } catch (e: Exception) {
                            future.completeExceptionally(e)
                        }
                    }
                    is EndReadFile -> {
                        fileChannel.closeJob().join()
                        closed.set(true)
                        break@readMessageLoop
                    }
                }
            }

            readChannel.pollAll { }
        }
    }

    fun length(): Long = length

    override fun isOpen(): Boolean = !closed.get()

    fun toByteBuffer(): ByteBuffer {
        throw UnsupportedOperationException("The file content does not support this method")
    }

    override fun closeAsync(): CompletableFuture = event { closeAwait() }.asVoidFuture()

    override fun close() {
        if (isOpen) readChannel.offer(EndReadFile)
    }

    private suspend fun closeAwait() {
        close()
        readJob.join()
    }

    override fun read(byteBuffer: ByteBuffer): CompletableFuture {
        val future = CompletableFuture()
        readChannel.offer(ReadFileRequest(byteBuffer, future))
        return future
    }

}

sealed class ReadFileMessage
data class ReadFileRequest(val buffer: ByteBuffer, val future: CompletableFuture) : ReadFileMessage()
object EndReadFile : ReadFileMessage()




© 2015 - 2024 Weber Informatics LLC | Privacy Policy