mirror of https://github.com/usbharu/Hideout.git
				
				
				
			feat: 汎用ResourceResolverを追加
This commit is contained in:
		
							parent
							
								
									1db8f31b5f
								
							
						
					
					
						commit
						bb87cab45f
					
				|  | @ -0,0 +1,6 @@ | |||
| package dev.usbharu.hideout.core.service.resource | ||||
| 
 | ||||
| interface CacheManager { | ||||
|     suspend fun putCache(key: String, block: suspend () -> ResolveResponse) | ||||
|     suspend fun getOrWait(key: String): ResolveResponse | ||||
| } | ||||
|  | @ -0,0 +1,49 @@ | |||
| package dev.usbharu.hideout.core.service.resource | ||||
| 
 | ||||
| import dev.usbharu.hideout.util.LruCache | ||||
| import kotlinx.coroutines.delay | ||||
| import kotlinx.coroutines.sync.Mutex | ||||
| import kotlinx.coroutines.sync.withLock | ||||
| import org.springframework.stereotype.Service | ||||
| import java.time.Instant | ||||
| 
 | ||||
| @Service | ||||
| class InMemoryCacheManager : CacheManager { | ||||
|     private val cacheKey = LruCache<String, Long>(15) | ||||
|     private val valueStore = mutableMapOf<String, ResolveResponse>() | ||||
|     private val keyMutex = Mutex() | ||||
| 
 | ||||
|     override suspend fun putCache(key: String, block: suspend () -> ResolveResponse) { | ||||
|         val needRunBlock: Boolean | ||||
|         keyMutex.withLock { | ||||
|             cacheKey.filter { Instant.ofEpochMilli(it.value).plusSeconds(300) <= Instant.now() } | ||||
| 
 | ||||
|             val cached = cacheKey.get(key) | ||||
|             if (cached == null) { | ||||
|                 needRunBlock = true | ||||
|                 cacheKey[key] = Instant.now().toEpochMilli() | ||||
| 
 | ||||
|                 valueStore.remove(key) | ||||
|             } else { | ||||
|                 needRunBlock = false | ||||
|             } | ||||
|         } | ||||
|         if (needRunBlock) { | ||||
|             val processed = block() | ||||
| 
 | ||||
|             if (cacheKey.containsKey(key)) { | ||||
|                 valueStore[key] = processed | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun getOrWait(key: String): ResolveResponse { | ||||
|         while (valueStore.contains(key).not()) { | ||||
|             if (cacheKey.containsKey(key).not()) { | ||||
|                 throw IllegalStateException("Invalid cache key.") | ||||
|             } | ||||
|             delay(1) | ||||
|         } | ||||
|         return valueStore.getValue(key) | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,14 @@ | |||
| package dev.usbharu.hideout.core.service.resource | ||||
| 
 | ||||
| import io.ktor.client.statement.* | ||||
| import io.ktor.util.* | ||||
| import io.ktor.utils.io.jvm.javaio.* | ||||
| import java.io.InputStream | ||||
| 
 | ||||
| class KtorResolveResponse(val ktorHttpResponse: HttpResponse) : ResolveResponse { | ||||
| 
 | ||||
|     override suspend fun body(): InputStream = ktorHttpResponse.bodyAsChannel().toInputStream() | ||||
|     override suspend fun header(): Map<String, List<String>> = ktorHttpResponse.headers.toMap() | ||||
|     override suspend fun status(): Int = ktorHttpResponse.status.value | ||||
|     override suspend fun statusMessage(): String = ktorHttpResponse.status.description | ||||
| } | ||||
|  | @ -0,0 +1,24 @@ | |||
| package dev.usbharu.hideout.core.service.resource | ||||
| 
 | ||||
| import io.ktor.client.* | ||||
| import io.ktor.client.request.* | ||||
| import org.springframework.stereotype.Service | ||||
| 
 | ||||
| @Service | ||||
| open class KtorResourceResolveService(private val httpClient: HttpClient, private val cacheManager: CacheManager) : | ||||
|     ResourceResolveService { | ||||
|     override suspend fun resolve(url: String): ResolveResponse { | ||||
|         cacheManager.putCache(getCacheKey(url)) { | ||||
|             runResolve(url) | ||||
|         } | ||||
|         return cacheManager.getOrWait(getCacheKey(url)) | ||||
|     } | ||||
| 
 | ||||
|     protected suspend fun runResolve(url: String): ResolveResponse { | ||||
|         val httpResponse = httpClient.get(url) | ||||
| 
 | ||||
|         return KtorResolveResponse(httpResponse) | ||||
|     } | ||||
| 
 | ||||
|     protected suspend fun getCacheKey(url: String) = url | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| package dev.usbharu.hideout.core.service.resource | ||||
| 
 | ||||
| import java.io.InputStream | ||||
| 
 | ||||
| interface ResolveResponse { | ||||
|     suspend fun body(): InputStream | ||||
|     suspend fun header(): Map<String, List<String>> | ||||
|     suspend fun status(): Int | ||||
|     suspend fun statusMessage(): String | ||||
| } | ||||
|  | @ -0,0 +1,5 @@ | |||
| package dev.usbharu.hideout.core.service.resource | ||||
| 
 | ||||
| interface ResourceResolveService { | ||||
|     suspend fun resolve(url: String): ResolveResponse | ||||
| } | ||||
		Loading…
	
		Reference in New Issue