Skip to content
This repository has been archived by the owner on Nov 2, 2021. It is now read-only.

Commit

Permalink
Merge pull request #24 from SokoMishaLov/feature/1.1.x
Browse files Browse the repository at this point in the history
1.1.0
  • Loading branch information
sokomishalov authored Dec 12, 2019
2 parents e3bf24b + eeb8432 commit 348c83e
Show file tree
Hide file tree
Showing 74 changed files with 2,695 additions and 706 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -28,4 +28,10 @@ Add the dependency:

Available modules now are:
- core
- logging
- serialization
- reactor
- coroutines
- distributed-locks
- cache
- spring
170 changes: 170 additions & 0 deletions commons-cache/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>ru.sokomishalov.commons</groupId>
<artifactId>commons-parent</artifactId>
<version>1.1.0</version>
</parent>

<artifactId>commons-cache</artifactId>
<version>1.1.0</version>

<dependencies>

<!-- COMPILE DEPS -->

<dependency>
<groupId>ru.sokomishalov.commons</groupId>
<artifactId>commons-core</artifactId>
<version>1.1.0</version>
</dependency>

<dependency>
<groupId>ru.sokomishalov.commons</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.0</version>
</dependency>

<dependency>
<groupId>ru.sokomishalov.commons</groupId>
<artifactId>commons-serialization</artifactId>
<version>1.1.0</version>
</dependency>

<dependency>
<groupId>ru.sokomishalov.commons</groupId>
<artifactId>commons-coroutines</artifactId>
<version>1.1.0</version>
</dependency>

<!-- OPTIONAL DEPS -->

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>${mongo-reactive.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>${lettuce.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- TEST DEPS -->

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${test-containers.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>test</scope>
</dependency>

</dependencies>


<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>${java.version}</jvmTarget>
<args>
<arg>-Xuse-experimental=kotlin.Experimental</arg>
</args>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>


</project>
Original file line number Diff line number Diff line change
@@ -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<String>

// ---------------------------------------------------------------------------------------------------------------------------------

suspend fun exists(key: String): Boolean {
return getRaw(key) != null
}

suspend fun <T> getOne(key: String, clazz: Class<T>): T? {
return getRaw(key)?.deserializeValue(clazz)
}

suspend fun <T> getList(key: String, clazz: Class<T>): List<T> {
val collectionType = mapper.typeFactory.constructCollectionType(List::class.java, clazz)
return getRaw(key)?.deserializeValue(collectionType) ?: emptyList()
}

suspend fun <T> getMap(key: String, clazz: Class<T>): Map<String, T> {
val mapType = mapper.typeFactory.constructMapType(HashMap::class.java, String::class.java, clazz)
return getRaw(key)?.deserializeValue(mapType) ?: emptyMap()
}

suspend fun <T> getFromMap(key: String, mapKey: String, clazz: Class<T>): T? {
return getMap(key, clazz)[mapKey]
}

suspend fun <T> 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<String> {
return findKeys("*")
}

suspend fun <T : Any> find(pattern: String, clazz: Class<T>): List<T> {
return findKeys(pattern).mapNotNull { getRaw(it)?.deserializeValue(clazz) }
}

suspend fun delete(keys: Iterable<String>) {
keys.forEach { delete(it) }
}

suspend fun deleteAll() {
findAllKeys().forEach { delete(it) }
}

// ---------------------------------------------------------------------------------------------------------------------------------

fun <T> T.serializeValue(): ByteArray = mapper.writeValueAsBytes(this)
fun <T> ByteArray.deserializeValue(clazz: Class<T>): T = mapper.readValue(this, clazz)
fun <T> ByteArray.deserializeValue(typeRef: TypeReference<T>): T = mapper.readValue(this, typeRef)
fun <T> ByteArray.deserializeValue(javaType: JavaType): T = mapper.readValue(this, javaType)
}
Original file line number Diff line number Diff line change
@@ -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 <reified T> CacheService.getOne(key: String, orElse: () -> T? = { null }): T? = getOne(key, T::class.java) ?: orElse()

suspend inline fun <reified T> CacheService.getList(key: String, ifEmpty: () -> List<T> = { emptyList() }): List<T> = getList(key, T::class.java).ifEmpty { ifEmpty() }

suspend inline fun <reified T> CacheService.getMap(key: String, ifEmpty: () -> Map<String, T> = { emptyMap() }): Map<String, T> = getMap(key, T::class.java).ifEmpty { ifEmpty() }

suspend inline fun <reified T> CacheService.getFromMap(key: String, mapKey: String, orElse: () -> T? = { null }): T? = getFromMap(key, mapKey, T::class.java) ?: orElse()

suspend inline fun <reified T : Any> CacheService.find(glob: String, ifEmpty: () -> List<T> = { emptyList() }): List<T> = find(glob, T::class.java).ifEmpty { ifEmpty() }
Original file line number Diff line number Diff line change
@@ -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<String, ByteArray> = 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<String> = map.keys.map { it.removePrefix() }.filter { glob.convertGlobToRegex().matches(it) }


override suspend fun deleteAll() = map.clear()

// override some methods for better performance
}
Loading

0 comments on commit 348c83e

Please sign in to comment.