Skip to content

Commit

Permalink
fix(code convert) add FileStore, sessionStore and EventStore Kotlin c…
Browse files Browse the repository at this point in the history
…lasses
  • Loading branch information
YYChen01988 committed Nov 22, 2023
1 parent b156c17 commit d25636c
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 638 deletions.
4 changes: 1 addition & 3 deletions bugsnag-android-core/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:ConfigInternal.kt$ConfigInternal$fun getConfigDifferences(): Map&lt;String, Any></ID>
<ID>FunctionParameterNaming:EventStore.kt$EventStore$`object`: Any?</ID>
<ID>FunctionParameterNaming:FileStore.kt$FileStore$`object`: Any?</ID>
<ID>ImplicitDefaultLocale:DeliveryHeaders.kt$String.format("%02x", byte)</ID>
<ID>LongParameterList:App.kt$App$( /** * The architecture of the running application binary */ var binaryArch: String?, /** * The package name of the application */ var id: String?, /** * The release stage set in [Configuration.releaseStage] */ var releaseStage: String?, /** * The version of the application set in [Configuration.version] */ var version: String?, /** The revision ID from the manifest (React Native apps only) */ var codeBundleId: String?, /** * The unique identifier for the build of the application set in [Configuration.buildUuid] */ var buildUuid: String?, /** * The application type set in [Configuration#version] */ var type: String?, /** * The version code of the application set in [Configuration.versionCode] */ var versionCode: Number? )</ID>
<ID>LongParameterList:AppDataCollector.kt$AppDataCollector$( appContext: Context, private val packageManager: PackageManager?, private val config: ImmutableConfig, private val sessionTracker: SessionTracker, private val activityManager: ActivityManager?, private val launchCrashTracker: LaunchCrashTracker, private val memoryTrimState: MemoryTrimState )</ID>
Expand Down Expand Up @@ -56,10 +54,10 @@
<ID>SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$exc: Exception</ID>
<ID>SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$exception: Exception</ID>
<ID>SwallowedException:DeviceIdFilePersistence.kt$DeviceIdFilePersistence$exc: OverlappingFileLockException</ID>
<ID>SwallowedException:ImmutableConfig.kt$e: Exception</ID>
<ID>SwallowedException:EventStore.kt$EventStore$exception: RejectedExecutionException</ID>
<ID>SwallowedException:EventStore.kt$EventStore$ioe: Exception</ID>
<ID>SwallowedException:ForegroundDetector.kt$ForegroundDetector$e: Exception</ID>
<ID>SwallowedException:ImmutableConfig.kt$e: Exception</ID>
<ID>SwallowedException:JsonHelperTest.kt$JsonHelperTest$e: IllegalArgumentException</ID>
<ID>SwallowedException:PluginClient.kt$PluginClient$exc: ClassNotFoundException</ID>
<ID>SwallowedException:SharedPrefMigrator.kt$SharedPrefMigrator$e: RuntimeException</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ internal class CustomFileStore(
comparator: Comparator<in File?>,
delegate: Delegate?
) : FileStore(folder, maxStoreCount, comparator, NoopLogger, delegate) {
override fun getFilename(`object`: Any?) = "foo.json"
override fun getFilename(obj: Any?) = "foo.json"
}
150 changes: 65 additions & 85 deletions bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:Suppress("NAME_SHADOWING")

package com.bugsnag.android

import com.bugsnag.android.EventFilenameInfo.Companion.findTimestampInFilename
Expand All @@ -19,7 +17,6 @@ import java.util.concurrent.Future
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import kotlin.collections.ArrayList

/**
* Store and flush Event reports which couldn't be sent immediately due to
Expand All @@ -29,7 +26,7 @@ internal class EventStore(
private val config: ImmutableConfig,
logger: Logger,
notifier: Notifier,
bgTaskSevice: BackgroundTaskService,
bgTaskService: BackgroundTaskService,
delegate: Delegate?,
callbackState: CallbackState
) : FileStore(
Expand All @@ -39,9 +36,8 @@ internal class EventStore(
logger,
delegate
) {
private val delegate: Delegate?
private val notifier: Notifier
private val bgTaskSevice: BackgroundTaskService
private val bgTaskService: BackgroundTaskService
private val callbackState: CallbackState
override val logger: Logger

Expand All @@ -52,17 +48,17 @@ internal class EventStore(
if (!config.sendLaunchCrashesSynchronously) {
return
}
var future: Future<*>? = null
try {
future = bgTaskSevice.submitTask(
val future = try {
bgTaskService.submitTask(
TaskType.ERROR_REQUEST,
Runnable { flushLaunchCrashReport() }
)
} catch (exc: RejectedExecutionException) {
logger.d("Failed to flush launch crash reports, continuing.", exc)
return
}
try {
future?.get(LAUNCH_CRASH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
future.get(LAUNCH_CRASH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
} catch (exc: InterruptedException) {
logger.d("Failed to send launch crash reports within 2s timeout, continuing.", exc)
} catch (exc: ExecutionException) {
Expand All @@ -72,14 +68,12 @@ internal class EventStore(
}
}

fun flushLaunchCrashReport() {
private fun flushLaunchCrashReport() {
val storedFiles = findStoredFiles()
val launchCrashReport = findLaunchCrashReport(storedFiles)

// cancel non-launch crash reports
if (launchCrashReport != null) {
storedFiles.remove(launchCrashReport)
}
launchCrashReport?.let { storedFiles.remove(it) }
cancelQueuedFiles(storedFiles)
if (launchCrashReport != null) {
logger.i("Attempting to send the most recent launch crash report")
Expand All @@ -91,33 +85,24 @@ internal class EventStore(
}

fun findLaunchCrashReport(storedFiles: Collection<File>): File? {
val launchCrashes: ArrayList<File?> = ArrayList()
for (file in storedFiles) {
val filenameInfo = fromFile(file, config)
if (filenameInfo.isLaunchCrashReport()) {
launchCrashes.add(file)
}
}

// sort to get most recent timestamp
launchCrashes.sortWith(EVENT_COMPARATOR)
return if (launchCrashes.isEmpty()) null else launchCrashes[launchCrashes.size - 1]
return storedFiles
.asSequence()
.filter { fromFile(it, config).isLaunchCrashReport() }
.maxWithOrNull(EVENT_COMPARATOR)
}

fun writeAndDeliver(streamable: Streamable): Future<String>? {
val filename = write(streamable)
if (filename != null) {
try {
return bgTaskSevice.submitTask(
TaskType.ERROR_REQUEST,
Callable {
flushEventFile(File(filename))
filename
}
)
} catch (exception: RejectedExecutionException) {
logger.w("Failed to flush all on-disk errors, retaining unsent errors for later.")
}
val filename = write(streamable) ?: return null
try {
return bgTaskService.submitTask(
TaskType.ERROR_REQUEST,
Callable {
flushEventFile(File(filename))
filename
}
)
} catch (exception: RejectedExecutionException) {
logger.w("Failed to flush all on-disk errors, retaining unsent errors for later.")
}
return null
}
Expand All @@ -127,7 +112,7 @@ internal class EventStore(
*/
fun flushAsync() {
try {
bgTaskSevice.submitTask(
bgTaskService.submitTask(
TaskType.ERROR_REQUEST,
Runnable {
val storedFiles = findStoredFiles()
Expand All @@ -142,7 +127,7 @@ internal class EventStore(
}
}

fun flushReports(storedReports: Collection<File>) {
private fun flushReports(storedReports: Collection<File>) {
if (!storedReports.isEmpty()) {
val size = storedReports.size
logger.i("Sending $size saved error(s) to Bugsnag")
Expand All @@ -152,7 +137,7 @@ internal class EventStore(
}
}

fun flushEventFile(eventFile: File) {
private fun flushEventFile(eventFile: File) {
try {
val (apiKey) = fromFile(eventFile, config)
val payload = createEventPayload(eventFile, apiKey)
Expand All @@ -169,36 +154,40 @@ internal class EventStore(
private fun deliverEventPayload(eventFile: File, payload: EventPayload) {
val deliveryParams = config.getErrorApiDeliveryParams(payload)
val delivery = config.delivery
val deliveryStatus = delivery.deliver(payload, deliveryParams)
when (deliveryStatus) {
when (delivery.deliver(payload, deliveryParams)) {
DeliveryStatus.DELIVERED -> {
deleteStoredFiles(setOf(eventFile))
logger.i("Deleting sent error file " + eventFile.name)
}
DeliveryStatus.UNDELIVERED -> if (isTooBig(eventFile)) {
logger.w(
"Discarding over-sized event (" + eventFile.length() + ") after failed delivery"
)
deleteStoredFiles(setOf(eventFile))
} else if (isTooOld(eventFile)) {
logger.w(
"Discarding historical event (from " + getCreationDate(eventFile) + ") after failed delivery"
)
deleteStoredFiles(setOf(eventFile))
} else {
cancelQueuedFiles(setOf(eventFile))
logger.w(
"Could not send previously saved error(s)" + " to Bugsnag, will try again later"
)
logger.i("Deleting sent error file $eventFile.name")
}
DeliveryStatus.UNDELIVERED -> undeliveredEventPayload(eventFile)
DeliveryStatus.FAILURE -> {
val exc: Exception = RuntimeException("Failed to deliver event payload")
handleEventFlushFailure(exc, eventFile)
}
}
}

private fun undeliveredEventPayload(eventFile: File) {
if (isTooBig(eventFile)) {
logger.w(
"Discarding over-sized event (${eventFile.length()}) after failed delivery"
)
deleteStoredFiles(setOf(eventFile))
} else if (isTooOld(eventFile)) {
logger.w(
"Discarding historical event (from ${getCreationDate(eventFile)}) after failed delivery"
)
deleteStoredFiles(setOf(eventFile))
} else {
cancelQueuedFiles(setOf(eventFile))
logger.w(
"Could not send previously saved error(s) to Bugsnag, will try again later"
)
}
}

private fun createEventPayload(eventFile: File, apiKey: String): EventPayload? {
@Suppress("NAME_SHADOWING")
var apiKey: String? = apiKey
val eventSource = MarshalledEventSource(eventFile, apiKey!!, logger)
try {
Expand All @@ -223,54 +212,45 @@ internal class EventStore(
deleteStoredFiles(setOf(eventFile))
}

override fun getFilename(`object`: Any?): String {
val eventInfo = `object`?.let { fromEvent(obj = it, apiKey = null, config = config) }
if (eventInfo != null) {
return eventInfo.encode()
}
return ""
override fun getFilename(obj: Any?): String {
return obj?.let { fromEvent(obj = it, apiKey = null, config = config) }?.encode() ?: ""
}

fun getNdkFilename(`object`: Any?, apiKey: String?): String {
val eventInfo = fromEvent(obj = `object`!!, apiKey = apiKey, config = config)
return eventInfo.encode()
fun getNdkFilename(obj: Any?, apiKey: String?): String {
return obj?.let { fromEvent(obj = it, apiKey = apiKey, config = config) }?.encode() ?: ""
}

init {
this.logger = logger
this.delegate = delegate
this.notifier = notifier
this.bgTaskSevice = bgTaskSevice
this.bgTaskService = bgTaskService
this.callbackState = callbackState
}

fun isTooBig(file: File): Boolean {
private fun isTooBig(file: File): Boolean {
return file.length() > oneMegabyte
}

fun isTooOld(file: File?): Boolean {
private fun isTooOld(file: File): Boolean {
val cal = Calendar.getInstance()
cal.add(Calendar.DATE, -60)
return findTimestampInFilename(file!!) < cal.timeInMillis
return findTimestampInFilename(file) < cal.timeInMillis
}

fun getCreationDate(file: File?): Date {
return Date(findTimestampInFilename(file!!))
private fun getCreationDate(file: File): Date {
return Date(findTimestampInFilename(file))
}

companion object {
private const val LAUNCH_CRASH_TIMEOUT_MS: Long = 2000
val EVENT_COMPARATOR: Comparator<in File?> = Comparator { lhs, rhs ->
if (lhs == null && rhs == null) {
return@Comparator 0
}
if (lhs == null) {
return@Comparator 1
when {
lhs == null && rhs == null -> 0
lhs == null -> 1
rhs == null -> -1
else -> lhs.compareTo(rhs)
}
if (rhs == null) {
-1
} else lhs.compareTo(rhs)
}
private const val oneMegabyte = (1024 * 1024).toLong()
private const val oneMegabyte = 1024L * 1024L
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock

internal abstract class FileStore(
storageDir: File,
private val storageDir: File,
private val maxStoreCount: Int,
private val comparator: Comparator<in File?>,
protected open val logger: Logger,
delegate: Delegate?
protected val delegate: Delegate?
) {
internal interface Delegate {
internal fun interface Delegate {
/**
* Invoked when an error report is not (de)serialized correctly
*
Expand All @@ -31,14 +31,10 @@ internal abstract class FileStore(
fun onErrorIOFailure(exception: Exception?, errorFile: File?, context: String?)
}

private val storageDir: File
private val lock: Lock = ReentrantLock()
private val queuedFiles: MutableCollection<File> = ConcurrentSkipListSet()
private val delegate: Delegate?

init {
this.delegate = delegate
this.storageDir = storageDir
isStorageDirValid(storageDir)
}

Expand Down Expand Up @@ -119,7 +115,7 @@ internal abstract class FileStore(
// Limit number of saved payloads to prevent disk space issues
if (isStorageDirValid(storageDir)) {
val listFiles = storageDir.listFiles() ?: return
val files: ArrayList<File> = ArrayList(listOf(*listFiles))
val files: ArrayList<File> = arrayListOf(*listFiles)
if (files.size >= maxStoreCount) {
// Sort files then delete the first one (oldest timestamp)
Collections.sort(files, comparator)
Expand All @@ -141,7 +137,7 @@ internal abstract class FileStore(
}
}

abstract fun getFilename(`object`: Any?): String
abstract fun getFilename(obj: Any?): String

fun findStoredFiles(): MutableList<File> {
lock.lock()
Expand Down
Loading

0 comments on commit d25636c

Please sign in to comment.