diff --git a/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt b/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt index 0a23c11a..dab9a6da 100644 --- a/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt +++ b/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt @@ -11,12 +11,12 @@ import okio.BufferedSink import okio.Source import okio.buffer import okio.sink -import java.io.File +import java.nio.file.Path -suspend fun Source.saveTo(file: File) { +suspend fun Source.saveTo(path: Path) { withIOContext { use { source -> - file.sink().buffer().use { it.writeAll(source) } + path.sink().buffer().use { it.writeAll(source) } } } } diff --git a/src/main/kotlin/ca/gosyer/core/logging/LoggingSetup.kt b/src/main/kotlin/ca/gosyer/core/logging/LoggingSetup.kt index c5fcf560..739ee203 100644 --- a/src/main/kotlin/ca/gosyer/core/logging/LoggingSetup.kt +++ b/src/main/kotlin/ca/gosyer/core/logging/LoggingSetup.kt @@ -16,7 +16,8 @@ import org.apache.logging.log4j.core.appender.ConsoleAppender import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory import org.slf4j.bridge.SLF4JBridgeHandler -import java.io.File +import java.nio.file.Path +import kotlin.io.path.absolutePathString import java.util.logging.LogManager as JLogManager const val consolePattern = @@ -37,7 +38,7 @@ const val filePattern = "{LOG_EXCEPTION_CONVERSION_WORD:-%xEx}" @Suppress("UPPER_BOUND_VIOLATED_WARNING") -fun initializeLogger(loggingLocation: File) { +fun initializeLogger(loggingLocation: Path) { val ctx = LogManager.getContext(false) as LoggerContext val builder = ConfigurationBuilderFactory.newConfigurationBuilder() .apply { @@ -59,11 +60,11 @@ fun initializeLogger(loggingLocation: File) { newAppender("Rolling", "RollingFile") .addAttribute( "fileName", - loggingLocation.absolutePath.trimEnd { it == '/' || it == '\\' } + "/rolling.log" + loggingLocation.absolutePathString().trimEnd { it == '/' || it == '\\' } + "/rolling.log" ) .addAttribute( "filePattern", - loggingLocation.absolutePath.trimEnd { it == '/' || it == '\\' } + "/archive/rolling-%d{yyyy-MM-dd-}.log.gz" + loggingLocation.absolutePathString().trimEnd { it == '/' || it == '\\' } + "/archive/rolling-%d{yyyy-MM-dd-}.log.gz" ) .add( newLayout("PatternLayout") diff --git a/src/main/kotlin/ca/gosyer/data/server/ServerService.kt b/src/main/kotlin/ca/gosyer/data/server/ServerService.kt index e17c1ca1..96dbedb7 100644 --- a/src/main/kotlin/ca/gosyer/data/server/ServerService.kt +++ b/src/main/kotlin/ca/gosyer/data/server/ServerService.kt @@ -21,13 +21,22 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge import kotlinx.coroutines.launch import mu.KotlinLogging -import java.io.File +import java.io.File.pathSeparatorChar import java.io.IOException import java.io.Reader +import java.nio.file.Path import java.util.jar.Attributes import java.util.jar.JarInputStream import javax.inject.Inject import kotlin.concurrent.thread +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.createDirectories +import kotlin.io.path.exists +import kotlin.io.path.inputStream +import kotlin.io.path.isExecutable +import kotlin.io.path.name +import kotlin.io.path.outputStream @OptIn(DelicateCoroutinesApi::class) class ServerService @Inject constructor( @@ -55,7 +64,7 @@ class ServerService @Inject constructor( } @Throws(IOException::class) - private fun copyJar(jarFile: File) { + private fun copyJar(jarFile: Path) { javaClass.getResourceAsStream("/Tachidesk.jar")?.buffered()?.use { input -> jarFile.outputStream().use { output -> input.copyTo(output) @@ -63,30 +72,30 @@ class ServerService @Inject constructor( } } - private fun getJavaFromPath(javaPath: File): String? { - val javaExeFile = File(javaPath, "java.exe") - val javaUnixFile = File(javaPath, "java") + private fun getJavaFromPath(javaPath: Path): String? { + val javaExeFile = javaPath.resolve("java.exe") + val javaUnixFile = javaPath.resolve("java") return when { - javaExeFile.exists() && javaExeFile.canExecute() -> javaExeFile.absolutePath - javaUnixFile.exists() && javaUnixFile.canExecute() -> javaUnixFile.absolutePath + javaExeFile.exists() && javaExeFile.isExecutable() -> javaExeFile.absolutePathString() + javaUnixFile.exists() && javaUnixFile.isExecutable() -> javaUnixFile.absolutePathString() else -> null } } private fun getRuntimeJava(): String? { - return System.getProperty("java.home")?.let { getJavaFromPath(File(it, "bin")) } + return System.getProperty("java.home")?.let { getJavaFromPath(Path(it).resolve("bin")) } } private fun getPossibleJava(): String? { - return System.getProperty("java.library.path")?.split(File.pathSeparatorChar) + return System.getProperty("java.library.path")?.split(pathSeparatorChar) .orEmpty() .asSequence() .mapNotNull { - val file = File(it) - if (file.absolutePath.contains("java") || file.absolutePath.contains("jdk")) { + val file = Path(it) + if (file.absolutePathString().contains("java") || file.absolutePathString().contains("jdk")) { if (file.name.equals("bin", true)) { file - } else File(file, "bin") + } else file.resolve("bin") } else null } .mapNotNull { getJavaFromPath(it) } @@ -117,7 +126,7 @@ class ServerService @Inject constructor( } } GlobalScope.launch(handler) { - val jarFile = File(userDataDir.also { it.mkdirs() }, "Tachidesk.jar") + val jarFile = userDataDir.also { it.createDirectories() }.resolve("Tachidesk.jar") if (!jarFile.exists()) { info { "Copying server to resources" } withIOContext { copyJar(jarFile) } @@ -147,7 +156,7 @@ class ServerService @Inject constructor( withIOContext { val reader: Reader - process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.absolutePath) + process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.absolutePathString()) .redirectErrorStream(true) .start() .also { diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt index 82285983..6b3a88e6 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt @@ -21,15 +21,16 @@ import io.ktor.client.statement.HttpResponse import io.ktor.http.ContentType import io.ktor.http.Headers import io.ktor.http.HttpHeaders -import java.io.File +import java.nio.file.Path import javax.inject.Inject +import kotlin.io.path.readBytes class BackupInteractionHandler @Inject constructor( client: Http, serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun importBackupFile(file: File, block: HttpRequestBuilder.() -> Unit = {}) = withIOContext { + suspend fun importBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}) = withIOContext { client.submitFormWithBinaryData( serverUrl + backupFileImportRequest(), formData = formData { @@ -45,7 +46,7 @@ class BackupInteractionHandler @Inject constructor( ) } - suspend fun validateBackupFile(file: File, block: HttpRequestBuilder.() -> Unit = {}) = withIOContext { + suspend fun validateBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}) = withIOContext { client.submitFormWithBinaryData( serverUrl + validateBackupFileRequest(), formData = formData { diff --git a/src/main/kotlin/ca/gosyer/ui/main/main.kt b/src/main/kotlin/ca/gosyer/ui/main/main.kt index f02950ba..d5e59623 100644 --- a/src/main/kotlin/ca/gosyer/ui/main/main.kt +++ b/src/main/kotlin/ca/gosyer/ui/main/main.kt @@ -52,13 +52,12 @@ import org.jetbrains.skiko.currentSystemTheme import toothpick.configuration.Configuration import toothpick.ktp.KTP import toothpick.ktp.extension.getInstance -import java.io.File import java.util.Locale import kotlin.system.exitProcess @OptIn(DelicateCoroutinesApi::class) suspend fun main() { - initializeLogger(File(userDataDir, "logging")) + initializeLogger(userDataDir.resolve(userDataDir, "logging")) if (BuildConfig.DEBUG) { System.setProperty("kotlinx.coroutines.debug", "on") diff --git a/src/main/kotlin/ca/gosyer/ui/settings/SettingsBackupScreen.kt b/src/main/kotlin/ca/gosyer/ui/settings/SettingsBackupScreen.kt index 6f159526..9b393736 100644 --- a/src/main/kotlin/ca/gosyer/ui/settings/SettingsBackupScreen.kt +++ b/src/main/kotlin/ca/gosyer/ui/settings/SettingsBackupScreen.kt @@ -47,8 +47,11 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import java.io.File +import java.nio.file.Path import javax.inject.Inject +import kotlin.io.path.absolutePathString +import kotlin.io.path.notExists +import kotlin.io.path.outputStream class SettingsBackupViewModel @Inject constructor( private val backupHandler: BackupInteractionHandler @@ -59,7 +62,7 @@ class SettingsBackupViewModel @Inject constructor( val restoringProgress = _restoringProgress.asStateFlow() private val _restoreStatus = MutableStateFlow(Status.Nothing) internal val restoreStatus = _restoreStatus.asStateFlow() - private val _missingSourceFlow = MutableSharedFlow>>() + private val _missingSourceFlow = MutableSharedFlow>>() val missingSourceFlow = _missingSourceFlow.asSharedFlow() private val _creating = MutableStateFlow(false) @@ -68,13 +71,13 @@ class SettingsBackupViewModel @Inject constructor( val creatingProgress = _creatingProgress.asStateFlow() private val _creatingStatus = MutableStateFlow(Status.Nothing) internal val creatingStatus = _creatingStatus.asStateFlow() - private val _createFlow = MutableSharedFlow Unit>>() + private val _createFlow = MutableSharedFlow Unit>>() val createFlow = _createFlow.asSharedFlow() - fun restoreFile(file: File?) { + fun restoreFile(file: Path?) { scope.launch { - if (file == null || !file.exists()) { - info { "Invalid file ${file?.absolutePath}" } + if (file == null || file.notExists()) { + info { "Invalid file ${file?.absolutePathString()}" } _restoreStatus.value = Status.Error _restoring.value = false } else { @@ -94,7 +97,7 @@ class SettingsBackupViewModel @Inject constructor( } } - fun restoreBackup(file: File) { + fun restoreBackup(file: Path) { scope.launch { _restoreStatus.value = Status.Nothing _restoringProgress.value = null @@ -189,7 +192,7 @@ fun SettingsBackupScreen(menuController: MenuController) { launch { vm.createFlow.collect { (filename, function) -> fileSaver(filename, "proto.gz") { - function(it.selectedFile) + function(it.selectedFile.toPath()) } } } @@ -207,7 +210,7 @@ fun SettingsBackupScreen(menuController: MenuController) { restoreStatus ) { filePicker("gz") { - vm.restoreFile(it.selectedFile) + vm.restoreFile(it.selectedFile.toPath()) } } PreferenceFile( diff --git a/src/main/kotlin/ca/gosyer/util/compose/Image.kt b/src/main/kotlin/ca/gosyer/util/compose/Image.kt index 019ffc75..39493763 100644 --- a/src/main/kotlin/ca/gosyer/util/compose/Image.kt +++ b/src/main/kotlin/ca/gosyer/util/compose/Image.kt @@ -13,11 +13,12 @@ import io.ktor.client.request.HttpRequestBuilder import io.ktor.client.request.get import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.jvm.javaio.copyTo -import org.jetbrains.skija.Image +import org.jetbrains.skia.Image import java.io.ByteArrayOutputStream -import java.io.File +import java.nio.file.Path +import kotlin.io.path.readBytes -fun imageFromFile(file: File): ImageBitmap { +fun imageFromFile(file: Path): ImageBitmap { return Image.makeFromEncoded(file.readBytes()).asImageBitmap() } diff --git a/src/main/kotlin/ca/gosyer/util/system/File.kt b/src/main/kotlin/ca/gosyer/util/system/File.kt index c81eaf78..b9921001 100644 --- a/src/main/kotlin/ca/gosyer/util/system/File.kt +++ b/src/main/kotlin/ca/gosyer/util/system/File.kt @@ -12,9 +12,12 @@ import kotlinx.coroutines.DelicateCoroutinesApi import mu.KotlinLogging import net.harawata.appdirs.AppDirs import net.harawata.appdirs.AppDirsFactory -import java.io.File +import java.nio.file.Path import javax.swing.JFileChooser import javax.swing.filechooser.FileNameExtensionFilter +import kotlin.io.path.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.exists private val logger = KotlinLogging.logger {} @@ -22,10 +25,10 @@ val appDirs: AppDirs by lazy { AppDirsFactory.getInstance() } -val userDataDir: File by lazy { - File(appDirs.getUserDataDir(BuildConfig.NAME, null, null)).also { +val userDataDir: Path by lazy { + Path(appDirs.getUserDataDir(BuildConfig.NAME, null, null)).also { if (!it.exists()) { - logger.info("Attempted to create app data dir, result: {}", it.mkdirs()) + logger.info("Attempted to create app data dir, result: {}", it.createDirectories()) } } } @@ -82,7 +85,7 @@ private fun fileChooser( fileFilter = FileNameExtensionFilter("${extensions.joinToString()} files", *extensions) } if (saving) { - selectedFile = File(defaultFileName) + selectedFile = Path(defaultFileName).toFile() } } .apply(builder)