diff --git a/README.md b/README.md
index ebae1f7..b2a6ef5 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Kotlin Commons
+# Kotlin/JVM Commons
[![Apache License 2](https://img.shields.io/badge/license-ASF2-purple.svg)](https://www.apache.org/licenses/LICENSE-2.0.txt)
[![](https://jitpack.io/v/sokomishalov/commons.svg)](https://jitpack.io/#sokomishalov/commons)
@@ -28,4 +28,10 @@ Add the dependency:
Available modules now are:
- core
+- logging
+- serialization
+- reactor
+- coroutines
+- distributed-locks
+- cache
- spring
diff --git a/commons-cache/pom.xml b/commons-cache/pom.xml
new file mode 100644
index 0000000..aee0dc1
--- /dev/null
+++ b/commons-cache/pom.xml
@@ -0,0 +1,170 @@
+
+
+ 4.0.0
+
+
+ ru.sokomishalov.commons
+ commons-parent
+ 1.1.0
+
+
+ commons-cache
+ 1.1.0
+
+
+
+
+
+
+ ru.sokomishalov.commons
+ commons-core
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-logging
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-serialization
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-coroutines
+ 1.1.0
+
+
+
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+ true
+
+
+
+ org.mongodb
+ mongodb-driver-reactivestreams
+ ${mongo-reactive.version}
+ true
+
+
+
+ io.lettuce
+ lettuce-core
+ ${lettuce.version}
+ true
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-test-junit
+ ${kotlin.version}
+ test
+
+
+
+ org.testcontainers
+ testcontainers
+ ${test-containers.version}
+ test
+
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+ test
+
+
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/test/kotlin
+ ${project.artifactId}
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+ ${java.version}
+
+ -Xuse-experimental=kotlin.Experimental
+
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ kapt
+
+ kapt
+
+
+
+ ${project.basedir}/src/main/kotlin
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ testCompile
+ test-compile
+
+ testCompile
+
+
+
+
+
+ ${java.version}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/CacheService.kt b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/CacheService.kt
new file mode 100644
index 0000000..7038193
--- /dev/null
+++ b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/CacheService.kt
@@ -0,0 +1,101 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("unused")
+
+package ru.sokomishalov.commons.cache
+
+import com.fasterxml.jackson.core.type.TypeReference
+import com.fasterxml.jackson.databind.JavaType
+import com.fasterxml.jackson.databind.ObjectMapper
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+import java.time.Duration
+import java.time.Duration.ofSeconds
+import java.time.Period
+import java.time.temporal.TemporalAmount
+
+/**
+ * @author sokomishalov
+ */
+interface CacheService {
+
+ // ---------------------------------------------------------------------------------------------------------------------------------
+
+ val mapper: ObjectMapper get() = OBJECT_MAPPER
+ val cacheName: String get() = "cache"
+
+ // ---------------------------------------------------------------------------------------------------------------------------------
+
+ suspend fun getRaw(key: String): ByteArray?
+ suspend fun putRaw(key: String, value: ByteArray)
+ suspend fun expire(key: String, ttl: TemporalAmount)
+ suspend fun delete(key: String)
+ suspend fun findKeys(glob: String): List
+
+ // ---------------------------------------------------------------------------------------------------------------------------------
+
+ suspend fun exists(key: String): Boolean {
+ return getRaw(key) != null
+ }
+
+ suspend fun getOne(key: String, clazz: Class): T? {
+ return getRaw(key)?.deserializeValue(clazz)
+ }
+
+ suspend fun getList(key: String, clazz: Class): List {
+ val collectionType = mapper.typeFactory.constructCollectionType(List::class.java, clazz)
+ return getRaw(key)?.deserializeValue(collectionType) ?: emptyList()
+ }
+
+ suspend fun getMap(key: String, clazz: Class): Map {
+ val mapType = mapper.typeFactory.constructMapType(HashMap::class.java, String::class.java, clazz)
+ return getRaw(key)?.deserializeValue(mapType) ?: emptyMap()
+ }
+
+ suspend fun getFromMap(key: String, mapKey: String, clazz: Class): T? {
+ return getMap(key, clazz)[mapKey]
+ }
+
+ suspend fun put(key: String, value: T, ttl: TemporalAmount = ofSeconds(-1)) {
+ putRaw(key, value.serializeValue())
+ when {
+ ttl is Duration && ttl.isNegative.not() -> expire(key, ttl)
+ ttl is Period && ttl.isNegative.not() -> expire(key, ttl)
+ }
+ }
+
+ suspend fun findAllKeys(): List {
+ return findKeys("*")
+ }
+
+ suspend fun find(pattern: String, clazz: Class): List {
+ return findKeys(pattern).mapNotNull { getRaw(it)?.deserializeValue(clazz) }
+ }
+
+ suspend fun delete(keys: Iterable) {
+ keys.forEach { delete(it) }
+ }
+
+ suspend fun deleteAll() {
+ findAllKeys().forEach { delete(it) }
+ }
+
+ // ---------------------------------------------------------------------------------------------------------------------------------
+
+ fun T.serializeValue(): ByteArray = mapper.writeValueAsBytes(this)
+ fun ByteArray.deserializeValue(clazz: Class): T = mapper.readValue(this, clazz)
+ fun ByteArray.deserializeValue(typeRef: TypeReference): T = mapper.readValue(this, typeRef)
+ fun ByteArray.deserializeValue(javaType: JavaType): T = mapper.readValue(this, javaType)
+}
\ No newline at end of file
diff --git a/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/CacheServiceExtensions.kt b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/CacheServiceExtensions.kt
new file mode 100644
index 0000000..3d868e8
--- /dev/null
+++ b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/CacheServiceExtensions.kt
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache
+
+/**
+ * @author sokomishalov
+ */
+suspend inline fun CacheService.getOne(key: String, orElse: () -> T? = { null }): T? = getOne(key, T::class.java) ?: orElse()
+
+suspend inline fun CacheService.getList(key: String, ifEmpty: () -> List = { emptyList() }): List = getList(key, T::class.java).ifEmpty { ifEmpty() }
+
+suspend inline fun CacheService.getMap(key: String, ifEmpty: () -> Map = { emptyMap() }): Map = getMap(key, T::class.java).ifEmpty { ifEmpty() }
+
+suspend inline fun CacheService.getFromMap(key: String, mapKey: String, orElse: () -> T? = { null }): T? = getFromMap(key, mapKey, T::class.java) ?: orElse()
+
+suspend inline fun CacheService.find(glob: String, ifEmpty: () -> List = { emptyList() }): List = find(glob, T::class.java).ifEmpty { ifEmpty() }
\ No newline at end of file
diff --git a/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/inmemory/ConcurrentMapCacheService.kt b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/inmemory/ConcurrentMapCacheService.kt
new file mode 100644
index 0000000..95890bb
--- /dev/null
+++ b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/inmemory/ConcurrentMapCacheService.kt
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("unused")
+
+package ru.sokomishalov.commons.cache.inmemory
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.core.common.unit
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+import ru.sokomishalov.commons.core.string.convertGlobToRegex
+import java.time.temporal.TemporalAmount
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.ConcurrentMap
+
+/**
+ * @author sokomishalov
+ */
+class ConcurrentMapCacheService(
+ override val cacheName: String = "cache:",
+ override val mapper: ObjectMapper = OBJECT_MAPPER,
+ private val map: ConcurrentMap = ConcurrentHashMap()
+) : CacheService {
+
+ companion object : Loggable
+
+ private fun String.addPrefix(): String = "${cacheName}${this}"
+ private fun String.removePrefix(): String = removePrefix(cacheName)
+
+ override suspend fun getRaw(key: String): ByteArray? = map[key.addPrefix()]
+
+ override suspend fun putRaw(key: String, value: ByteArray) = map.put(key.addPrefix(), value).unit()
+
+ override suspend fun delete(key: String) = map.remove(key.addPrefix()).unit()
+
+ override suspend fun expire(key: String, ttl: TemporalAmount) = logWarn("expire() is unsupported")
+
+ override suspend fun findKeys(glob: String): List = map.keys.map { it.removePrefix() }.filter { glob.convertGlobToRegex().matches(it) }
+
+
+ override suspend fun deleteAll() = map.clear()
+
+ // override some methods for better performance
+}
\ No newline at end of file
diff --git a/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/mongo/MongoCacheService.kt b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/mongo/MongoCacheService.kt
new file mode 100644
index 0000000..99986d1
--- /dev/null
+++ b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/mongo/MongoCacheService.kt
@@ -0,0 +1,109 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("DEPRECATION")
+
+package ru.sokomishalov.commons.cache.mongo
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.mongodb.ConnectionString
+import com.mongodb.client.model.Filters.eq
+import com.mongodb.client.model.FindOneAndUpdateOptions
+import com.mongodb.client.model.Updates.set
+import com.mongodb.reactivestreams.client.MongoClient
+import com.mongodb.reactivestreams.client.MongoClients
+import com.mongodb.reactivestreams.client.MongoCollection
+import kotlinx.coroutines.reactive.awaitFirstOrNull
+import org.bson.Document
+import org.bson.conversions.Bson
+import reactor.core.publisher.toFlux
+import reactor.core.publisher.toMono
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.core.consts.LOCALHOST
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.core.reactor.await
+import ru.sokomishalov.commons.core.reactor.awaitUnit
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+import ru.sokomishalov.commons.core.string.convertGlobToRegex
+import java.time.temporal.TemporalAmount
+import java.util.Base64.getDecoder
+import java.util.Base64.getEncoder
+
+/**
+ * @author sokomishalov
+ */
+class MongoCacheService(
+ override val cacheName: String = "cache",
+ override val mapper: ObjectMapper = OBJECT_MAPPER,
+ private val host: String = LOCALHOST,
+ private val port: Int = 27017,
+ private val databaseName: String = "lockDB",
+ private val client: MongoClient = MongoClients.create(ConnectionString("mongodb://${host}:${port}/${databaseName}"))
+) : CacheService {
+
+ companion object : Loggable {
+ private const val ID_FIELD = "_id"
+ private const val RAW_VALUE_FIELD = "rawValue"
+ }
+
+ private val collection: MongoCollection get() = client.getDatabase(databaseName).getCollection(cacheName)
+
+ override suspend fun getRaw(key: String): ByteArray? {
+ return collection
+ .find(key.buildKeyBson())
+ .toMono()
+ .await()
+ ?.get(RAW_VALUE_FIELD)
+ ?.let { (it as String).decodeBase64() }
+ }
+
+ override suspend fun putRaw(key: String, value: ByteArray) {
+ collection
+ .findOneAndUpdate(key.buildKeyBson(), value.buildValueBson(), FindOneAndUpdateOptions().upsert(true))
+ .toMono()
+ .awaitUnit()
+ }
+
+ override suspend fun expire(key: String, ttl: TemporalAmount) {
+ logWarn("expire() is unsupported")
+ }
+
+ override suspend fun delete(key: String) {
+ collection
+ .deleteOne(key.buildKeyBson())
+ .toMono()
+ .awaitUnit()
+ }
+
+ override suspend fun findKeys(glob: String): List {
+ return collection
+ .find(eq(ID_FIELD, glob.convertGlobToRegex().toPattern()))
+ .toFlux()
+ .await()
+ .mapNotNull { it?.get(ID_FIELD) as String? }
+ }
+
+ override suspend fun deleteAll() {
+ collection.drop().awaitFirstOrNull()
+ }
+
+ private fun String.buildKeyBson(): Bson = eq(ID_FIELD, this)
+ private fun ByteArray.buildValueBson(): Bson = set(RAW_VALUE_FIELD, this.encodeBase64())
+
+ private fun ByteArray.encodeBase64() = getEncoder().encodeToString(this)
+ private fun String.decodeBase64() = getDecoder().decode(this)
+
+ // override some methods for better performance
+}
\ No newline at end of file
diff --git a/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/redis/RedisCacheService.kt b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/redis/RedisCacheService.kt
new file mode 100644
index 0000000..9f51a0e
--- /dev/null
+++ b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/redis/RedisCacheService.kt
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.redis
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import io.lettuce.core.RedisClient
+import io.lettuce.core.RedisURI
+import io.lettuce.core.ScanArgs
+import io.lettuce.core.api.StatefulRedisConnection
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.core.consts.LOCALHOST
+import ru.sokomishalov.commons.core.reactor.await
+import ru.sokomishalov.commons.core.reactor.awaitUnit
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+import java.time.temporal.ChronoUnit.SECONDS
+import java.time.temporal.TemporalAmount
+import kotlin.Long.Companion.MAX_VALUE
+
+
+/**
+ * @author sokomishalov
+ */
+open class RedisCacheService(
+ override val cacheName: String = "cache",
+ override val mapper: ObjectMapper = OBJECT_MAPPER,
+ private val host: String = LOCALHOST,
+ private val port: Int = 6379,
+ private val client: RedisClient = RedisClient.create(RedisURI.create(host, port)),
+ private val connection: StatefulRedisConnection = client.connect(StringByteArrayCodec())
+) : CacheService {
+
+ private fun String.addPrefix(): String = "${cacheName}${this}"
+ private fun String.removePrefix(): String = removePrefix(cacheName)
+
+ override suspend fun getRaw(key: String): ByteArray? = connection.reactive().get(key.addPrefix()).await()
+
+ override suspend fun putRaw(key: String, value: ByteArray) = connection.reactive().set(key.addPrefix(), value).awaitUnit()
+
+ override suspend fun delete(key: String) = connection.reactive().del(key.addPrefix()).awaitUnit()
+
+ override suspend fun expire(key: String, ttl: TemporalAmount) = connection.reactive().expire(key.addPrefix(), ttl.get(SECONDS)).awaitUnit()
+
+ override suspend fun findKeys(glob: String): List {
+ val scanArgs = ScanArgs().match(glob).limit(MAX_VALUE)
+ return connection.reactive().scan(scanArgs).await()?.keys?.map { it.removePrefix() } ?: emptyList()
+ }
+
+ // override some methods for better performance
+}
\ No newline at end of file
diff --git a/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/redis/StringByteArrayCodec.kt b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/redis/StringByteArrayCodec.kt
new file mode 100644
index 0000000..3ed710f
--- /dev/null
+++ b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/redis/StringByteArrayCodec.kt
@@ -0,0 +1,20 @@
+package ru.sokomishalov.commons.cache.redis
+
+import io.lettuce.core.codec.ByteArrayCodec
+import io.lettuce.core.codec.RedisCodec
+import io.lettuce.core.codec.StringCodec
+import java.nio.ByteBuffer
+import kotlin.text.Charsets.UTF_8
+
+/**
+ * @author sokomishalov
+ */
+internal class StringByteArrayCodec(
+ private val keyCodec: RedisCodec = StringCodec(UTF_8),
+ private val valueCodec: RedisCodec = ByteArrayCodec()
+) : RedisCodec {
+ override fun encodeKey(key: String?): ByteBuffer = keyCodec.encodeKey(key)
+ override fun decodeKey(bytes: ByteBuffer?): String = keyCodec.decodeKey(bytes)
+ override fun encodeValue(value: ByteArray?): ByteBuffer = valueCodec.encodeValue(value)
+ override fun decodeValue(bytes: ByteBuffer?): ByteArray = valueCodec.decodeValue(bytes)
+}
\ No newline at end of file
diff --git a/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/spring/SpringCacheService.kt b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/spring/SpringCacheService.kt
new file mode 100644
index 0000000..0e95916
--- /dev/null
+++ b/commons-cache/src/main/kotlin/ru/sokomishalov/commons/cache/spring/SpringCacheService.kt
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.spring
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.core.common.unit
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+import java.time.temporal.TemporalAmount
+import org.springframework.cache.CacheManager as SpringCacheManager
+import org.springframework.cache.concurrent.ConcurrentMapCacheManager as SpringConcurrentMapCacheManager
+
+/**
+ * @author sokomishalov
+ */
+class SpringCacheService(
+ override val cacheName: String = "cache",
+ override val mapper: ObjectMapper = OBJECT_MAPPER,
+ private val cacheManager: SpringCacheManager = SpringConcurrentMapCacheManager(cacheName)
+) : CacheService {
+
+ companion object : Loggable
+
+ override suspend fun getRaw(key: String): ByteArray? = cacheManager.getCache(cacheName)?.get(key)?.get() as ByteArray?
+
+ override suspend fun putRaw(key: String, value: ByteArray) = cacheManager.getCache(cacheName)?.put(key, value).unit()
+
+ override suspend fun expire(key: String, ttl: TemporalAmount) = logWarn("expire() is unsupported")
+
+ override suspend fun delete(key: String) = cacheManager.getCache(cacheName)?.evict(key).unit()
+
+ override suspend fun findKeys(glob: String): List {
+ logWarn("findKeys() is unsupported")
+ return emptyList()
+ }
+
+ override suspend fun deleteAll() = cacheManager.getCache(cacheName)?.clear().unit()
+
+ // override some methods for better performance
+}
\ No newline at end of file
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/AbstractCacheServiceTest.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/AbstractCacheServiceTest.kt
new file mode 100644
index 0000000..eff9efd
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/AbstractCacheServiceTest.kt
@@ -0,0 +1,244 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache
+
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import ru.sokomishalov.commons.cache.util.DummyModel
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.core.random.randomString
+import java.util.UUID.randomUUID
+import kotlin.Long.Companion.MAX_VALUE
+import kotlin.Long.Companion.MIN_VALUE
+
+abstract class AbstractCacheServiceTest {
+
+ companion object : Loggable {
+ private const val CACHE_KEY = "key"
+ private const val CACHE_VALUE = "value"
+ }
+
+ protected abstract val cacheService: CacheService
+
+ @After
+ open fun tearDown() {
+ runBlocking {
+ cacheService.deleteAll()
+ }
+ }
+
+ @Test
+ open fun `Put strings`() {
+ runBlocking {
+ val data = listOf(
+ "key1" to "value1",
+ "key2" to "value2",
+ "key3" to "value3"
+ )
+ data.forEach { (key, value) ->
+ cacheService.put(key, value)
+ }
+
+ data.shuffled().forEach { (key, value) ->
+ assertEquals(value, cacheService.getOne(key))
+ }
+ }
+ }
+
+ @Test
+ open fun `Put and delete strings`() {
+ runBlocking {
+ cacheService.put(CACHE_KEY, CACHE_VALUE)
+ assertEquals(CACHE_VALUE, cacheService.getOne(CACHE_KEY))
+
+ cacheService.delete(CACHE_KEY)
+ assertNull(cacheService.getOne(CACHE_KEY))
+ }
+ }
+
+ @Test
+ open fun `Put and replace strings`() {
+ runBlocking {
+ cacheService.put(CACHE_KEY, CACHE_VALUE)
+ assertEquals(CACHE_VALUE, cacheService.getOne(CACHE_KEY))
+
+ val newValue = "newValue"
+ cacheService.put(CACHE_KEY, newValue)
+ assertEquals(newValue, cacheService.getOne(CACHE_KEY))
+ }
+ }
+
+ @Test
+ open fun `Put objects`() {
+ runBlocking {
+ val data = mutableListOf(
+ "key1" to DummyModel(MIN_VALUE, "firstDummy"),
+ "key2" to DummyModel(0, "secondDummy"),
+ "key3" to DummyModel(MAX_VALUE, "thirdDummy")
+ )
+ data.forEach { (key, value) ->
+ cacheService.put(key, value)
+ }
+
+ data.shuffled().forEach { (key, value) ->
+ assertEquals(value, cacheService.getOne(key))
+ }
+ }
+ }
+
+ @Test
+ open fun `Put and delete objects`() {
+ runBlocking {
+ val dummy = DummyModel(1, "DummyModel")
+ cacheService.put(CACHE_KEY, dummy)
+ assertEquals(dummy, cacheService.getOne(CACHE_KEY))
+
+ cacheService.delete(CACHE_KEY)
+ assertNull(cacheService.getOne(CACHE_KEY))
+ }
+ }
+
+ @Test
+ open fun `Put and replace object`() {
+ runBlocking {
+ val firstDummy = DummyModel(MIN_VALUE, "firstDummy")
+ cacheService.put(CACHE_KEY, firstDummy)
+ assertEquals(firstDummy, cacheService.getOne(CACHE_KEY))
+
+ val secondDummy = DummyModel(MAX_VALUE, "secondDummy")
+ cacheService.put(CACHE_KEY, secondDummy)
+ assertEquals(secondDummy, cacheService.getOne(CACHE_KEY))
+ }
+ }
+
+ @Test
+ open fun `Get not existing value`() {
+ runBlocking {
+ assertNull(cacheService.getOne(randomString(10)))
+ assertEquals("kek", cacheService.getOne(randomString(10)) { "kek" })
+ }
+ }
+
+ @Test
+ open fun `Put string list`() {
+ runBlocking {
+ val data = listOf("value1", "value2", "value3")
+ cacheService.put(CACHE_KEY, data)
+
+ val result = cacheService.getList(CACHE_KEY)
+ assertEquals(data, result.sorted())
+ }
+ }
+
+ @Test
+ open fun `Put object list`() {
+ runBlocking {
+ val data = mutableListOf(
+ DummyModel(1, "aFirstDummy"),
+ DummyModel(2, "bSecondDummy"),
+ DummyModel(3, "cThirdDummy")
+ )
+ cacheService.put(CACHE_KEY, data)
+
+ val result = cacheService.getList(CACHE_KEY)
+ assertEquals(data, result.sorted())
+ }
+ }
+
+ @Test
+ open fun `Put empty list`() {
+ runBlocking {
+ cacheService.put(CACHE_KEY, emptyList())
+ assertEquals(emptyList(), cacheService.getList(CACHE_KEY))
+ }
+ }
+
+ @Test
+ open fun `Get non existing list`() {
+ runBlocking {
+ assertEquals(emptyList(), cacheService.getList(CACHE_KEY))
+ assertEquals(listOf("kek"), cacheService.getList(CACHE_KEY) { listOf("kek") })
+ }
+ }
+
+ @Test
+ open fun `Put map`() {
+ runBlocking {
+ val data = mapOf(
+ "key1" to "value1",
+ "key2" to "value2",
+ "key3" to "value3"
+ )
+ cacheService.put(CACHE_KEY, data)
+
+ assertEquals(data, cacheService.getMap(CACHE_KEY))
+ data.forEach { (key, value) ->
+ assertEquals(value, cacheService.getFromMap(CACHE_KEY, key))
+ }
+ }
+ }
+
+ @Test
+ open fun `Put empty map`() {
+ runBlocking {
+ cacheService.put(CACHE_KEY, emptyMap())
+ assertEquals(emptyMap(), cacheService.getMap(CACHE_KEY))
+ }
+ }
+
+ @Test
+ open fun `Get non existing map`() {
+ runBlocking {
+ assertEquals(emptyMap(), cacheService.getMap(randomUUID().toString()))
+ }
+ }
+
+ @Test
+ open fun `Get from not existing map`() {
+ runBlocking {
+ assertNull(cacheService.getFromMap(randomUUID().toString(), randomUUID().toString()))
+ assertEquals("kek", cacheService.getFromMap(randomUUID().toString(), randomUUID().toString()) { "kek" })
+ }
+ }
+
+ @Test
+ open fun `Test find by glob pattern`() {
+ runBlocking {
+ val data = mapOf(
+ "kekpek1" to "kekpek1",
+ "kekpek2" to "kekpek2",
+ "cheburek1" to "cheburek1",
+ "cheburek2" to "cheburek2"
+ )
+ data.forEach { (k, v) -> cacheService.put(k, v) }
+
+ val all = cacheService.find("*")
+ assertEquals(4, all.size)
+
+ val found = cacheService.find("*kek*")
+ assertEquals(2, found.size)
+
+ val notFound = cacheService.find("*hehmda")
+ assertEquals(0, notFound.size)
+
+ val notFoundButElse = cacheService.find("*hehmda") { listOf("lol") }
+ assertEquals(1, notFoundButElse.size)
+ }
+ }
+}
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/inmemory/ConcurrentMapCacheServiceTest.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/inmemory/ConcurrentMapCacheServiceTest.kt
new file mode 100644
index 0000000..75f8644
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/inmemory/ConcurrentMapCacheServiceTest.kt
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.inmemory
+
+import ru.sokomishalov.commons.cache.AbstractCacheServiceTest
+import ru.sokomishalov.commons.cache.CacheService
+
+
+/**
+ * @author sokomishalov
+ */
+class ConcurrentMapCacheServiceTest : AbstractCacheServiceTest() {
+
+ override val cacheService: CacheService by lazy { ConcurrentMapCacheService() }
+
+}
\ No newline at end of file
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/mongo/MongoCacheServiceTest.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/mongo/MongoCacheServiceTest.kt
new file mode 100644
index 0000000..3a085ee
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/mongo/MongoCacheServiceTest.kt
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.mongo
+
+import org.junit.AfterClass
+import org.junit.ClassRule
+import ru.sokomishalov.commons.cache.AbstractCacheServiceTest
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.cache.util.MongoTestContainer
+import ru.sokomishalov.commons.cache.util.createDefaultMongoContainer
+import ru.sokomishalov.commons.cache.util.createReactiveMongoClient
+import ru.sokomishalov.commons.core.log.Loggable
+
+/**
+ * @author sokomishalov
+ */
+class MongoCacheServiceTest : AbstractCacheServiceTest() {
+
+ companion object : Loggable {
+ @get:ClassRule
+ val mongo: MongoTestContainer = createDefaultMongoContainer()
+
+ @AfterClass
+ @JvmStatic
+ fun stop() = mongo.stop()
+ }
+
+ override val cacheService: CacheService by lazy {
+ mongo.start()
+ logInfo(mongo.logs)
+ MongoCacheService(client = mongo.createReactiveMongoClient())
+ }
+}
\ No newline at end of file
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/redis/RedisCacheServiceTest.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/redis/RedisCacheServiceTest.kt
new file mode 100644
index 0000000..96f9c4c
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/redis/RedisCacheServiceTest.kt
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.redis
+
+import org.junit.AfterClass
+import org.junit.ClassRule
+import ru.sokomishalov.commons.cache.AbstractCacheServiceTest
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.cache.util.RedisTestContainer
+import ru.sokomishalov.commons.cache.util.createDefaultRedisContainer
+import ru.sokomishalov.commons.cache.util.createRedisClient
+import ru.sokomishalov.commons.core.log.Loggable
+
+/**
+ * @author sokomishalov
+ */
+class RedisCacheServiceTest : AbstractCacheServiceTest() {
+
+ companion object : Loggable {
+ @get:ClassRule
+ val redis: RedisTestContainer = createDefaultRedisContainer()
+
+ @AfterClass
+ @JvmStatic
+ fun stop() = redis.stop()
+ }
+
+ override val cacheService: CacheService by lazy {
+ redis.start()
+ logInfo(redis.logs)
+ RedisCacheService(client = redis.createRedisClient())
+ }
+}
\ No newline at end of file
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/spring/SpringCacheServiceTest.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/spring/SpringCacheServiceTest.kt
new file mode 100644
index 0000000..a82e4e7
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/spring/SpringCacheServiceTest.kt
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.spring
+
+import org.junit.Assert.assertTrue
+import ru.sokomishalov.commons.cache.AbstractCacheServiceTest
+import ru.sokomishalov.commons.cache.CacheService
+
+/**
+ * @author sokomishalov
+ */
+class SpringCacheServiceTest : AbstractCacheServiceTest() {
+
+ override val cacheService: CacheService by lazy { SpringCacheService(cacheName = "cache") }
+
+ // not realized yet
+ override fun `Test find by glob pattern`() = assertTrue(true)
+}
\ No newline at end of file
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/DummyModel.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/DummyModel.kt
new file mode 100644
index 0000000..7b8388f
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/DummyModel.kt
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.util
+
+import ru.sokomishalov.commons.core.random.randomString
+import java.lang.System.currentTimeMillis
+
+data class DummyModel(
+ val id: Long = 0,
+ val name: String? = randomString(10),
+ val createdAt: Long = currentTimeMillis()
+) : Comparable {
+
+ override fun compareTo(other: DummyModel): Int = id.compareTo(other.id)
+}
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/MongoTestContainer.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/MongoTestContainer.kt
new file mode 100644
index 0000000..35f7810
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/MongoTestContainer.kt
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.util
+
+import com.mongodb.ConnectionString
+import com.mongodb.reactivestreams.client.MongoClient
+import com.mongodb.reactivestreams.client.MongoClients
+import org.testcontainers.containers.GenericContainer
+
+/**
+ * @author sokomishalov
+ */
+
+class MongoTestContainer : GenericContainer("mvertes/alpine-mongo")
+
+internal fun createDefaultMongoContainer(): MongoTestContainer {
+ return MongoTestContainer().apply {
+ withReuse(true)
+ withExposedPorts(27017)
+ }
+}
+
+fun MongoTestContainer.createReactiveMongoClient(): MongoClient {
+ return MongoClients.create(ConnectionString("mongodb://${containerIpAddress}:${firstMappedPort}"))
+}
\ No newline at end of file
diff --git a/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/RedisTestContainer.kt b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/RedisTestContainer.kt
new file mode 100644
index 0000000..06ecde3
--- /dev/null
+++ b/commons-cache/src/test/kotlin/ru/sokomishalov/commons/cache/util/RedisTestContainer.kt
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.cache.util
+
+import io.lettuce.core.RedisClient
+import io.lettuce.core.RedisURI
+import org.testcontainers.containers.GenericContainer
+import java.time.Duration
+
+/**
+ * @author sokomishalov
+ */
+
+class RedisTestContainer : GenericContainer("redis:alpine")
+
+internal fun createDefaultRedisContainer(): RedisTestContainer {
+ return RedisTestContainer().apply {
+ withReuse(true)
+ withExposedPorts(6379)
+ }
+}
+
+fun RedisTestContainer.createRedisClient(timeout: Duration = Duration.ofSeconds(1)): RedisClient {
+ return RedisClient.create(RedisURI(containerIpAddress, firstMappedPort, timeout))
+}
\ No newline at end of file
diff --git a/commons-cache/src/test/resources/tarantool/app.lua b/commons-cache/src/test/resources/tarantool/app.lua
new file mode 100644
index 0000000..572e9a1
--- /dev/null
+++ b/commons-cache/src/test/resources/tarantool/app.lua
@@ -0,0 +1,30 @@
+--[[
+
+ Copyright 2019-2019 the original author or authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+]]
+box.cfg {}
+
+box.once("schema", function()
+ box.schema.space.create('cache')
+ box.space.cache:format({
+ { name = 'key', type = 'string' },
+ { name = 'value', type = 'string' }
+ })
+ box.space.cache:create_index('primary', {
+ type = 'hash',
+ parts = { 'key' }
+ })
+end)
\ No newline at end of file
diff --git a/commons-core/pom.xml b/commons-core/pom.xml
index bebfd83..4023e32 100644
--- a/commons-core/pom.xml
+++ b/commons-core/pom.xml
@@ -7,29 +7,11 @@
ru.sokomishalov.commonscommons-parent
- 1.0.29
+ 1.1.0commons-core
- 1.0.29
-
-
- 1.3.61
- 1.3.2
- 2.10.1
- 3.9
- 2.6
- 4.4
- 28.1-jre
- 3.3.1.RELEASE
- 0.9.2.RELEASE
- 3.3.1.RELEASE
- 1.7.29
- 1.2.3
- 4.1.0
- 1.12.1
-
-
+ 1.1.0
@@ -40,91 +22,6 @@
kotlin-stdlib-jdk8${kotlin.version}
-
- org.jetbrains.kotlin
- kotlin-reflect
- ${kotlin.version}
-
-
- org.jetbrains.kotlinx
- kotlinx-coroutines-core
- ${kotlinx.version}
-
-
- org.jetbrains.kotlinx
- kotlinx-coroutines-reactor
- ${kotlinx.version}
-
-
- org.jetbrains.kotlinx
- kotlinx-coroutines-reactive
- ${kotlinx.version}
-
-
- org.jetbrains.kotlinx
- kotlinx-coroutines-jdk8
- ${kotlinx.version}
-
-
-
- io.projectreactor
- reactor-core
- ${reactor.version}
-
-
- io.projectreactor.netty
- reactor-netty
- ${reactor-netty.version}
-
-
-
- com.fasterxml.jackson.core
- jackson-databind
- ${jackson.version}
-
-
- com.fasterxml.jackson.module
- jackson-module-kotlin
- ${jackson.version}
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jdk8
- ${jackson.version}
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
- ${jackson.version}
-
-
-
- org.apache.commons
- commons-lang3
- ${commons-lang.version}
-
-
-
- org.slf4j
- slf4j-api
- ${slf4j.version}
-
-
-
-
-
- org.jeasy
- easy-random-core
- ${easy-random.version}
- true
-
-
-
- org.jsoup
- jsoup
- ${jsoup.version}
- true
-
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/html/ScrapeUtils.kt b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/html/ScrapeUtils.kt
deleted file mode 100644
index 00c0db2..0000000
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/html/ScrapeUtils.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:Suppress("unused")
-
-package ru.sokomishalov.commons.core.html
-
-import io.netty.handler.codec.http.HttpHeaderNames.USER_AGENT
-import org.jsoup.Jsoup.clean
-import org.jsoup.Jsoup.parse
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-import org.jsoup.safety.Whitelist
-import ru.sokomishalov.commons.core.http.REACTIVE_NETTY_HTTP_CLIENT
-import ru.sokomishalov.commons.core.reactor.awaitStrict
-import ru.sokomishalov.commons.core.string.isNotNullOrBlank
-import java.nio.charset.StandardCharsets.UTF_8
-
-
-/**
- * @author sokomishalov
- */
-suspend fun getWebPage(
- url: String,
- userAgent: String? = null
-): Document {
- return REACTIVE_NETTY_HTTP_CLIENT
- .headers {
- if (userAgent.isNotNullOrBlank()) {
- it.set(USER_AGENT, userAgent)
- }
- }
- .get()
- .uri(url)
- .responseContent()
- .aggregate()
- .asString(UTF_8)
- .awaitStrict()
- .let { parse(it) }
-}
-
-fun Element.getSingleElementByClass(name: String): Element {
- return getElementsByClass(name).first()
-}
-
-fun Element.getSingleElementByTag(name: String): Element {
- return getElementsByTag(name).first()
-}
-
-fun Element.getImageBackgroundUrl(): String {
- val style = attr("style")
- return style.substring(style.indexOf("http"), style.indexOf(")"))
-}
-
-fun Element.fixText(): String {
- return clean(toString(), Whitelist.simpleText())
-}
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/images/ImageUtils.kt b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/images/ImageUtils.kt
deleted file mode 100644
index fe06f86..0000000
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/images/ImageUtils.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:Suppress("unused", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
-
-package ru.sokomishalov.commons.core.images
-
-import ru.sokomishalov.commons.core.http.REACTIVE_NETTY_HTTP_CLIENT
-import ru.sokomishalov.commons.core.reactor.awaitStrict
-import java.awt.image.BufferedImage
-import java.io.ByteArrayInputStream
-import javax.imageio.ImageIO
-
-
-fun ByteArray.toBufferedImage(): BufferedImage {
- return ByteArrayInputStream(this).use {
- ImageIO.read(it)
- }
-}
-
-suspend fun getImageByteArray(url: String?, orElse: ByteArray = ByteArray(0)): ByteArray {
- return runCatching {
- REACTIVE_NETTY_HTTP_CLIENT
- .get()
- .uri(url)
- .responseContent()
- .aggregate()
- .asByteArray()
- .awaitStrict()
- }.getOrElse {
- orElse
- }
-}
-
-suspend fun getImageDimensions(url: String?, default: Pair = 1 to 1): Pair {
- return runCatching {
- val imageByteArray = getImageByteArray(url)
- imageByteArray.toBufferedImage().run { width to height }
- }.getOrElse {
- default
- }
-}
-
-suspend fun getImageAspectRatio(url: String?): Double {
- return getImageDimensions(url).run { first.toDouble().div(second) }
-}
-
-suspend fun checkImageUrl(url: String?): Boolean {
- return runCatching {
- getImageByteArray(url).toBufferedImage()
- true
- }.getOrElse {
- false
- }
-}
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/random/JEasyRandomUtils.kt b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/random/JEasyRandomUtils.kt
deleted file mode 100644
index d9c8434..0000000
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/random/JEasyRandomUtils.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:Suppress("unused")
-
-package ru.sokomishalov.commons.core.random
-
-import org.jeasy.random.EasyRandom
-import org.jeasy.random.EasyRandomParameters
-import java.nio.charset.StandardCharsets.UTF_8
-
-/**
- * @author sokomishalov
- */
-val EASY_RANDOM_PARAMS: EasyRandomParameters = EasyRandomParameters()
- .seed((0L..1000L).random())
- .objectPoolSize(100)
- .randomizationDepth(3)
- .charset(UTF_8)
- .stringLengthRange(5, 20)
- .collectionSizeRange(0, 10)
- .scanClasspathForConcreteTypes(true)
- .overrideDefaultInitialization(false)
- .ignoreRandomizationErrors(true)
-
-
-inline fun randomPojo(params: EasyRandomParameters = EASY_RANDOM_PARAMS): T {
- return EasyRandom(params).nextObject(T::class.java)
-}
-
-inline fun randomPojoSequence(params: EasyRandomParameters = EASY_RANDOM_PARAMS) = sequence {
- while (true) {
- yield(randomPojo(params))
- }
-}
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/random/RandomStringUtils.kt b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/random/RandomStringUtils.kt
index 137dfad..7a0a501 100644
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/random/RandomStringUtils.kt
+++ b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/random/RandomStringUtils.kt
@@ -17,14 +17,29 @@
package ru.sokomishalov.commons.core.random
-import org.apache.commons.lang3.RandomStringUtils.random
-
/**
* @author sokomishalov
*/
+private val LOWER_CASE_ALPHABET: CharRange = ('a'..'z')
+private val UPPER_CASE_ALPHABET: CharRange = ('A'..'Z')
+private val NUMBERS: CharRange = ('0'..'9')
+
fun randomString(
length: Int = 20,
useLetters: Boolean = true,
- useDigits: Boolean = false
-): String = random(length, useLetters, useDigits)
\ No newline at end of file
+ useDigits: Boolean = true,
+ lowerCase: Boolean = true,
+ upperCase: Boolean = true
+): String {
+ require(useLetters || useDigits)
+ require(!useLetters || lowerCase || upperCase)
+
+ val fullAlphabet: MutableList = mutableListOf()
+
+ if (useDigits) fullAlphabet += NUMBERS
+ if (useLetters && lowerCase) fullAlphabet += LOWER_CASE_ALPHABET
+ if (useLetters && upperCase) fullAlphabet += UPPER_CASE_ALPHABET
+
+ return List(length) { fullAlphabet.random() }.joinToString("")
+}
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/reflection/ReflectionUtils.kt b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/reflection/ReflectionUtils.kt
index 726f638..cc466d5 100644
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/reflection/ReflectionUtils.kt
+++ b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/reflection/ReflectionUtils.kt
@@ -17,13 +17,14 @@
package ru.sokomishalov.commons.core.reflection
-import kotlin.reflect.full.companionObject
-
/**
* @author sokomishalov
*/
-fun unwrapCompanionClass(ofClass: Class): Class<*> {
- return ofClass.enclosingClass?.takeIf {
- ofClass.enclosingClass.kotlin.companionObject?.java == ofClass
- } ?: ofClass
+
+
+fun Class.unwrapCompanionClass(): Class<*> {
+ return when {
+ name.endsWith("\$Companion") -> enclosingClass ?: this
+ else -> this
+ }
}
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/string/StringUtils.kt b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/string/StringExtensions.kt
similarity index 99%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/string/StringUtils.kt
rename to commons-core/src/main/kotlin/ru/sokomishalov/commons/core/string/StringExtensions.kt
index 3e53064..7c2068b 100644
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/string/StringUtils.kt
+++ b/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/string/StringExtensions.kt
@@ -32,7 +32,6 @@ inline fun CharSequence?.isNotNullOrBlank(): Boolean {
return (this == null || this.isBlank()).not()
}
-
// https://stackoverflow.com/a/1248627/5843129
fun String.convertGlobToRegex(): Regex {
val sb = StringBuilder(length)
diff --git a/commons-core/src/test/kotlin/ru/sokomishalov/commons/core/images/ImageUtilsTest.kt b/commons-core/src/test/kotlin/ru/sokomishalov/commons/core/images/ImageUtilsTest.kt
deleted file mode 100644
index e3b536b..0000000
--- a/commons-core/src/test/kotlin/ru/sokomishalov/commons/core/images/ImageUtilsTest.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ru.sokomishalov.commons.core.images
-
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert.*
-import org.junit.Test
-import kotlin.math.abs
-
-class ImageUtilsTest {
-
- private val imageWidth = 200
- private val imageHeight = 300
- private val imageUrl = "https://picsum.photos/$imageWidth/$imageHeight"
- private val invalidImageUrl = "https://lol.kek/cheburek"
- private val imageUrl401 = "https://httpstat.us/401"
- private val imageUrl404 = "https://httpstat.us/404"
-
-
- @Test
- fun `Get random image by url and check dimensions`() {
- val imageDimensions = runBlocking { getImageDimensions(imageUrl) }
-
- assertEquals(imageWidth, imageDimensions.first)
- assertEquals(imageHeight, imageDimensions.second)
- }
-
- @Test
- fun `Get random image by url and check aspect ratio`() {
- val expected = imageWidth.toDouble().div(imageHeight)
- val result = runBlocking { getImageAspectRatio(imageUrl) }
-
- assertTrue(abs(expected - result) < 0.01)
- }
-
- @Test
- fun `Check image availability`() {
- runBlocking {
- assertTrue(checkImageUrl(imageUrl))
- assertFalse(checkImageUrl(invalidImageUrl))
- assertFalse(checkImageUrl(imageUrl401))
- assertFalse(checkImageUrl(imageUrl404))
- }
- }
-}
\ No newline at end of file
diff --git a/commons-coroutines/pom.xml b/commons-coroutines/pom.xml
new file mode 100644
index 0000000..4c9e069
--- /dev/null
+++ b/commons-coroutines/pom.xml
@@ -0,0 +1,117 @@
+
+
+ 4.0.0
+
+
+ ru.sokomishalov.commons
+ commons-parent
+ 1.1.0
+
+
+ commons-coroutines
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-core
+ 1.1.0
+
+
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-core
+ ${kotlinx.version}
+
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-jdk8
+ ${kotlinx.version}
+
+
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-reactive
+ ${kotlinx.version}
+
+
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-reactor
+ ${kotlinx.version}
+
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.artifactId}
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+ ${java.version}
+
+ -Xuse-experimental=kotlin.Experimental
+
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ kapt
+
+ kapt
+
+
+
+ ${project.basedir}/src/main/kotlin
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ testCompile
+ test-compile
+
+ testCompile
+
+
+
+
+
+ ${java.version}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineIterableExtensions.kt b/commons-coroutines/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineIterableExtensions.kt
similarity index 100%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineIterableExtensions.kt
rename to commons-coroutines/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineIterableExtensions.kt
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineMapExtensions.kt b/commons-coroutines/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineMapExtensions.kt
similarity index 100%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineMapExtensions.kt
rename to commons-coroutines/src/main/kotlin/ru/sokomishalov/commons/core/collections/CoroutineMapExtensions.kt
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/reactor/ReactorUtils.kt b/commons-coroutines/src/main/kotlin/ru/sokomishalov/commons/core/reactor/ReactorUtils.kt
similarity index 100%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/reactor/ReactorUtils.kt
rename to commons-coroutines/src/main/kotlin/ru/sokomishalov/commons/core/reactor/ReactorUtils.kt
diff --git a/commons-distributed-locks/pom.xml b/commons-distributed-locks/pom.xml
new file mode 100644
index 0000000..ab12b26
--- /dev/null
+++ b/commons-distributed-locks/pom.xml
@@ -0,0 +1,175 @@
+
+
+ 4.0.0
+
+
+ ru.sokomishalov.commons
+ commons-parent
+ 1.1.0
+
+
+ commons-distributed-locks
+ 1.1.0
+
+
+
+
+
+ ru.sokomishalov.commons
+ commons-core
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-coroutines
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-serialization
+ 1.1.0
+
+
+
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+ true
+
+
+
+ org.mongodb
+ mongodb-driver-reactivestreams
+ ${mongo-reactive.version}
+ true
+
+
+
+ io.lettuce
+ lettuce-core
+ ${lettuce.version}
+ true
+
+
+
+
+
+ ru.sokomishalov.commons
+ commons-logging
+ 1.1.0
+
+
+
+ org.testcontainers
+ testcontainers
+ ${test-containers.version}
+ test
+
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${spring.boot.version}
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring.boot.version}
+ test
+
+
+
+ org.jetbrains.kotlin
+ kotlin-test-junit
+ ${kotlin.version}
+ test
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/test/kotlin
+ ${project.artifactId}
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+ ${java.version}
+
+ -Xuse-experimental=kotlin.Experimental
+
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ kapt
+
+ kapt
+
+
+
+ ${project.basedir}/src/main/kotlin
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ testCompile
+ test-compile
+
+ testCompile
+
+
+
+
+
+ ${java.version}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockExtensions.kt b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockExtensions.kt
similarity index 86%
rename from commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockExtensions.kt
rename to commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockExtensions.kt
index 6e836ba..ed5aa9b 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockExtensions.kt
+++ b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockExtensions.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ru.sokomishalov.commons.spring.locks.cluster
+package ru.sokomishalov.commons.distributed.locks
import ru.sokomishalov.commons.core.string.isNotNullOrBlank
import ru.sokomishalov.commons.core.url.HOSTNAME
@@ -29,10 +29,10 @@ import java.time.ZonedDateTime.now
* @param lockName name of lock
* @param lockAtLeastFor will hold the lock for this duration, at a minimum
* @param lockAtMostFor will hold the lock for this duration, at a maximum
- * @param action to execute the given [action] under cluster lock.
+ * @param action to execute the given [action] under distributed lock.
* @return the return value of the action or null if locked
*/
-suspend inline fun LockProvider.withClusterLock(
+suspend inline fun DistributedLockProvider.withDistributedLock(
lockName: String,
lockAtLeastFor: Duration = ZERO,
lockAtMostFor: Duration = lockAtLeastFor,
@@ -45,10 +45,10 @@ suspend inline fun LockProvider.withClusterLock(
val now = now()
- val lockedAtLeastUntil = now.plus(lockAtLeastFor)
- val lockedAtMostUntil = now.plus(lockAtMostFor)
+ val lockedAtLeastUntil = now + lockAtLeastFor
+ val lockedAtMostUntil = now + lockAtMostFor
- val lockInfo = LockInfo(
+ val lockInfo = DistributedLockInfo(
lockName = lockName,
lockedBy = HOSTNAME,
lockedAt = now,
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockInfo.kt b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockInfo.kt
similarity index 90%
rename from commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockInfo.kt
rename to commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockInfo.kt
index cff1992..30bb104 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockInfo.kt
+++ b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockInfo.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ru.sokomishalov.commons.spring.locks.cluster
+package ru.sokomishalov.commons.distributed.locks
import java.time.ZonedDateTime
@@ -21,7 +21,7 @@ import java.time.ZonedDateTime
* @author sokomishalov
*/
-data class LockInfo(
+data class DistributedLockInfo(
val lockName: String,
val lockedBy: String,
val lockedAt: ZonedDateTime,
diff --git a/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockProvider.kt b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockProvider.kt
new file mode 100644
index 0000000..727a51b
--- /dev/null
+++ b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/DistributedLockProvider.kt
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("unused")
+
+package ru.sokomishalov.commons.distributed.locks
+
+/**
+ * @author sokomishalov
+ */
+interface DistributedLockProvider {
+
+ suspend fun tryLock(distributedLockInfo: DistributedLockInfo): Boolean
+
+ suspend fun release(distributedLockInfo: DistributedLockInfo)
+}
\ No newline at end of file
diff --git a/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/WithDistributedLock.kt b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/WithDistributedLock.kt
new file mode 100644
index 0000000..bf5d069
--- /dev/null
+++ b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/WithDistributedLock.kt
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.distributed.locks
+
+import kotlin.annotation.AnnotationRetention.RUNTIME
+import kotlin.annotation.AnnotationTarget.*
+
+/**
+ * @author sokomishalov
+ */
+
+@Target(FUNCTION, PROPERTY_SETTER, PROPERTY_GETTER)
+@Retention(RUNTIME)
+annotation class WithDistributedLock(
+
+ // name of lock
+ val lockName: String = "lock",
+
+ // will hold the lock for this duration, at a minimum
+ val lockAtLeastForMs: Long = 0,
+
+ // will hold the lock for this duration, at a maximum
+ val lockAtMostForMs: Long = 0
+)
\ No newline at end of file
diff --git a/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/WithDistributedLockAspect.kt b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/WithDistributedLockAspect.kt
new file mode 100644
index 0000000..6209c98
--- /dev/null
+++ b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/WithDistributedLockAspect.kt
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.distributed.locks
+
+import kotlinx.coroutines.runBlocking
+import org.aspectj.lang.ProceedingJoinPoint
+import org.aspectj.lang.annotation.Around
+import org.aspectj.lang.annotation.Aspect
+import org.aspectj.lang.reflect.MethodSignature
+import java.time.Duration.ofMillis
+
+@Aspect
+class WithDistributedLockAspect(private val distributedLockProvider: DistributedLockProvider) {
+
+ @Around("@annotation(ru.sokomishalov.commons.distributed.locks.WithDistributedLock) && execution(* *.*(..))")
+ fun doActionWithDistributedLock(pjp: ProceedingJoinPoint): Any? {
+ val signature = pjp.signature as MethodSignature
+ val annotation = signature.method.getAnnotation(WithDistributedLock::class.java)
+
+ return runBlocking {
+ distributedLockProvider.withDistributedLock(
+ lockName = annotation.lockName,
+ lockAtLeastFor = ofMillis(annotation.lockAtLeastForMs),
+ lockAtMostFor = ofMillis(annotation.lockAtMostForMs),
+ ifLockedValue = null
+ ) {
+ pjp.proceed()
+ }
+ }
+ }
+}
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/mongo/MongoReactiveLockProvider.kt b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoReactiveDistributedLockProvider.kt
similarity index 72%
rename from commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/mongo/MongoReactiveLockProvider.kt
rename to commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoReactiveDistributedLockProvider.kt
index 87f0236..eb330b1 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/mongo/MongoReactiveLockProvider.kt
+++ b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoReactiveDistributedLockProvider.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ru.sokomishalov.commons.spring.locks.cluster.mongo
+package ru.sokomishalov.commons.distributed.locks.mongo
import com.mongodb.ErrorCategory.DUPLICATE_KEY
import com.mongodb.ErrorCategory.fromErrorCode
@@ -25,29 +25,27 @@ import com.mongodb.client.model.Updates.set
import com.mongodb.reactivestreams.client.MongoClient
import com.mongodb.reactivestreams.client.MongoClients
import com.mongodb.reactivestreams.client.MongoCollection
-import kotlinx.coroutines.reactive.awaitFirstOrElse
+import kotlinx.coroutines.reactive.awaitFirstOrNull
import kotlinx.coroutines.reactive.awaitSingle
import org.bson.Document
-import org.springframework.boot.autoconfigure.mongo.MongoProperties
import ru.sokomishalov.commons.core.common.unit
-import ru.sokomishalov.commons.spring.locks.cluster.LockInfo
-import ru.sokomishalov.commons.spring.locks.cluster.LockProvider
+import ru.sokomishalov.commons.distributed.locks.DistributedLockInfo
+import ru.sokomishalov.commons.distributed.locks.DistributedLockProvider
import java.util.*
/**
* @author sokomishalov
*/
-class MongoReactiveLockProvider(
- private val mongoProperties: MongoProperties? = null,
+class MongoReactiveDistributedLockProvider(
private val client: MongoClient = MongoClients.create(),
- private val databaseName: String = mongoProperties?.mongoClientDatabase ?: DEFAULT_DB_NAME,
+ private val databaseName: String = DEFAULT_DB_NAME,
private val collectionName: String = DEFAULT_COLLECTION_NAME
-) : LockProvider {
+) : DistributedLockProvider {
companion object {
- private const val DEFAULT_DB_NAME = "clusterLockDb"
- private const val DEFAULT_COLLECTION_NAME = "clusterLock"
+ private const val DEFAULT_DB_NAME = "distributedLockDb"
+ private const val DEFAULT_COLLECTION_NAME = "distributedLock"
private const val ID_FIELD = "_id"
private const val LOCKED_UNTIL_FIELD = "lockedUntil"
private const val LOCKED_AT_FIELD = "lockedAt"
@@ -60,19 +58,19 @@ class MongoReactiveLockProvider(
* 2. The lock document exists and lockUntil before now - it is updated - we have the lock
* 3. The lock document exists and lockUntil after now - Duplicate key exception is thrown
*/
- override suspend fun tryLock(lockInfo: LockInfo): Boolean {
+ override suspend fun tryLock(distributedLockInfo: DistributedLockInfo): Boolean {
return try {
getCollection()
.findOneAndUpdate(
- and(eq(ID_FIELD, lockInfo.lockName), lte(LOCKED_UNTIL_FIELD, Date())),
+ and(eq(ID_FIELD, distributedLockInfo.lockName), lte(LOCKED_UNTIL_FIELD, Date())),
combine(
- set(LOCKED_UNTIL_FIELD, Date.from(lockInfo.lockedUntil.toInstant())),
- set(LOCKED_AT_FIELD, Date.from(lockInfo.lockedAt.toInstant())),
- set(LOCKED_BY_FIELD, lockInfo.lockedBy)
+ set(LOCKED_UNTIL_FIELD, Date.from(distributedLockInfo.lockedUntil.toInstant())),
+ set(LOCKED_AT_FIELD, Date.from(distributedLockInfo.lockedAt.toInstant())),
+ set(LOCKED_BY_FIELD, distributedLockInfo.lockedBy)
),
FindOneAndUpdateOptions().upsert(true)
)
- .awaitFirstOrElse { null }
+ .awaitFirstOrNull()
.unit()
true
} catch (e: MongoServerException) {
@@ -83,11 +81,11 @@ class MongoReactiveLockProvider(
}
}
- override suspend fun release(lockInfo: LockInfo) {
+ override suspend fun release(distributedLockInfo: DistributedLockInfo) {
return getCollection()
.findOneAndUpdate(
- eq(ID_FIELD, lockInfo.lockName),
- combine(set(LOCKED_UNTIL_FIELD, Date.from(lockInfo.lockedUntil.toInstant())))
+ eq(ID_FIELD, distributedLockInfo.lockName),
+ combine(set(LOCKED_UNTIL_FIELD, Date.from(distributedLockInfo.lockedUntil.toInstant())))
)
.awaitSingle()
.unit()
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/redis/RedisLettuceLockProvider.kt b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisLettuceDistributedLockProvider.kt
similarity index 71%
rename from commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/redis/RedisLettuceLockProvider.kt
rename to commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisLettuceDistributedLockProvider.kt
index 704f453..90eea8f 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/redis/RedisLettuceLockProvider.kt
+++ b/commons-distributed-locks/src/main/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisLettuceDistributedLockProvider.kt
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ru.sokomishalov.commons.spring.locks.cluster.redis
+package ru.sokomishalov.commons.distributed.locks.redis
+import com.fasterxml.jackson.module.kotlin.readValue
import io.lettuce.core.RedisClient
import io.lettuce.core.RedisURI
import io.lettuce.core.SetArgs
@@ -23,23 +24,22 @@ import ru.sokomishalov.commons.core.consts.LOCALHOST
import ru.sokomishalov.commons.core.reactor.await
import ru.sokomishalov.commons.core.reactor.awaitUnit
import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
-import ru.sokomishalov.commons.core.serialization.aReadValue
import ru.sokomishalov.commons.core.string.isNotNullOrBlank
-import ru.sokomishalov.commons.spring.locks.cluster.LockInfo
-import ru.sokomishalov.commons.spring.locks.cluster.LockProvider
+import ru.sokomishalov.commons.distributed.locks.DistributedLockInfo
+import ru.sokomishalov.commons.distributed.locks.DistributedLockProvider
import java.time.Duration
import java.time.ZonedDateTime.now
-class RedisLettuceLockProvider(
+class RedisLettuceDistributedLockProvider(
private val client: RedisClient = RedisClient.create(RedisURI.create(LOCALHOST)),
private val connection: StatefulRedisConnection = client.connect(),
private val keyPrefix: String = "",
private val keyDelimiter: String = ":"
-) : LockProvider {
+) : DistributedLockProvider {
- override suspend fun tryLock(lockInfo: LockInfo): Boolean {
- val keyValue = lockInfo.buildKeyValue()
- val expireAfterMs = Duration.between(now(), lockInfo.lockedUntil).toMillis()
+ override suspend fun tryLock(distributedLockInfo: DistributedLockInfo): Boolean {
+ val keyValue = distributedLockInfo.buildKeyValue()
+ val expireAfterMs = Duration.between(now(), distributedLockInfo.lockedUntil).toMillis()
val lockAcquired = connection
.reactive()
@@ -55,15 +55,16 @@ class RedisLettuceLockProvider(
}
}
- override suspend fun release(lockInfo: LockInfo) {
- val keyValue = lockInfo.buildKeyValue()
+ override suspend fun release(distributedLockInfo: DistributedLockInfo) {
+ val keyValue = distributedLockInfo.buildKeyValue()
connection
.reactive()
.set(keyValue.first, keyValue.second, SetArgs().xx())
.awaitUnit()
}
- private fun LockInfo.buildKeyValue(): Pair {
+
+ private fun DistributedLockInfo.buildKeyValue(): Pair {
val key = when {
keyPrefix.isBlank() -> lockName
else -> "$keyPrefix$keyDelimiter$lockName"
@@ -73,7 +74,7 @@ class RedisLettuceLockProvider(
return key to value
}
- private suspend fun String.deserializeValue(): LockInfo {
- return OBJECT_MAPPER.aReadValue(this)
+ private fun String.deserializeValue(): DistributedLockInfo {
+ return OBJECT_MAPPER.readValue(this)
}
}
\ No newline at end of file
diff --git a/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/AbstractDistributedLockAspectTest.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/AbstractDistributedLockAspectTest.kt
new file mode 100644
index 0000000..8c8fd83
--- /dev/null
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/AbstractDistributedLockAspectTest.kt
@@ -0,0 +1,88 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.distributed.locks
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.TestConfiguration
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.ComponentScan
+import org.springframework.context.annotation.EnableAspectJAutoProxy
+import org.springframework.stereotype.Component
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
+import java.util.concurrent.atomic.AtomicInteger
+
+
+/**
+ * @author sokomishalov
+ */
+@RunWith(SpringJUnit4ClassRunner::class)
+@ComponentScan("ru.sokomishalov.commons.distributed.locks")
+@EnableAspectJAutoProxy
+abstract class AbstractDistributedLockAspectTest {
+
+ @Autowired
+ lateinit var someBean: AspectConfig.SomeBean
+
+ @Test
+ fun `Check at least for duration`() {
+ val counter = AtomicInteger(0)
+ val iterations = 5
+
+ repeat(iterations) {
+ someBean.incrementCounterAtLeast(counter)
+ }
+
+ assertEquals(1, counter.get())
+ }
+
+ @Test
+ fun `Check at most for duration`() {
+ val counter = AtomicInteger(0)
+ val iterations = 5
+
+ repeat(iterations) {
+ someBean.incrementCounterAtMost(counter)
+ }
+
+ assertEquals(iterations, counter.get())
+ }
+
+ @TestConfiguration
+ open class AspectConfig {
+
+ @Bean
+ open fun aspect(distributedLockProvider: DistributedLockProvider): WithDistributedLockAspect {
+ return WithDistributedLockAspect(distributedLockProvider)
+ }
+
+ @Component
+ open class SomeBean {
+
+ @WithDistributedLock(lockAtLeastForMs = 600_000, lockAtMostForMs = 600_000)
+ open fun incrementCounterAtLeast(counter: AtomicInteger) {
+ counter.incrementAndGet()
+ }
+
+ @WithDistributedLock(lockAtMostForMs = 600_000)
+ open fun incrementCounterAtMost(counter: AtomicInteger) {
+ counter.incrementAndGet()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/AbstractClusterLockTest.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/AbstractDistributedLockWithExtensionsTest.kt
similarity index 77%
rename from commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/AbstractClusterLockTest.kt
rename to commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/AbstractDistributedLockWithExtensionsTest.kt
index f59d6f3..76bfc1a 100644
--- a/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/AbstractClusterLockTest.kt
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/AbstractDistributedLockWithExtensionsTest.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ru.sokomishalov.commons.spring.locks.cluster
+package ru.sokomishalov.commons.distributed.locks
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -21,9 +21,12 @@ import org.junit.Test
import java.time.Duration.ofMinutes
import java.util.concurrent.atomic.AtomicInteger
-abstract class AbstractClusterLockTest {
+/**
+ * @author sokomishalov
+ */
+abstract class AbstractDistributedLockWithExtensionsTest {
- protected abstract val provider: LockProvider
+ protected abstract val provider: DistributedLockProvider
@Test
fun `Check at least for duration`() {
@@ -32,7 +35,7 @@ abstract class AbstractClusterLockTest {
repeat(iterations) {
runBlocking {
- provider.withClusterLock(lockName = "atLeastForLock", lockAtLeastFor = ofMinutes(10)) {
+ provider.withDistributedLock(lockName = "lockAtLeastFor", lockAtLeastFor = ofMinutes(10)) {
counter.incrementAndGet()
}
}
@@ -48,7 +51,7 @@ abstract class AbstractClusterLockTest {
repeat(iterations) {
runBlocking {
- provider.withClusterLock(lockName = "atLeastForLock", lockAtMostFor = ofMinutes(10)) {
+ provider.withDistributedLock(lockName = "lockAtMostFor", lockAtMostFor = ofMinutes(10)) {
counter.incrementAndGet()
}
}
diff --git a/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoDistributedLockAspectTest.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoDistributedLockAspectTest.kt
new file mode 100644
index 0000000..1801751
--- /dev/null
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoDistributedLockAspectTest.kt
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.distributed.locks.mongo
+
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.springframework.context.ApplicationContextInitializer
+import org.springframework.context.ConfigurableApplicationContext
+import org.springframework.test.context.ContextConfiguration
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.distributed.locks.AbstractDistributedLockAspectTest
+import ru.sokomishalov.commons.util.MongoTestContainer
+import ru.sokomishalov.commons.util.createDefaultMongoContainer
+import ru.sokomishalov.commons.util.createReactiveMongoClient
+
+
+/**
+ * @author sokomishalov
+ */
+
+@ContextConfiguration(classes = [AbstractDistributedLockAspectTest.AspectConfig::class], initializers = [MongoDistributedLockAspectTest.Initializer::class])
+class MongoDistributedLockAspectTest : AbstractDistributedLockAspectTest() {
+
+ companion object : Loggable {
+ @get:ClassRule
+ val mongo: MongoTestContainer = createDefaultMongoContainer()
+
+ @AfterClass
+ @JvmStatic
+ fun stop() = mongo.stop()
+ }
+
+ internal class Initializer : ApplicationContextInitializer {
+ override fun initialize(context: ConfigurableApplicationContext) {
+ mongo.start()
+ logInfo(mongo.logs)
+ context.beanFactory.registerSingleton("lockProvider", MongoReactiveDistributedLockProvider(client = mongo.createReactiveMongoClient()))
+ }
+ }
+}
\ No newline at end of file
diff --git a/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoDistributedLockProviderWithExtensionsTest.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoDistributedLockProviderWithExtensionsTest.kt
new file mode 100644
index 0000000..fce6597
--- /dev/null
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/mongo/MongoDistributedLockProviderWithExtensionsTest.kt
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.distributed.locks.mongo
+
+import org.junit.AfterClass
+import org.junit.ClassRule
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.distributed.locks.AbstractDistributedLockWithExtensionsTest
+import ru.sokomishalov.commons.distributed.locks.DistributedLockProvider
+import ru.sokomishalov.commons.util.MongoTestContainer
+import ru.sokomishalov.commons.util.createDefaultMongoContainer
+import ru.sokomishalov.commons.util.createReactiveMongoClient
+
+/**
+ * @author sokomishalov
+ */
+class MongoDistributedLockProviderWithExtensionsTest : AbstractDistributedLockWithExtensionsTest() {
+
+ companion object : Loggable {
+ @get:ClassRule
+ val mongo: MongoTestContainer = createDefaultMongoContainer()
+
+ @AfterClass
+ @JvmStatic
+ fun stop() = mongo.stop()
+ }
+
+ override val provider: DistributedLockProvider by lazy {
+ mongo.start()
+ logInfo(mongo.logs)
+ MongoReactiveDistributedLockProvider(client = mongo.createReactiveMongoClient())
+ }
+}
\ No newline at end of file
diff --git a/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisDistributedLockAspectTest.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisDistributedLockAspectTest.kt
new file mode 100644
index 0000000..192b710
--- /dev/null
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisDistributedLockAspectTest.kt
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.distributed.locks.redis
+
+import io.lettuce.core.RedisClient
+import io.lettuce.core.RedisURI
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.springframework.context.ApplicationContextInitializer
+import org.springframework.context.ConfigurableApplicationContext
+import org.springframework.test.context.ContextConfiguration
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.distributed.locks.AbstractDistributedLockAspectTest
+import ru.sokomishalov.commons.util.RedisTestContainer
+import ru.sokomishalov.commons.util.createDefaultRedisContainer
+
+
+/**
+ * @author sokomishalov
+ */
+
+@ContextConfiguration(classes = [AbstractDistributedLockAspectTest.AspectConfig::class], initializers = [RedisDistributedLockAspectTest.Initializer::class])
+class RedisDistributedLockAspectTest : AbstractDistributedLockAspectTest() {
+
+ companion object : Loggable {
+ @get:ClassRule
+ val redis: RedisTestContainer = createDefaultRedisContainer()
+
+ @AfterClass
+ @JvmStatic
+ fun stop() = redis.stop()
+ }
+
+ internal class Initializer : ApplicationContextInitializer {
+ override fun initialize(context: ConfigurableApplicationContext) {
+ redis.start()
+ logInfo(redis.logs)
+ context.beanFactory.registerSingleton("lockProvider", RedisLettuceDistributedLockProvider(client = RedisClient.create(RedisURI().apply {
+ host = redis.containerIpAddress
+ port = redis.firstMappedPort
+ })))
+ }
+ }
+}
\ No newline at end of file
diff --git a/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisDistributedLockProviderWithExtensionsTest.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisDistributedLockProviderWithExtensionsTest.kt
new file mode 100644
index 0000000..0dc8742
--- /dev/null
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/distributed/locks/redis/RedisDistributedLockProviderWithExtensionsTest.kt
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.distributed.locks.redis
+
+import org.junit.AfterClass
+import org.junit.ClassRule
+import ru.sokomishalov.commons.core.log.Loggable
+import ru.sokomishalov.commons.distributed.locks.AbstractDistributedLockWithExtensionsTest
+import ru.sokomishalov.commons.distributed.locks.DistributedLockProvider
+import ru.sokomishalov.commons.util.RedisTestContainer
+import ru.sokomishalov.commons.util.createDefaultRedisContainer
+import ru.sokomishalov.commons.util.createRedisClient
+
+/**
+ * @author sokomishalov
+ */
+class RedisDistributedLockProviderWithExtensionsTest : AbstractDistributedLockWithExtensionsTest() {
+
+ companion object : Loggable {
+ @get:ClassRule
+ val redis: RedisTestContainer = createDefaultRedisContainer()
+
+ @AfterClass
+ @JvmStatic
+ fun stop() = redis.stop()
+ }
+
+ override val provider: DistributedLockProvider by lazy {
+ redis.start()
+ logInfo(redis.logs)
+ RedisLettuceDistributedLockProvider(client = redis.createRedisClient())
+ }
+}
\ No newline at end of file
diff --git a/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/util/MongoTestContainer.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/util/MongoTestContainer.kt
new file mode 100644
index 0000000..b53a196
--- /dev/null
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/util/MongoTestContainer.kt
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.util
+
+import com.mongodb.ConnectionString
+import com.mongodb.reactivestreams.client.MongoClient
+import com.mongodb.reactivestreams.client.MongoClients
+import org.testcontainers.containers.GenericContainer
+
+/**
+ * @author sokomishalov
+ */
+
+class MongoTestContainer : GenericContainer("mvertes/alpine-mongo")
+
+internal fun createDefaultMongoContainer(): MongoTestContainer {
+ return MongoTestContainer().apply {
+ withReuse(true)
+ withExposedPorts(27017)
+ }
+}
+
+fun MongoTestContainer.createReactiveMongoClient(): MongoClient {
+ return MongoClients.create(ConnectionString("mongodb://${containerIpAddress}:${firstMappedPort}"))
+}
\ No newline at end of file
diff --git a/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/util/RedisTestContainer.kt b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/util/RedisTestContainer.kt
new file mode 100644
index 0000000..60d67fb
--- /dev/null
+++ b/commons-distributed-locks/src/test/kotlin/ru/sokomishalov/commons/util/RedisTestContainer.kt
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ru.sokomishalov.commons.util
+
+import io.lettuce.core.RedisClient
+import io.lettuce.core.RedisURI
+import org.testcontainers.containers.GenericContainer
+import java.time.Duration
+
+/**
+ * @author sokomishalov
+ */
+
+class RedisTestContainer : GenericContainer("redis:alpine")
+
+internal fun createDefaultRedisContainer(): RedisTestContainer {
+ return RedisTestContainer().apply {
+ withReuse(true)
+ withExposedPorts(6379)
+ }
+}
+
+fun RedisTestContainer.createRedisClient(timeout: Duration = Duration.ofSeconds(1)): RedisClient {
+ return RedisClient.create(RedisURI(containerIpAddress, firstMappedPort, timeout))
+}
\ No newline at end of file
diff --git a/commons-logging/pom.xml b/commons-logging/pom.xml
new file mode 100644
index 0000000..7fbe623
--- /dev/null
+++ b/commons-logging/pom.xml
@@ -0,0 +1,114 @@
+
+
+ 4.0.0
+
+
+ ru.sokomishalov.commons
+ commons-parent
+ 1.1.0
+
+
+ commons-logging
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-core
+ 1.1.0
+
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+ test
+
+
+
+ org.jetbrains.kotlin
+ kotlin-test-junit
+ ${kotlin.version}
+ test
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/test/kotlin
+ ${project.artifactId}
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+ ${java.version}
+
+ -Xuse-experimental=kotlin.Experimental
+
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ kapt
+
+ kapt
+
+
+
+ ${project.basedir}/src/main/kotlin
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ testCompile
+ test-compile
+
+ testCompile
+
+
+
+
+
+ ${java.version}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/CustomLoggerFactory.kt b/commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/CustomLoggerFactory.kt
similarity index 77%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/CustomLoggerFactory.kt
rename to commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/CustomLoggerFactory.kt
index 03901b0..08467c9 100644
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/CustomLoggerFactory.kt
+++ b/commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/CustomLoggerFactory.kt
@@ -16,6 +16,7 @@
package ru.sokomishalov.commons.core.log
import org.slf4j.Logger
+import ru.sokomishalov.commons.core.reflection.unwrapCompanionClass
import java.util.concurrent.ConcurrentHashMap
/**
@@ -26,12 +27,13 @@ object CustomLoggerFactory {
private val loggersMap: MutableMap = ConcurrentHashMap()
fun getLogger(clazz: Class): Logger {
- val logger = loggersMap[clazz.name]
+ val nonCompanionClazz = clazz.unwrapCompanionClass()
+ val logger = loggersMap[nonCompanionClazz.name]
return when {
logger != null -> logger
else -> {
- val newLogger = loggerFor(clazz)
- loggersMap[clazz.name] = newLogger
+ val newLogger = loggerFor(nonCompanionClazz)
+ loggersMap[nonCompanionClazz.name] = newLogger
newLogger
}
}
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/LogUtils.kt b/commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/LogUtils.kt
similarity index 95%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/LogUtils.kt
rename to commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/LogUtils.kt
index 7d3d0cf..392815f 100644
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/LogUtils.kt
+++ b/commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/LogUtils.kt
@@ -31,5 +31,5 @@ fun loggerFor(name: String): Logger = getLogger(name)
inline fun T.logger(): Logger = loggerFor(javaClass)
-inline fun T.loggerDelegate(): Lazy = lazy { loggerFor(unwrapCompanionClass(javaClass)) }
+inline fun T.loggerDelegate(): Lazy = lazy { loggerFor(javaClass.unwrapCompanionClass()) }
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/Loggable.kt b/commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/Loggable.kt
similarity index 100%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/Loggable.kt
rename to commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/Loggable.kt
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/Sl4jExtensions.kt b/commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/Sl4jExtensions.kt
similarity index 100%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/log/Sl4jExtensions.kt
rename to commons-logging/src/main/kotlin/ru/sokomishalov/commons/core/log/Sl4jExtensions.kt
diff --git a/commons-core/src/test/kotlin/ru/sokomishalov/commons/core/log/LogUtilsTest.kt b/commons-logging/src/test/kotlin/ru/sokomishalov/commons/core/log/LogUtilsTest.kt
similarity index 79%
rename from commons-core/src/test/kotlin/ru/sokomishalov/commons/core/log/LogUtilsTest.kt
rename to commons-logging/src/test/kotlin/ru/sokomishalov/commons/core/log/LogUtilsTest.kt
index 7aff836..3591704 100644
--- a/commons-core/src/test/kotlin/ru/sokomishalov/commons/core/log/LogUtilsTest.kt
+++ b/commons-logging/src/test/kotlin/ru/sokomishalov/commons/core/log/LogUtilsTest.kt
@@ -26,6 +26,7 @@ class LogUtilsTest {
@Test
fun `Assert logger`() {
LogWithInterface().doJob()
+ LogWithCompanion().doJob()
LogWithDelegate().doJob()
}
@@ -33,16 +34,26 @@ class LogUtilsTest {
class LogWithInterface : Loggable {
fun doJob() {
- log("test")
+ log("interface")
}
}
-class LogWithDelegate {
+class LogWithCompanion {
companion object : Loggable
fun doJob() {
- logInfo { "kek" }
- logger.debug("lek")
+ logInfo { "companion" }
+ }
+}
+
+class LogWithDelegate {
+
+ companion object {
+ private val log by loggerDelegate()
+ }
+
+ fun doJob() {
+ log.info { "companion delegate" }
}
}
\ No newline at end of file
diff --git a/commons-reactor/pom.xml b/commons-reactor/pom.xml
new file mode 100644
index 0000000..8f152e5
--- /dev/null
+++ b/commons-reactor/pom.xml
@@ -0,0 +1,103 @@
+
+
+ 4.0.0
+
+
+ ru.sokomishalov.commons
+ commons-parent
+ 1.1.0
+
+
+ commons-reactor
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-core
+ 1.1.0
+
+
+
+ io.projectreactor
+ reactor-core
+ ${reactor.version}
+
+
+ io.projectreactor.netty
+ reactor-netty
+ ${reactor-netty.version}
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.artifactId}
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+ ${java.version}
+
+ -Xuse-experimental=kotlin.Experimental
+
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ kapt
+
+ kapt
+
+
+
+ ${project.basedir}/src/main/kotlin
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ testCompile
+ test-compile
+
+ testCompile
+
+
+
+
+
+ ${java.version}
+
+
+
+
+
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/http/HttpUtils.kt b/commons-reactor/src/main/kotlin/ru/sokomishalov/commons/core/http/HttpUtils.kt
similarity index 100%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/http/HttpUtils.kt
rename to commons-reactor/src/main/kotlin/ru/sokomishalov/commons/core/http/HttpUtils.kt
diff --git a/commons-serialization/pom.xml b/commons-serialization/pom.xml
new file mode 100644
index 0000000..98cca1a
--- /dev/null
+++ b/commons-serialization/pom.xml
@@ -0,0 +1,144 @@
+
+
+ 4.0.0
+
+
+ ru.sokomishalov.commons
+ commons-parent
+ 1.1.0
+
+
+ commons-serialization
+ 1.1.0
+
+
+
+
+
+ ru.sokomishalov.commons
+ commons-core
+ 1.1.0
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.module
+ jackson-module-kotlin
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
+
+
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ ${jackson.version}
+ true
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ ${jackson.version}
+ true
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-protobuf
+ ${jackson.version}
+ true
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-csv
+ ${jackson.version}
+ true
+
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.artifactId}
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+ ${java.version}
+
+ -Xuse-experimental=kotlin.Experimental
+
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ kapt
+
+ kapt
+
+
+
+ ${project.basedir}/src/main/kotlin
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ testCompile
+ test-compile
+
+ testCompile
+
+
+
+
+
+ ${java.version}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/CsvObjectMapperHelper.kt b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/CsvObjectMapperHelper.kt
new file mode 100644
index 0000000..e3e768e
--- /dev/null
+++ b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/CsvObjectMapperHelper.kt
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("unused")
+
+package ru.sokomishalov.commons.core.serialization
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.dataformat.csv.CsvFactory
+
+/**
+ * @author sokomishalov
+ */
+
+val CSV_OBJECT_MAPPER: ObjectMapper = buildComplexObjectMapper(factory = CsvFactory())
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockProvider.kt b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/JsonObjectMapperHelper.kt
similarity index 76%
rename from commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockProvider.kt
rename to commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/JsonObjectMapperHelper.kt
index bb59306..5606d9e 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/locks/cluster/LockProvider.kt
+++ b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/JsonObjectMapperHelper.kt
@@ -13,16 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("unused")
+package ru.sokomishalov.commons.core.serialization
-package ru.sokomishalov.commons.spring.locks.cluster
+import com.fasterxml.jackson.databind.ObjectMapper
/**
* @author sokomishalov
*/
-interface LockProvider {
- suspend fun tryLock(lockInfo: LockInfo): Boolean
-
- suspend fun release(lockInfo: LockInfo)
-}
\ No newline at end of file
+val OBJECT_MAPPER: ObjectMapper = buildComplexObjectMapper()
\ No newline at end of file
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ObjectMapperHelper.kt b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ObjectMapperHelper.kt
similarity index 97%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ObjectMapperHelper.kt
rename to commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ObjectMapperHelper.kt
index bff8e8c..87e59cf 100644
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ObjectMapperHelper.kt
+++ b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ObjectMapperHelper.kt
@@ -34,7 +34,6 @@ import com.fasterxml.jackson.module.kotlin.registerKotlinModule
/**
* @author sokomishalov
*/
-val OBJECT_MAPPER: ObjectMapper = buildComplexObjectMapper()
fun buildComplexObjectMapper(
factory: JsonFactory? = null,
diff --git a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/serialization/CoroutinesSerialization.kt b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ProtobufObjectMapperHelper.kt
similarity index 53%
rename from commons-core/src/main/kotlin/ru/sokomishalov/commons/core/serialization/CoroutinesSerialization.kt
rename to commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ProtobufObjectMapperHelper.kt
index 6e14626..7613b68 100644
--- a/commons-core/src/main/kotlin/ru/sokomishalov/commons/core/serialization/CoroutinesSerialization.kt
+++ b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/ProtobufObjectMapperHelper.kt
@@ -13,30 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("unused", "RemoveExplicitTypeArguments")
+@file:Suppress("unused")
package ru.sokomishalov.commons.core.serialization
-import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.convertValue
-import com.fasterxml.jackson.module.kotlin.readValue
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.withContext
-
+import com.fasterxml.jackson.dataformat.protobuf.ProtobufFactory
/**
* @author sokomishalov
*/
-suspend inline fun ObjectMapper.aReadTree(content: String): JsonNode = withContext(IO) {
- readTree(content)
-}
-
-suspend inline fun ObjectMapper.aReadValue(content: String): T = withContext(IO) {
- readValue(content)
-}
-
-suspend inline fun ObjectMapper.aConvertValue(from: String): T = withContext(IO) {
- convertValue(from)
-}
\ No newline at end of file
+val PROTOBUF_OBJECT_MAPPER: ObjectMapper = buildComplexObjectMapper(factory = ProtobufFactory())
\ No newline at end of file
diff --git a/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/XmlObjectMapperHelper.kt b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/XmlObjectMapperHelper.kt
new file mode 100644
index 0000000..97b6fc7
--- /dev/null
+++ b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/XmlObjectMapperHelper.kt
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("unused")
+
+package ru.sokomishalov.commons.core.serialization
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.dataformat.xml.XmlFactory
+
+/**
+ * @author sokomishalov
+ */
+
+val XML_OBJECT_MAPPER: ObjectMapper = buildComplexObjectMapper(factory = XmlFactory())
\ No newline at end of file
diff --git a/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/YamlObjectMapperHelper.kt b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/YamlObjectMapperHelper.kt
new file mode 100644
index 0000000..28cdf5e
--- /dev/null
+++ b/commons-serialization/src/main/kotlin/ru/sokomishalov/commons/core/serialization/YamlObjectMapperHelper.kt
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("unused")
+
+package ru.sokomishalov.commons.core.serialization
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
+
+/**
+ * @author sokomishalov
+ */
+
+val YAML_OBJECT_MAPPER: ObjectMapper = buildComplexObjectMapper(factory = YAMLFactory())
\ No newline at end of file
diff --git a/commons-spring/pom.xml b/commons-spring/pom.xml
index 495ccfc..87d9556 100644
--- a/commons-spring/pom.xml
+++ b/commons-spring/pom.xml
@@ -7,25 +7,11 @@
ru.sokomishalov.commonscommons-parent
- 1.0.29
+ 1.1.0commons-spring
- 1.0.29
-
-
- 1.3.61
- 2.2.2.RELEASE
- 5.2.2.RELEASE
- 2.8.0
- 2.9.2
-
- 2.2.0
- 3.11.0
- 1.12.0
- 5.2.1.RELEASE
- 0.7.2
-
+ 1.1.0
@@ -34,7 +20,31 @@
ru.sokomishalov.commonscommons-core
- 1.0.29
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-logging
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-serialization
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-reactor
+ 1.1.0
+
+
+
+ ru.sokomishalov.commons
+ commons-coroutines
+ 1.1.0
@@ -57,6 +67,20 @@
+
+ ru.sokomishalov.commons
+ commons-distributed-locks
+ 1.1.0
+ true
+
+
+
+ ru.sokomishalov.commons
+ commons-cache
+ 1.1.0
+ true
+
+
org.springframeworkspring-context-support
@@ -95,41 +119,14 @@
io.lettucelettuce-core
- ${redis-lettuce.version}
+ ${lettuce.version}true
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- ${spring.boot.version}
-
-
- org.jetbrains.kotlin
- kotlin-test-junit
- ${kotlin.version}
- test
-
-
- de.flapdoodle.embed
- de.flapdoodle.embed.mongo
- ${mongo-embedded.version}
- test
-
-
- it.ozimov
- embedded-redis
- ${redis-embedded.version}
- test
-
-
${project.basedir}/src/main/kotlin
- ${project.basedir}/src/test/kotlin${project.artifactId}
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/InMemoryCacheAutoConfiguration.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/InMemoryCacheAutoConfiguration.kt
new file mode 100644
index 0000000..0a15d33
--- /dev/null
+++ b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/InMemoryCacheAutoConfiguration.kt
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("SpringJavaInjectionPointsAutowiringInspection")
+
+package ru.sokomishalov.commons.spring.autoconfigure
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.springframework.beans.factory.ObjectProvider
+import org.springframework.boot.autoconfigure.AutoConfigureOrder
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.core.Ordered.LOWEST_PRECEDENCE
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.cache.inmemory.ConcurrentMapCacheService
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+
+/**
+ * @author sokomishalov
+ */
+@Configuration(proxyBeanMethods = false)
+@AutoConfigureOrder(LOWEST_PRECEDENCE)
+@ConditionalOnClass(CacheService::class)
+class InMemoryCacheAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(CacheService::class)
+ fun concurrentMapCacheService(
+ mapper: ObjectProvider
+ ): CacheService {
+ return ConcurrentMapCacheService(mapper = mapper.getIfAvailable { OBJECT_MAPPER })
+ }
+}
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveCacheAutoConfiguration.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveCacheAutoConfiguration.kt
new file mode 100644
index 0000000..9281ce0
--- /dev/null
+++ b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveCacheAutoConfiguration.kt
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("SpringJavaInjectionPointsAutowiringInspection")
+
+package ru.sokomishalov.commons.spring.autoconfigure
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.mongodb.reactivestreams.client.MongoClient
+import org.springframework.beans.factory.ObjectProvider
+import org.springframework.boot.autoconfigure.AutoConfigureOrder
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.core.Ordered.HIGHEST_PRECEDENCE
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.cache.mongo.MongoCacheService
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+
+/**
+ * @author sokomishalov
+ */
+@Configuration
+@ConditionalOnClass(CacheService::class, MongoClient::class)
+@AutoConfigureOrder(HIGHEST_PRECEDENCE)
+class MongoReactiveCacheAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(CacheService::class)
+ @ConditionalOnBean(MongoClient::class)
+ fun mongoCacheService(
+ client: MongoClient,
+ mapper: ObjectProvider
+ ): CacheService {
+ return MongoCacheService(client = client, mapper = mapper.getIfAvailable { OBJECT_MAPPER })
+ }
+}
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveAutoConfiguration.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveDistributedLocksAutoConfiguration.kt
similarity index 64%
rename from commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveAutoConfiguration.kt
rename to commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveDistributedLocksAutoConfiguration.kt
index f2e69ee..0796db4 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveAutoConfiguration.kt
+++ b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/MongoReactiveDistributedLocksAutoConfiguration.kt
@@ -18,25 +18,29 @@
package ru.sokomishalov.commons.spring.autoconfigure
import com.mongodb.reactivestreams.client.MongoClient
+import org.springframework.boot.autoconfigure.AutoConfigureOrder
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
-import ru.sokomishalov.commons.spring.locks.cluster.LockProvider
-import ru.sokomishalov.commons.spring.locks.cluster.mongo.MongoReactiveLockProvider
+import org.springframework.core.Ordered.HIGHEST_PRECEDENCE
+import ru.sokomishalov.commons.distributed.locks.DistributedLockProvider
+import ru.sokomishalov.commons.distributed.locks.mongo.MongoReactiveDistributedLockProvider
/**
* @author sokomishalov
*/
@Configuration
-@ConditionalOnClass(MongoClient::class)
-class MongoReactiveAutoConfiguration {
+@ConditionalOnClass(DistributedLockProvider::class, MongoClient::class)
+@AutoConfigureOrder(HIGHEST_PRECEDENCE)
+class MongoReactiveDistributedLocksAutoConfiguration {
@Bean
- @ConditionalOnMissingBean(LockProvider::class)
+ @ConditionalOnMissingBean(DistributedLockProvider::class)
@ConditionalOnBean(MongoClient::class)
- fun reactiveMongoClusterLockProvider(client: MongoClient): LockProvider =
- MongoReactiveLockProvider(client = client)
+ fun reactiveMongoDistributedLockProvider(client: MongoClient): DistributedLockProvider {
+ return MongoReactiveDistributedLockProvider(client = client)
+ }
}
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceCacheAutoConfiguration.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceCacheAutoConfiguration.kt
new file mode 100644
index 0000000..2ca3636
--- /dev/null
+++ b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceCacheAutoConfiguration.kt
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2019-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("SpringJavaInjectionPointsAutowiringInspection")
+
+package ru.sokomishalov.commons.spring.autoconfigure
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import io.lettuce.core.RedisClient
+import org.springframework.beans.factory.ObjectProvider
+import org.springframework.boot.autoconfigure.AutoConfigureOrder
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.core.Ordered.HIGHEST_PRECEDENCE
+import ru.sokomishalov.commons.cache.CacheService
+import ru.sokomishalov.commons.cache.redis.RedisCacheService
+import ru.sokomishalov.commons.core.serialization.OBJECT_MAPPER
+
+/**
+ * @author sokomishalov
+ */
+@Configuration
+@ConditionalOnClass(CacheService::class, RedisClient::class)
+@AutoConfigureOrder(HIGHEST_PRECEDENCE)
+class RedisLettuceCacheAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(CacheService::class)
+ @ConditionalOnBean(RedisClient::class)
+ fun redisCacheService(
+ client: RedisClient,
+ mapper: ObjectProvider
+ ): CacheService {
+ return RedisCacheService(client = client, mapper = mapper.getIfAvailable { OBJECT_MAPPER })
+ }
+}
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceAutoConfiguration.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceDistributedLocksAutoConfiguration.kt
similarity index 64%
rename from commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceAutoConfiguration.kt
rename to commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceDistributedLocksAutoConfiguration.kt
index 72e8525..bd45f9d 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceAutoConfiguration.kt
+++ b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/autoconfigure/RedisLettuceDistributedLocksAutoConfiguration.kt
@@ -18,24 +18,28 @@
package ru.sokomishalov.commons.spring.autoconfigure
import io.lettuce.core.RedisClient
+import org.springframework.boot.autoconfigure.AutoConfigureOrder
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
-import ru.sokomishalov.commons.spring.locks.cluster.LockProvider
-import ru.sokomishalov.commons.spring.locks.cluster.redis.RedisLettuceLockProvider
+import org.springframework.core.Ordered.HIGHEST_PRECEDENCE
+import ru.sokomishalov.commons.distributed.locks.DistributedLockProvider
+import ru.sokomishalov.commons.distributed.locks.redis.RedisLettuceDistributedLockProvider
/**
* @author sokomishalov
*/
@Configuration
-@ConditionalOnClass(RedisClient::class)
-class RedisLettuceAutoConfiguration {
+@ConditionalOnClass(DistributedLockProvider::class, RedisClient::class)
+@AutoConfigureOrder(HIGHEST_PRECEDENCE)
+class RedisLettuceDistributedLocksAutoConfiguration {
@Bean
- @ConditionalOnMissingBean(LockProvider::class)
+ @ConditionalOnMissingBean(DistributedLockProvider::class)
@ConditionalOnBean(RedisClient::class)
- fun reactiveMongoClusterLockProvider(client: RedisClient): LockProvider =
- RedisLettuceLockProvider(client = client)
+ fun redisDistributedLockProvider(client: RedisClient): DistributedLockProvider {
+ return RedisLettuceDistributedLockProvider(client = client)
+ }
}
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/cache/CacheManagerService.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/cache/CacheManagerService.kt
deleted file mode 100644
index b954538..0000000
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/cache/CacheManagerService.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:Suppress("UNCHECKED_CAST", "unused")
-
-package ru.sokomishalov.commons.spring.cache
-
-import org.springframework.cache.Cache
-import org.springframework.cache.CacheManager
-import org.springframework.cache.concurrent.ConcurrentMapCacheManager
-import ru.sokomishalov.commons.core.common.unit
-
-
-/**
- * @author sokomishalov
- */
-class CacheManagerService(
- private val caches: List = emptyList(),
- private val cacheManager: CacheManager = ConcurrentMapCacheManager(*caches.toTypedArray())
-) : CacheService {
-
- private fun getCache(cacheName: String): Cache? = cacheManager.getCache(cacheName)
-
- override suspend fun get(cacheName: String, key: String): T? = getCache(cacheName)?.get(key)?.get() as T?
- override suspend fun put(cacheName: String, key: String, value: T) = getCache(cacheName)?.put(key, value).unit()
- override suspend fun evict(cacheName: String, key: String) = getCache(cacheName)?.evict(key).unit()
-}
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/cache/CacheService.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/cache/CacheService.kt
deleted file mode 100644
index 0a908e9..0000000
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/cache/CacheService.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:Suppress("unused")
-
-package ru.sokomishalov.commons.spring.cache
-
-/**
- * @author sokomishalov
- */
-interface CacheService {
-
- companion object {
- const val DEFAULT_CACHE = "DEFAULT"
- }
-
- suspend fun get(cacheName: String = DEFAULT_CACHE, key: String): T?
-
- suspend fun put(cacheName: String = DEFAULT_CACHE, key: String, value: T)
-
- suspend fun evict(cacheName: String = DEFAULT_CACHE, key: String)
-
- suspend fun get(cacheName: String = DEFAULT_CACHE, key: String, orElse: suspend () -> T): T {
- val value = get(cacheName, key)
-
- return when {
- value != null -> value
- else -> {
- val orElseValue = orElse()
- put(cacheName, key, orElseValue)
- orElseValue
- }
- }
- }
-}
\ No newline at end of file
diff --git a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/net/NetUtils.kt b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/net/NetUtils.kt
index 99e3819..ee5edd4 100644
--- a/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/net/NetUtils.kt
+++ b/commons-spring/src/main/kotlin/ru/sokomishalov/commons/spring/net/NetUtils.kt
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+@file:Suppress("unused")
+
package ru.sokomishalov.commons.spring.net
import org.springframework.util.SocketUtils.*
diff --git a/commons-spring/src/main/resources/META-INF/spring.factories b/commons-spring/src/main/resources/META-INF/spring.factories
index 5166a17..23351ba 100644
--- a/commons-spring/src/main/resources/META-INF/spring.factories
+++ b/commons-spring/src/main/resources/META-INF/spring.factories
@@ -1,3 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
- ru.sokomishalov.commons.spring.autoconfigure.MongoReactiveAutoConfiguration,\
- ru.sokomishalov.commons.spring.autoconfigure.RedisLettuceAutoConfiguration
\ No newline at end of file
+ ru.sokomishalov.commons.spring.autoconfigure.InMemoryCacheAutoConfiguration,\
+ ru.sokomishalov.commons.spring.autoconfigure.MongoReactiveCacheAutoConfiguration,\
+ ru.sokomishalov.commons.spring.autoconfigure.MongoReactiveDistributedLocksAutoConfiguration,\
+ ru.sokomishalov.commons.spring.autoconfigure.RedisLettuceCacheAutoConfiguration,\
+ ru.sokomishalov.commons.spring.autoconfigure.RedisLettuceDistributedLocksAutoConfiguration
\ No newline at end of file
diff --git a/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/mongo/MongoClusterLockTest.kt b/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/mongo/MongoClusterLockTest.kt
deleted file mode 100644
index c125d9b..0000000
--- a/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/mongo/MongoClusterLockTest.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ru.sokomishalov.commons.spring.locks.cluster.mongo
-
-import com.mongodb.reactivestreams.client.MongoClients
-import de.flapdoodle.embed.mongo.MongodProcess
-import de.flapdoodle.embed.mongo.MongodStarter
-import de.flapdoodle.embed.mongo.config.MongodConfigBuilder
-import de.flapdoodle.embed.mongo.config.Net
-import de.flapdoodle.embed.mongo.distribution.Version.Main.PRODUCTION
-import de.flapdoodle.embed.process.runtime.Network.localhostIsIPv6
-import org.junit.After
-import org.junit.Before
-import ru.sokomishalov.commons.core.consts.LOCALHOST
-import ru.sokomishalov.commons.spring.locks.cluster.AbstractClusterLockTest
-import ru.sokomishalov.commons.spring.locks.cluster.LockProvider
-import ru.sokomishalov.commons.spring.net.randomFreePort
-
-
-/**
- * @author sokomishalov
- */
-class MongoClusterLockTest : AbstractClusterLockTest() {
-
- companion object {
- const val DATABASE = "admin"
- }
-
- private lateinit var mongoDaemon: MongodProcess
- private lateinit var client: LockProvider
-
- @Before
- fun setUp() {
- val randomPort = randomFreePort()
-
- mongoDaemon = MongodStarter
- .getDefaultInstance()
- .prepare(MongodConfigBuilder()
- .version(PRODUCTION)
- .net(Net(LOCALHOST, randomPort, localhostIsIPv6()))
- .build()
- )
- .start()
-
- client = MongoReactiveLockProvider(client = MongoClients.create("mongodb://$LOCALHOST:$randomPort/$DATABASE"))
- }
-
-
- override val provider: LockProvider
- get() = client
-
- @After
- fun tearDown() {
- mongoDaemon.stop()
- }
-}
\ No newline at end of file
diff --git a/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/redis/RedisClusterLockTest.kt b/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/redis/RedisClusterLockTest.kt
deleted file mode 100644
index d9dfade..0000000
--- a/commons-spring/src/test/kotlin/ru/sokomishalov/commons/spring/locks/cluster/redis/RedisClusterLockTest.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Copyright 2019-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ru.sokomishalov.commons.spring.locks.cluster.redis
-
-import io.lettuce.core.RedisClient
-import io.lettuce.core.RedisURI
-import org.junit.After
-import org.junit.Before
-import redis.embedded.RedisServer
-import ru.sokomishalov.commons.core.consts.LOCALHOST
-import ru.sokomishalov.commons.spring.locks.cluster.AbstractClusterLockTest
-import ru.sokomishalov.commons.spring.locks.cluster.LockProvider
-import ru.sokomishalov.commons.spring.net.randomFreePort
-
-class RedisClusterLockTest : AbstractClusterLockTest() {
-
- private lateinit var redisServer: RedisServer
- private lateinit var lettuceLockProvider: RedisLettuceLockProvider
-
- @Before
- fun setUp() {
- val randomPort = randomFreePort()
- redisServer = RedisServer(randomPort)
- redisServer.start()
-
- lettuceLockProvider = RedisLettuceLockProvider(RedisClient.create(RedisURI.create(LOCALHOST, randomPort)))
- }
-
- override val provider: LockProvider
- get() = lettuceLockProvider
-
- @After
- fun tearDown() {
- redisServer.stop()
- }
-}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 1046624..8eae4cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,16 +6,36 @@
ru.sokomishalov.commonscommons-parent
- 1.0.29
+ 1.1.0pom1.8
- 1.3.50
+ 1.3.61
+ 1.3.2
+ 2.10.1
+ 3.3.1.RELEASE
+ 0.9.2.RELEASE
+ 1.7.29
+ 1.2.3
+ 2.2.2.RELEASE
+ 5.2.2.RELEASE
+ 5.2.1.RELEASE
+ 1.9.5
+ 2.8.0
+ 2.9.2
+ 1.13.0
+ 1.12.3commons-core
+ commons-logging
+ commons-serialization
+ commons-reactor
+ commons-coroutines
+ commons-distributed-locks
+ commons-cachecommons-spring