Skip to content

Commit

Permalink
fix: fix issue with SessionStorage not works (#1016)
Browse files Browse the repository at this point in the history
  • Loading branch information
tangcent authored Aug 18, 2023
1 parent 5731dd1 commit 850caf7
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 164 deletions.
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

0 comments on commit 850caf7

Please sign in to comment.