Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix issue with SessionStorage not works #1016

Merged
merged 1 commit into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.itangcent.idea.plugin.utils

import com.itangcent.idea.plugin.utils.Storage.Companion.DEFAULT_GROUP
import com.itangcent.idea.plugin.utils.Storage.Companion.NULL
import java.util.*

/**
* Abstract implementation of [Storage]
*/
abstract class AbstractStorage : Storage {

override fun get(name: String?): Any? {
Expand Down Expand Up @@ -36,4 +41,93 @@ abstract class AbstractStorage : Storage {
clear(DEFAULT_GROUP)
}

override fun get(group: String?, name: String?): Any? {
return getCache(group ?: DEFAULT_GROUP)[name]
}

override fun set(group: String?, name: String?, value: Any?) {
useCache(group) { put(name ?: NULL, value) }
}

override fun pop(group: String?, name: String?): Any? {
return useQueue(group, name) { pollLast() }
}

override fun peek(group: String?, name: String?): Any? {
return useQueue(group, name) { peekLast() }
}

override fun push(group: String?, name: String?, value: Any?) {
useQueue(group, name) { addLast(value) }
}

override fun remove(group: String?, name: String) {
useCache(group) { remove(name) }
}

override fun keys(group: String?): Array<Any?> {
return getCache(group ?: DEFAULT_GROUP).keys.toTypedArray()
}

override fun clear(group: String?) {
useCache(group ?: DEFAULT_GROUP) { clear() }
}

/**
* Use cache and update cache
*/
@Synchronized
private fun <T : Any> useCache(group: String?, action: MutableMap<String, Any?>.() -> T?): T? {
val cache = getCache(group ?: DEFAULT_GROUP)
try {
return cache.action()
} finally {
onUpdate(group, cache)
}
}

/**
* Use queue and update cache
*/
@Synchronized
@Suppress("UNCHECKED_CAST")
private fun <T : Any> useQueue(group: String?, name: String?, action: LinkedList<Any?>.() -> T?): T? {
val cache = getCache(group ?: DEFAULT_GROUP)
var queue = cache[name ?: NULL]
try {
when (queue) {

is LinkedList<*> -> {
return action(queue as LinkedList<Any?>)
}

is Collection<*> -> {
queue = LinkedList<Any?>(queue)
cache[name ?: NULL] = queue
return action(queue)
}

else -> {
queue = LinkedList<Any?>()
cache[name ?: NULL] = queue
return action(queue)
}
}
} finally {
if (queue is Collection<*> && queue.isEmpty()) {
cache.remove(name ?: NULL)
}
onUpdate(group, cache)
}
}

/**
* Get cache of specified group
*/
protected abstract fun getCache(group: String): MutableMap<String, Any?>

/**
* called when cache is updated
*/
protected abstract fun onUpdate(group: String?, cache: MutableMap<String, Any?>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,124 +7,36 @@ import com.itangcent.common.utils.KV
import com.itangcent.idea.binder.DbBeanBinderFactory
import com.itangcent.idea.plugin.utils.Storage.Companion.DEFAULT_GROUP
import com.itangcent.intellij.file.LocalFileRepository
import java.util.*

/**
* Implementation of [Storage] based on local file
* The [LocalStorage] can be accessed cross projects.
*/
@Singleton
@ScriptTypeName("localStorage")
class LocalStorage : AbstractStorage() {

@Inject
private val localFileRepository: LocalFileRepository? = null

private var dbBeanBinderFactory: DbBeanBinderFactory<KV<Any?, Any?>>? = null

private fun getDbBeanBinderFactory(): DbBeanBinderFactory<KV<Any?, Any?>> {
if (dbBeanBinderFactory == null) {
synchronized(this) {
dbBeanBinderFactory = DbBeanBinderFactory(localFileRepository!!.getOrCreateFile(".api.local.storage.v1.1.db").path)
{ KV.create<Any?, Any?>() }
}
}
return this.dbBeanBinderFactory!!
}

override fun get(group: String?, name: String?): Any? {
return getDbBeanBinderFactory().getBeanBinder(group ?: DEFAULT_GROUP)
.tryRead()?.get(name)
}

override fun set(group: String?, name: String?, value: Any?) {
val beanBinder = getDbBeanBinderFactory().getBeanBinder(group ?: DEFAULT_GROUP)
val kv = beanBinder.read()
if (value == null) {
kv.remove(name)
} else {
kv[name] = value
}
beanBinder.save(kv)
}

override fun pop(group: String?, name: String?): Any? {
val beanBinder = getDbBeanBinderFactory().getBeanBinder(group ?: DEFAULT_GROUP)
val kv = beanBinder.read()
return tryQueue(kv, name)?.let {
val last = it.pollLast()
beanBinder.save(kv)
last
}
}

override fun peek(group: String?, name: String?): Any? {
val beanBinder = getDbBeanBinderFactory().getBeanBinder(group ?: DEFAULT_GROUP)
val kv = beanBinder.read()
return tryQueue(kv, name)?.peekLast()
private val dbBeanBinderFactory: DbBeanBinderFactory<KV<String, Any?>> by lazy {
DbBeanBinderFactory(localFileRepository!!.getOrCreateFile(".api.local.storage.v1.1.db").path)
{ KV.create() }
}

override fun push(group: String?, name: String?, value: Any?) {
val beanBinder = getDbBeanBinderFactory().getBeanBinder(group ?: DEFAULT_GROUP)
val kv = beanBinder.read()
val queue = queue(kv, name)
queue.addLast(value)
beanBinder.save(kv)
override fun clear(group: String?) {
dbBeanBinderFactory.deleteBinder(group ?: DEFAULT_GROUP)
}

@Suppress("UNCHECKED_CAST")
private fun queue(kv: KV<Any?, Any?>, name: String?): LinkedList<Any?> {
var queue = kv[name]
return when (queue) {
is LinkedList<*> -> {
queue as LinkedList<Any?>
}
//convert to linkedList
is List<*> -> {
val list = LinkedList(queue)
kv[name] = list
list
}
else -> {
queue = LinkedList<Any>()
kv[name ?: Storage.NULL] = queue
queue as LinkedList<Any?>
}
}
override fun getCache(group: String): MutableMap<String, Any?> {
return dbBeanBinderFactory.getBeanBinder(group).tryRead() ?: KV.create()
}


@Suppress("UNCHECKED_CAST")
private fun tryQueue(kv: KV<Any?, Any?>, name: String?): LinkedList<Any?>? {
val v = kv[name] ?: return null
return when (v) {
is LinkedList<*> -> {
v as LinkedList<Any?>
}
//convert to linkedList
is List<*> -> {
val list = LinkedList(v)
kv[name] = list
list
}
else -> null
override fun onUpdate(group: String?, cache: MutableMap<String, Any?>) {
if (cache.isEmpty()) {
dbBeanBinderFactory.deleteBinder(group ?: DEFAULT_GROUP)
} else {
dbBeanBinderFactory.getBeanBinder(group ?: DEFAULT_GROUP).save(cache as KV<String, Any?>)
}
}


override fun remove(group: String?, name: String) {
val beanBinder = getDbBeanBinderFactory().getBeanBinder(group ?: DEFAULT_GROUP)
val kv = beanBinder.tryRead() ?: return
kv.remove(name)
beanBinder.save(kv)
}

override fun keys(group: String?): Array<Any?> {
return getDbBeanBinderFactory().getBeanBinder(group ?: DEFAULT_GROUP)
.tryRead()
?.keys
?.toTypedArray()
?: emptyArray()
}

override fun clear(group: String?) {
getDbBeanBinderFactory().deleteBinder(group ?: DEFAULT_GROUP)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,27 @@ import com.google.inject.Singleton
import com.itangcent.annotation.script.ScriptTypeName
import com.itangcent.common.utils.KV
import com.itangcent.common.utils.sub
import com.itangcent.idea.plugin.utils.Storage.Companion.NULL
import java.util.*
import com.itangcent.idea.plugin.utils.Storage.Companion.DEFAULT_GROUP

/**
* Implementation of [Storage] based on memory
* The [SessionStorage] can only be accessed in the current action.
*/
@Singleton
@ScriptTypeName("session")
class SessionStorage : AbstractStorage() {

private val localKV = ThreadLocal.withInitial {
KV.create<String, Any?>()
}

private val kv: KV<String, Any?>
get() = localKV.get()

override fun get(group: String?, name: String?): Any? {
return kv.sub(group ?: NULL)[name]
}

override fun set(group: String?, name: String?, value: Any?) {
kv.sub(group ?: NULL)[name ?: NULL] = value
}

override fun pop(group: String?, name: String?): Any? {
return tryQueue(group, name)?.pollLast()
}

override fun peek(group: String?, name: String?): Any? {
return tryQueue(group, name)?.peekLast()
}
private val kv: KV<String, Any?> by lazy { KV.create() }

override fun push(group: String?, name: String?, value: Any?) {
queue(group, name).addLast(value)
override fun getCache(group: String): MutableMap<String, Any?> {
return kv.sub(group)
}

@Suppress("UNCHECKED_CAST")
private fun queue(group: String?, name: String?): LinkedList<Any?> {
val sub = kv.sub(group ?: NULL)
var queue = sub[name ?: NULL]
if (queue == null || queue !is LinkedList<*>) {
queue = LinkedList<Any>()
sub[name ?: NULL] = queue
override fun onUpdate(group: String?, cache: MutableMap<String, Any?>) {
if (cache.isEmpty()) {
kv.remove(group ?: DEFAULT_GROUP)
} else {
kv[group ?: DEFAULT_GROUP] = cache
}
return queue as LinkedList<Any?>
}

@Suppress("UNCHECKED_CAST")
private fun tryQueue(group: String?, name: String?): LinkedList<Any?>? {
val sub = kv.sub(group ?: NULL)
return sub[name ?: NULL] as? LinkedList<Any?>
}

override fun remove(group: String?, name: String) {
kv.sub(group ?: NULL).remove(name)
}

override fun keys(group: String?): Array<Any?> {
return kv.sub(group ?: NULL).keys.toTypedArray()
}

override fun clear(group: String?) {
kv.remove(group)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.itangcent.idea.plugin.utils

/**
* The [Storage] provides a way to store data.
*/
interface Storage {

fun get(name: String?): Any?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.itangcent.idea.utils

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow

/**
* Test case of [JacksonUtils]
Expand Down Expand Up @@ -41,13 +40,8 @@ internal class JacksonUtilsTest {

assertEquals("java.lang.Object,[\"java.lang.Object\",{}]", JacksonUtils.toJson(Any()))

assertDoesNotThrow {
JacksonUtils.fromJson<Any>("java.lang.Object,[\"java.lang.Object\",{}]").let {
assertNotNull(it)
//todo: make it true
//assertEquals("java.lang.Object", it!!::class.java)
}
}

assertNotNull(JacksonUtils.fromJson<Any>("java.lang.Object,[\"java.lang.Object\",{}]"))
}
}

Expand Down