#18 Add logging to cache

This commit is contained in:
FyloZ 2022-01-01 18:01:59 -05:00
parent bb069512b4
commit 26d696d66b
Signed by: william
GPG Key ID: 835378AE9AF4AE97
8 changed files with 64 additions and 36 deletions

View File

@ -102,7 +102,7 @@ tasks.withType<JavaCompile>() {
} }
tasks.withType<KotlinCompile>().all { tasks.withType<KotlinCompile>().all {
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString() jvmTarget = JavaVersion.VERSION_17.toString()
freeCompilerArgs = listOf( freeCompilerArgs = listOf(
"-Xopt-in=kotlin.contracts.ExperimentalContracts", "-Xopt-in=kotlin.contracts.ExperimentalContracts",
"-Xinline-classes" "-Xinline-classes"

View File

@ -51,7 +51,7 @@ class MaterialTypeInitializer(
// Remove old system types // Remove old system types
oldSystemTypes.forEach { oldSystemTypes.forEach {
logger.info("Material type '${it.name}' is not a system type anymore") logger.info("Material type '${it.name}' is not a system type anymore")
materialTypeService.update(materialType(it, newSystemType = false)) materialTypeService.updateSystemType(it.copy(systemType = false))
} }
} }
} }

View File

@ -7,7 +7,7 @@ import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
interface MaterialTypeService : interface MaterialTypeService :
ExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository> { ExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository> {
/** Checks if a material type with the given [prefix] exists. */ /** Checks if a material type with the given [prefix] exists. */
fun existsByPrefix(prefix: String): Boolean fun existsByPrefix(prefix: String): Boolean
@ -19,14 +19,17 @@ interface MaterialTypeService :
/** Gets all material types who are not a system type. */ /** Gets all material types who are not a system type. */
fun getAllNonSystemType(): Collection<MaterialType> fun getAllNonSystemType(): Collection<MaterialType>
/** Allows to update the given system [materialType], should not be exposed to users. */
fun updateSystemType(materialType: MaterialType): MaterialType
} }
@Service @Service
@Profile("!emergency") @Profile("!emergency")
class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val materialService: MaterialService) : class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val materialService: MaterialService) :
AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository>( AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository>(
repository repository
), MaterialTypeService { ), MaterialTypeService {
override fun idNotFoundException(id: Long) = materialTypeIdNotFoundException(id) override fun idNotFoundException(id: Long) = materialTypeIdNotFoundException(id)
override fun idAlreadyExistsException(id: Long) = materialIdAlreadyExistsException(id) override fun idAlreadyExistsException(id: Long) = materialIdAlreadyExistsException(id)
override fun nameNotFoundException(name: String) = materialTypeNameNotFoundException(name) override fun nameNotFoundException(name: String) = materialTypeNameNotFoundException(name)
@ -36,7 +39,7 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma
override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix) override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix)
override fun isUsedByMaterial(materialType: MaterialType): Boolean = override fun isUsedByMaterial(materialType: MaterialType): Boolean =
materialService.existsByMaterialType(materialType) materialService.existsByMaterialType(materialType)
override fun getAllSystemTypes(): Collection<MaterialType> = repository.findAllBySystemTypeIs(true) override fun getAllSystemTypes(): Collection<MaterialType> = repository.findAllBySystemTypeIs(true)
override fun getAllNonSystemType(): Collection<MaterialType> = repository.findAllBySystemTypeIs(false) override fun getAllNonSystemType(): Collection<MaterialType> = repository.findAllBySystemTypeIs(false)
@ -52,16 +55,22 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma
return update(with(entity) { return update(with(entity) {
MaterialType( MaterialType(
id = id, id = id,
name = if (isNotNullAndNotBlank(name)) name else persistedMaterialType.name, name = if (isNotNullAndNotBlank(name)) name else persistedMaterialType.name,
prefix = if (isNotNullAndNotBlank(prefix)) prefix else persistedMaterialType.prefix, prefix = if (isNotNullAndNotBlank(prefix)) prefix else persistedMaterialType.prefix,
systemType = false systemType = false
) )
}) })
} }
override fun update(entity: MaterialType): MaterialType { override fun updateSystemType(materialType: MaterialType) =
if (repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) { update(materialType, true)
override fun update(entity: MaterialType) =
update(entity, false)
private fun update(entity: MaterialType, allowSystemTypes: Boolean): MaterialType {
if (!allowSystemTypes && repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) {
throw cannotUpdateSystemMaterialTypeException(entity) throw cannotUpdateSystemMaterialTypeException(entity)
} }

View File

@ -1,17 +1,19 @@
package dev.fyloz.colorrecipesexplorer.service.files package dev.fyloz.colorrecipesexplorer.service.files
import dev.fyloz.colorrecipesexplorer.utils.FilePath import dev.fyloz.colorrecipesexplorer.utils.FilePath
import mu.KotlinLogging
object FileExistCache { object FileExistCache {
private val map = hashMapOf<FilePath, Boolean>() private val logger = KotlinLogging.logger {}
private val map = hashMapOf<String, Boolean>()
/** Checks if the given [path] is in the cache. */ /** Checks if the given [path] is in the cache. */
operator fun contains(path: FilePath) = operator fun contains(path: FilePath) =
path in map path.path in map
/** Checks if the file at the given [path] exists. */ /** Checks if the file at the given [path] exists. */
fun exists(path: FilePath) = fun exists(path: FilePath) =
map[path] ?: false map[path.path] ?: false
/** Sets the file at the given [path] as existing. */ /** Sets the file at the given [path] as existing. */
fun setExists(path: FilePath) = fun setExists(path: FilePath) =
@ -21,7 +23,10 @@ object FileExistCache {
fun setDoesNotExists(path: FilePath) = fun setDoesNotExists(path: FilePath) =
set(path, false) set(path, false)
private fun set(path: FilePath, exists: Boolean) { /** Sets if the file at the given [path] [exists]. */
map[path] = exists fun set(path: FilePath, exists: Boolean) {
map[path.path] = exists
logger.debug("Updated FileExistCache state: ${path.path} -> $exists")
} }
} }

View File

@ -5,6 +5,7 @@ import dev.fyloz.colorrecipesexplorer.exception.RestException
import dev.fyloz.colorrecipesexplorer.utils.File import dev.fyloz.colorrecipesexplorer.utils.File
import dev.fyloz.colorrecipesexplorer.utils.FilePath import dev.fyloz.colorrecipesexplorer.utils.FilePath
import dev.fyloz.colorrecipesexplorer.utils.withFileAt import dev.fyloz.colorrecipesexplorer.utils.withFileAt
import mu.KotlinLogging
import org.slf4j.Logger import org.slf4j.Logger
import org.springframework.core.io.ByteArrayResource import org.springframework.core.io.ByteArrayResource
import org.springframework.core.io.Resource import org.springframework.core.io.Resource
@ -47,16 +48,19 @@ interface WriteableFileService : FileService {
@Service @Service
class FileServiceImpl( class FileServiceImpl(
private val creProperties: CreProperties, private val creProperties: CreProperties
private val logger: Logger
) : WriteableFileService { ) : WriteableFileService {
private val logger = KotlinLogging.logger {}
override fun exists(path: String): Boolean { override fun exists(path: String): Boolean {
val fullPath = path.fullPath() val fullPath = path.fullPath()
return if (fullPath in FileExistCache) { return if (fullPath in FileExistCache) {
FileExistCache.exists(fullPath) FileExistCache.exists(fullPath)
} else { } else {
withFileAt(fullPath) { withFileAt(fullPath) {
this.exists() && this.isFile (this.exists() && this.isFile).also {
FileExistCache.set(fullPath, it)
}
} }
} }
} }
@ -79,6 +83,8 @@ class FileServiceImpl(
withFileAt(fullPath) { withFileAt(fullPath) {
this.create() this.create()
FileExistCache.setExists(fullPath) FileExistCache.setExists(fullPath)
logger.info("Created file at '${fullPath.path}'")
} }
} catch (ex: IOException) { } catch (ex: IOException) {
FileCreateException(path).logAndThrow(ex, logger) FileCreateException(path).logAndThrow(ex, logger)
@ -88,11 +94,13 @@ class FileServiceImpl(
override fun write(file: MultipartFile, path: String, overwrite: Boolean) = override fun write(file: MultipartFile, path: String, overwrite: Boolean) =
prepareWrite(path, overwrite) { prepareWrite(path, overwrite) {
logWrittenDataSize(file.size)
file.transferTo(this.toPath()) file.transferTo(this.toPath())
} }
override fun write(data: ByteArrayResource, path: String, overwrite: Boolean) = override fun write(data: ByteArrayResource, path: String, overwrite: Boolean) =
prepareWrite(path, overwrite) { prepareWrite(path, overwrite) {
logWrittenDataSize(data.contentLength())
this.writeBytes(data.byteArray) this.writeBytes(data.byteArray)
} }
@ -104,6 +112,8 @@ class FileServiceImpl(
this.delete() this.delete()
FileExistCache.setDoesNotExists(fullPath) FileExistCache.setDoesNotExists(fullPath)
logger.info("Deleted file at '${fullPath.path}'")
} }
} catch (ex: IOException) { } catch (ex: IOException) {
FileDeleteException(path).logAndThrow(ex, logger) FileDeleteException(path).logAndThrow(ex, logger)
@ -130,11 +140,17 @@ class FileServiceImpl(
try { try {
withFileAt(fullPath) { withFileAt(fullPath) {
this.op() this.op()
logger.info("Wrote data to file at '${fullPath.path}'")
} }
} catch (ex: IOException) { } catch (ex: IOException) {
FileWriteException(path).logAndThrow(ex, logger) FileWriteException(path).logAndThrow(ex, logger)
} }
} }
private fun logWrittenDataSize(size: Long) {
logger.debug("Writing $size bytes to file system...")
}
} }
private const val FILE_IO_EXCEPTION_TITLE = "File IO error" private const val FILE_IO_EXCEPTION_TITLE = "File IO error"

View File

@ -10,29 +10,29 @@ class File(val file: JavaFile) {
get() = file.isFile get() = file.isFile
fun toPath(): Path = fun toPath(): Path =
file.toPath() file.toPath()
fun exists() = fun exists() =
file.exists() file.exists()
fun readBytes() = fun readBytes() =
file.readBytes() file.readBytes()
fun writeBytes(array: ByteArray) = fun writeBytes(array: ByteArray) =
file.writeBytes(array) file.writeBytes(array)
fun create() = fun create() =
file.create() file.create()
fun delete(): Boolean = fun delete(): Boolean =
file.delete() file.delete()
companion object { companion object {
fun from(path: String) = fun from(path: String) =
File(JavaFile(path)) File(JavaFile(path))
fun from(path: FilePath) = fun from(path: FilePath) =
from(path.path) from(path.path)
} }
} }
@ -41,7 +41,7 @@ class FilePath(val path: String)
/** Runs the given [block] in the context of a file with the given [fullPath]. */ /** Runs the given [block] in the context of a file with the given [fullPath]. */
fun <T> withFileAt(fullPath: FilePath, block: File.() -> T) = fun <T> withFileAt(fullPath: FilePath, block: File.() -> T) =
File.from(fullPath).block() File.from(fullPath).block()
/** Shortcut to create a file and its parent directories. */ /** Shortcut to create a file and its parent directories. */
fun JavaFile.create() { fun JavaFile.create() {

View File

@ -8,9 +8,9 @@
%green(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{36}): %msg%n%throwable %green(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{36}): %msg%n%throwable
</Pattern> </Pattern>
</layout> </layout>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">-->
<level>INFO</level> <!-- <level>INFO</level>-->
</filter> <!-- </filter>-->
</appender> </appender>
<appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">

View File

@ -22,9 +22,7 @@ private val mockFilePathPath = Path.of(mockFilePath)
private val mockFileData = byteArrayOf(0x1, 0x8, 0xa, 0xf) private val mockFileData = byteArrayOf(0x1, 0x8, 0xa, 0xf)
class FileServiceTest { class FileServiceTest {
private val fileService = spyk(FileServiceImpl(creProperties, mockk { private val fileService = spyk(FileServiceImpl(creProperties))
every { error(any(), any<Exception>()) } just Runs
}))
private val mockFile = mockk<File> { private val mockFile = mockk<File> {
every { file } returns mockk() every { file } returns mockk()
every { exists() } returns true every { exists() } returns true