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

poapsis.ortserver.storage.database.0.1.0-RC1.source-code.DatabaseStorageProvider.kt Maven / Gradle / Ivy

/*
 * Copyright (C) 2023 The ORT Server Authors (See )
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 * License-Filename: LICENSE
 */

package org.eclipse.apoapsis.ortserver.storage.database

import java.io.InputStream

import kotlinx.datetime.Clock

import org.eclipse.apoapsis.ortserver.storage.Key
import org.eclipse.apoapsis.ortserver.storage.StorageEntry
import org.eclipse.apoapsis.ortserver.storage.StorageProvider

import org.jetbrains.exposed.sql.SizedIterable
import org.jetbrains.exposed.sql.Transaction
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction

/**
 * Implementation of the [StorageProvider] interface that is backed by a database table using PostgreSQL's large
 * objects.
 */
class DatabaseStorageProvider(
    /** The namespace under which data is to be stored. */
    private val namespace: String,

    /** The maximum size of a storage entry that can be loaded into memory. */
    private val inMemoryLimit: Int
) : StorageProvider {
    override fun read(key: Key): StorageEntry = transaction {
        val entry = findByKey(key).single()

        val inputStream = readLargeObject(entry.data, entry.size, inMemoryLimit)
        StorageEntry.create(inputStream, entry.contentType, entry.size)
    }

    override fun write(key: Key, data: InputStream, length: Long, contentType: String?) {
        transaction {
            // In case of an override, delete the key first. This may not be the cleanest solution (it has the
            // side effect that the createdAt date is changed), but it is very easy to implement.
            deleteKey(key)

            StorageDao.new {
                createdAt = Clock.System.now()
                namespace = [email protected]
                size = length
                this.key = key.key
                this.contentType = contentType
                this.data = storeLargeObject(data)
            }
        }
    }

    override fun contains(key: Key): Boolean = transaction {
        !findByKey(key).empty()
    }

    override fun delete(key: Key): Boolean = transaction {
        deleteKey(key)
    }

    /**
     * Delete the entry with the given [key] from this storage. The caller is responsible for transaction management.
     */
    private fun Transaction.deleteKey(key: Key) =
        findByKey(key).singleOrNull()?.let { dao ->
            deleteLargeObject(dao.data)
            dao.delete()
            true
        } ?: false

    /**
     * Search for an entry with the given [key].
     */
    private fun findByKey(key: Key): SizedIterable =
        StorageDao.find { (StorageTable.key eq key.key) and (StorageTable.namespace eq namespace) }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy