aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/ledger/utils
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-12-17 23:07:06 +0100
committerLinnea Gräf <nea@nea.moe>2024-12-17 23:07:06 +0100
commita046b198645811fe1b7db129942505c379aabb03 (patch)
treed97e68b1cf3a272aec5d8dd863b82fb0c9b0dc82 /src/main/kotlin/moe/nea/ledger/utils
parenta2f73de90fb9c9d0ea7a5e7e9e6b9e445a8094ee (diff)
downloadLocalTransactionLedger-a046b198645811fe1b7db129942505c379aabb03.tar.gz
LocalTransactionLedger-a046b198645811fe1b7db129942505c379aabb03.tar.bz2
LocalTransactionLedger-a046b198645811fe1b7db129942505c379aabb03.zip
feat: Add name prefetchingHEADmaster
Diffstat (limited to 'src/main/kotlin/moe/nea/ledger/utils')
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt5
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt10
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/di/DI.kt7
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt1
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/network/Request.kt40
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt63
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/network/Response.kt19
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt5
8 files changed, 146 insertions, 4 deletions
diff --git a/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt b/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt
index 4ba313e..344c8bc 100644
--- a/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt
+++ b/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt
@@ -10,6 +10,11 @@ class ErrorUtil {
@Inject
lateinit var reporter: EventRecorder
+ fun reportAdHoc(message: String) {
+ report(Exception(message), message)
+
+ }
+
fun report(exception: Throwable, message: String?) {
Span.current().recordException(reporter, exception, message)
}
diff --git a/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt b/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt
new file mode 100644
index 0000000..d3c1f6e
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/utils/GsonUtil.kt
@@ -0,0 +1,10 @@
+package moe.nea.ledger.utils
+
+import com.google.gson.reflect.TypeToken
+
+object GsonUtil {
+ inline fun <reified T> typeToken(): TypeToken<T> {
+ return object : TypeToken<T>() {}
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt b/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt
index 6940f72..133637a 100644
--- a/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt
+++ b/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt
@@ -6,6 +6,9 @@ import java.util.Stack
@Suppress("UNCHECKED_CAST")
class DI {
+ private fun formatInjectionStack() =
+ injectionStack.joinToString(" -> ")
+
private fun <T : Any, C> internalProvide(type: Class<T>, element: AnnotatedElement? = null): T {
val provider = providers[type] as BaseDIProvider<T, C>
val context = if (element == null) provider.createEmptyContext() else provider.createContext(element)
@@ -13,13 +16,13 @@ class DI {
val existingValue = values[key]
if (existingValue != null) return existingValue as T
if (type in injectionStack) {
- error("Found injection cycle: ${injectionStack.joinToString(" -> ")} -> $type")
+ error("Found injection cycle: ${formatInjectionStack()} -> $type")
}
injectionStack.push(type)
val value = try {
provider.provideWithContext(this, context)
} catch (ex: Exception) {
- throw RuntimeException("Could not create instance for type $type", ex)
+ throw RuntimeException("Could not create instance for type $type (in stack ${formatInjectionStack()})", ex)
}
val cycleCheckCookie = injectionStack.pop()
require(cycleCheckCookie == type) { "Unbalanced stack cookie: $cycleCheckCookie != $type" }
diff --git a/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt b/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt
index b5ce550..bd5b9ef 100644
--- a/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt
+++ b/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt
@@ -24,6 +24,7 @@ fun interface DIProvider<T : Any> : BaseDIProvider<T, Unit> {
?: clazz.constructors.find { it.parameterCount == 0 }
?: error("Could not find DI injection entrypoint for class $clazz"))
as Constructor<out T>
+ // TODO: consider using unsafe init to inject the parameters *before* calling the constructor
return DIProvider { di ->
val typArgs = cons.parameters.map {
di.provide(it.type, it)
diff --git a/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt b/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt
new file mode 100644
index 0000000..ddf2fcc
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/utils/network/Request.kt
@@ -0,0 +1,40 @@
+package moe.nea.ledger.utils.network
+
+import com.google.gson.JsonElement
+import java.net.URL
+
+data class Request(
+ val url: URL,
+ val method: Method,
+ val body: String?,
+ val headers: Map<String, String>,
+) {
+ enum class Method {
+ GET, POST
+ }
+
+ enum class MediaType(val text: String) {
+ JSON("application/json"),
+ TEXT("text/plain"),
+ HTML("text/html"),
+ ANY("*/*"),
+ }
+
+ fun withHeaders(map: Map<String, String>): Request {
+ // TODO: enforce caselessness?
+ return this.copy(headers = headers + map)
+ }
+
+ fun post() = copy(method = Method.POST)
+ fun get() = copy(method = Method.GET)
+
+ fun json(element: JsonElement) = copy(
+ headers = headers + mapOf("content-type" to "application/json"),
+ body = element.toString())
+
+ fun accept(request: MediaType) = withHeaders(mapOf("accept" to request.text))
+
+ fun acceptJson() = accept(MediaType.JSON)
+
+ fun execute(requestUtil: RequestUtil) = requestUtil.executeRequest(this)
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt b/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt
new file mode 100644
index 0000000..a49c65a
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt
@@ -0,0 +1,63 @@
+package moe.nea.ledger.utils.network
+
+import moe.nea.ledger.utils.ErrorUtil
+import moe.nea.ledger.utils.di.Inject
+import java.net.URL
+import java.net.URLConnection
+import java.security.KeyStore
+import java.util.zip.GZIPInputStream
+import javax.net.ssl.HttpsURLConnection
+import javax.net.ssl.KeyManagerFactory
+import javax.net.ssl.SSLContext
+import javax.net.ssl.TrustManagerFactory
+
+class RequestUtil @Inject constructor(val errorUtil: ErrorUtil) {
+
+ private fun createSSLContext(): SSLContext? = errorUtil.catch {
+ val keyStorePath = RequestUtil::class.java.getResourceAsStream("/ledgerkeystore.jks")
+ ?: error("Could not locate keystore")
+ val keyStore = KeyStore.getInstance("JKS")
+ keyStore.load(keyStorePath, "neuneu".toCharArray())
+ val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
+ val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
+ kmf.init(keyStore, null)
+ tmf.init(keyStore)
+ val ctx = SSLContext.getInstance("TLS")
+ ctx.init(kmf.keyManagers, tmf.trustManagers, null)
+ return@catch ctx
+ }
+
+ val sslContext = createSSLContext()
+
+ fun enhanceConnection(connection: URLConnection) {
+ if (connection is HttpsURLConnection && sslContext != null) {
+ connection.sslSocketFactory = sslContext.socketFactory
+ }
+ }
+
+ fun createRequest(url: String) = createRequest(URL(url))
+ fun createRequest(url: URL) = Request(url, Request.Method.GET, null, mapOf())
+
+ fun executeRequest(request: Request): Response {
+ val connection = request.url.openConnection()
+ enhanceConnection(connection)
+ connection.setRequestProperty("accept-encoding", "gzip")
+ request.headers.forEach { (k, v) ->
+ connection.setRequestProperty(k, v)
+ }
+ if (request.body != null) {
+ connection.getOutputStream().write(request.body.encodeToByteArray())
+ connection.getOutputStream().close()
+ }
+ var stream = connection.getInputStream()
+ if (connection.contentEncoding == "gzip") {
+ stream = GZIPInputStream(stream)
+ }
+ val text = stream.bufferedReader().readText()
+ stream.close()
+ // Do NOT call connection.disconnect() to allow for connection reuse
+ return Response(request, text, connection.headerFields)
+ }
+
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt b/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt
new file mode 100644
index 0000000..daae7f7
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/utils/network/Response.kt
@@ -0,0 +1,19 @@
+package moe.nea.ledger.utils.network
+
+import com.google.gson.reflect.TypeToken
+import moe.nea.ledger.Ledger
+
+data class Response(
+ val source: Request,
+ // TODO: allow other body processors, to avoid loading everything as strings
+ val response: String,
+ val headers: Map<String, List<String>>,
+) {
+ fun <T : Any> json(typ: TypeToken<T>): T {
+ return Ledger.gson.fromJson(response, typ.type)
+ }
+
+ fun <T : Any> json(clazz: Class<T>): T {
+ return Ledger.gson.fromJson(response, clazz)
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt b/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt
index df588a8..96b70ec 100644
--- a/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt
+++ b/src/main/kotlin/moe/nea/ledger/utils/telemetry/ExceptionContextValue.kt
@@ -22,8 +22,9 @@ class ExceptionContextValue(val exception: Throwable) : ContextValue {
obj.addProperty("message", exception.message)
// TODO: allow exceptions to implement an "extra info" interface
if (searchDepth > 0) {
- if (exception.cause != null) {
- obj.add("cause", walkExceptions(exception, searchDepth - 1))
+ val cause = exception.cause
+ if (cause != null && cause !== exception) {
+ obj.add("cause", walkExceptions(cause, searchDepth - 1))
}
val suppressions = JsonArray()
for (suppressedException in exception.suppressedExceptions) {