Skip to content

Commit

Permalink
1. ttl() shortcut function
Browse files Browse the repository at this point in the history
2. ttl Delegate
3. ttl callable extensions
  • Loading branch information
rybalkinsd authored and rybalkinsd committed Dec 10, 2018
1 parent 19dbff2 commit 6fdc467
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 1 deletion.
15 changes: 15 additions & 0 deletions src/main/java/com/alibaba/ttl/TransmittableThreadLocalExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.alibaba.ttl

import kotlin.reflect.KProperty

fun <T> ttl() = TransmittableThreadLocal<T?>()

class TtlDelegate<T> {
private var _value = ttl<T>()

operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = _value.get()

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
_value.set(value)
}
}
61 changes: 61 additions & 0 deletions src/main/java/com/alibaba/ttl/TtlCallableExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.alibaba.ttl

import java.util.concurrent.Callable

@Throws(Exception::class)
operator fun <V> TtlCallable<V>.invoke(): V = call()

/**
* Extension function wrap {@link Callable} into {@link TtlCallable}.
* <p>
*
* @param releaseTtlValueReferenceAfterCall release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @param idempotent is idempotent or not. {@code true} will cover up bugs! <b>DO NOT</b> set, only when you know why.
* @return Wrapped {@link Callable}
*
* * @since TODO
*/
fun <V> Callable<V>.wrap(
releaseTtlValueReferenceAfterCall: Boolean = false,
idempotent: Boolean = false
): TtlCallable<V> = TtlCallable.get(this, releaseTtlValueReferenceAfterCall, idempotent)!!

/**
* Extension function wrap input {@link Callable} Collection to {@link TtlCallable} Collection.
*
* @param releaseTtlValueReferenceAfterCall release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @param idempotent is idempotent or not. {@code true} will cover up bugs! <b>DO NOT</b> set, only when you know why.
* @return Wrapped list of {@link Callable}
*
* @see #Callable::wrap
* @since TODO
*/
fun <V> List<Callable<V>>.wrap(
releaseTtlValueReferenceAfterCall: Boolean = false,
idempotent: Boolean = false
): List<TtlCallable<V>> = map { it.wrap(releaseTtlValueReferenceAfterCall, idempotent) }

/**
* Extension function to unwrap {@link TtlCallable} to the original/underneath one.
* <p>
* if input {@code Callable} parameter is not a {@link TtlCallable} just return input {@code Callable}.
* <p>
* so {@code callable.wrap().unwrap()} will always return the same input {@code callable} object.
*
* @since TODO
*/
fun <V> Callable<V>.unwrap(): Callable<V> = when(this) {
is TtlCallable<V> -> getCallable()
else -> this
}

/**
* Extension function to unwrap {@link TtlCallable} to the original/underneath one.
* <p>
* Invoke {@link #unwrap(Callable)} for each element in collection.
* <p>
*
* @see #Callable::unwrap
* @since TODO
*/
fun <V> List<Callable<V>>.unwrap() : List<Callable<V>> = map { it.unwrap() }
30 changes: 30 additions & 0 deletions src/test/java/com/alibaba/ttl/TransmittableThreadLocalExtKtTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.alibaba.ttl

import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import org.junit.Test
import kotlin.concurrent.thread

class TransmittableThreadLocalExtKtTest {

@Test
fun `ttl delegate`() {
var parent by TtlDelegate<String>()
parent = "value-set-in-parent"
thread {
val valueFromParent = parent
assertEquals("value-set-in-parent", valueFromParent)
}.join()

}

@Test
fun `ttl nullable delegate`() {
val parent by TtlDelegate<Int>()

thread {
assertNotNull("value-set-in-parent", parent)
}.join()

}
}
70 changes: 70 additions & 0 deletions src/test/java/com/alibaba/ttl/TtlCallableExtKtTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.alibaba.ttl

import com.alibaba.assertChildTtlValues
import com.alibaba.assertParentTtlValues
import com.alibaba.copyTtlValues
import com.alibaba.createParentTtlInstances
import com.alibaba.createParentTtlInstancesAfterCreateChild
import com.alibaba.ttl.testmodel.Call
import org.hamcrest.CoreMatchers
import org.junit.Assert.assertEquals
import org.junit.Assert.assertSame
import org.junit.Assert.assertThat
import org.junit.Assert.fail
import org.junit.Test

class TtlCallableExtKtTest {

@Test
fun `callable wrap extension function `() {
val call = Call("1")
val ttlCallable = call.wrap()
assertSame(call, ttlCallable.callable)
}

@Test
fun `callable wrap extension function multiple times`() {
val call = Call("1").wrap()
try {
call.wrap()
fail()
} catch (e: IllegalStateException) {
assertThat<String>(e.message, CoreMatchers.containsString("Already TtlCallable"))
}

}

@Test
fun `list of callable wrap extension function`() {
val callList = listOf(Call("1"), Call("2"), Call("3")).wrap()

assertEquals(3, callList.size)
callList.forEach {
assertThat(it, CoreMatchers.instanceOf(TtlCallable::class.java))
}
}

@Test
fun `TtlCallable invoke operator`() {
val ttlInstances = createParentTtlInstances()

val call = Call("1", ttlInstances)


val ttlCallable = call.wrap()

// create after new Task, won't see parent value in in task!
createParentTtlInstancesAfterCreateChild(ttlInstances)

// run in the *current* thread
assertEquals("ok", ttlCallable())


// child Inheritable
assertChildTtlValues("1", call.copied)

// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
}

}
5 changes: 4 additions & 1 deletion src/test/java/com/alibaba/ttl/testmodel/Call.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import java.util.concurrent.ConcurrentMap
/**
* @author Jerry Lee (oldratlee at gmail dot com)
*/
class Call(private val tag: String, private val ttlInstances: ConcurrentMap<String, TransmittableThreadLocal<String>> = ConcurrentHashMap()) : Callable<String> {
class Call(
private val tag: String,
private val ttlInstances: ConcurrentMap<String,TransmittableThreadLocal<String>> = ConcurrentHashMap()
) : Callable<String> {

lateinit var copied: Map<String, String>

Expand Down

0 comments on commit 6fdc467

Please sign in to comment.