#18 Add logging to cache
This commit is contained in:
parent
bb069512b4
commit
26d696d66b
|
@ -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"
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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"
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue