Add failure cache to element loader
[Sone.git] / src / main / kotlin / net / pterodactylus / sone / core / DefaultElementLoader.kt
1 package net.pterodactylus.sone.core
2
3 import com.google.common.base.Ticker
4 import com.google.common.cache.CacheBuilder
5 import freenet.keys.FreenetURI
6 import java.io.ByteArrayInputStream
7 import java.util.concurrent.TimeUnit.MINUTES
8 import javax.imageio.ImageIO
9 import javax.inject.Inject
10
11 /**
12  * [ElementLoader] implementation that uses a simple Guava [com.google.common.cache.Cache].
13  */
14 class DefaultElementLoader @Inject constructor(private val freenetInterface: FreenetInterface, ticker: Ticker = Ticker.systemTicker()) : ElementLoader {
15
16         private val loadingLinks = CacheBuilder.newBuilder().build<String, Boolean>()
17         private val failureCache = CacheBuilder.newBuilder().ticker(ticker).expireAfterWrite(30, MINUTES).build<String, Boolean>()
18         private val imageCache = CacheBuilder.newBuilder().build<String, LinkedElement>()
19         private val callback = object : FreenetInterface.BackgroundFetchCallback {
20                 override fun cancelForMimeType(uri: FreenetURI, mimeType: String): Boolean {
21                         return !mimeType.startsWith("image/")
22                 }
23
24                 override fun loaded(uri: FreenetURI, mimeType: String, data: ByteArray) {
25                         if (!mimeType.startsWith("image/")) {
26                                 return
27                         }
28                         ByteArrayInputStream(data).use {
29                                 ImageIO.read(it)
30                         }?.let {
31                                 imageCache.get(uri.toString()) { LinkedElement(uri.toString()) }
32                         }
33                         removeLoadingLink(uri)
34                 }
35
36                 override fun failed(uri: FreenetURI) {
37                         failureCache.put(uri.toString(), true)
38                         removeLoadingLink(uri)
39                 }
40
41                 private fun removeLoadingLink(uri: FreenetURI) {
42                         synchronized(loadingLinks) {
43                                 loadingLinks.invalidate(uri.toString())
44                         }
45                 }
46         }
47
48         override fun loadElement(link: String): LinkedElement {
49                 synchronized(loadingLinks) {
50                         imageCache.getIfPresent(link)?.run {
51                                 return this
52                         }
53                         failureCache.getIfPresent(link)?.run {
54                                 return LinkedElement(link, failed = true)
55                         }
56                         if (loadingLinks.getIfPresent(link) == null) {
57                                 loadingLinks.put(link, true)
58                                 freenetInterface.startFetch(FreenetURI(link), callback)
59                         }
60                 }
61                 return LinkedElement(link, loading = true)
62         }
63
64 }