Split LabelRepository off the MessageRepository

This commit is contained in:
2017-11-26 20:30:05 +01:00
parent ddb2073c2f
commit 278d5b05e6
20 changed files with 407 additions and 236 deletions

View File

@ -73,6 +73,9 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
val addresses: AddressRepository
@JvmName("addresses") get
val labels: LabelRepository
@JvmName("labels") get
val messages: MessageRepository
@JvmName("messages") get
@ -301,6 +304,7 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
var nodeRegistry by Delegates.notNull<NodeRegistry>()
var networkHandler by Delegates.notNull<NetworkHandler>()
var addressRepo by Delegates.notNull<AddressRepository>()
var labelRepo by Delegates.notNull<LabelRepository>()
var messageRepo by Delegates.notNull<MessageRepository>()
var proofOfWorkRepo by Delegates.notNull<ProofOfWorkRepository>()
var proofOfWorkEngine: ProofOfWorkEngine? = null
@ -330,6 +334,11 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
return this
}
fun labelRepo(labelRepo: LabelRepository): Builder {
this.labelRepo = labelRepo
return this
}
fun messageRepo(messageRepo: MessageRepository): Builder {
this.messageRepo = messageRepo
return this
@ -386,6 +395,7 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
builder.nodeRegistry,
builder.networkHandler,
builder.addressRepo,
builder.labelRepo,
builder.messageRepo,
builder.proofOfWorkRepo,
builder.proofOfWorkEngine ?: MultiThreadedPOWEngine(),
@ -400,6 +410,7 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
builder.preferences
)
this.addresses = builder.addressRepo
this.labels = builder.labelRepo
this.messages = builder.messageRepo
(builder.listener as? Listener.WithContext)?.setContext(this)
internals.proofOfWorkService.doMissingProofOfWork(builder.preferences.doMissingProofOfWorkDelayInSeconds * 1000L)

View File

@ -40,11 +40,12 @@ import java.util.concurrent.Executors
*/
class InternalContext(
val cryptography: Cryptography,
val inventory: ch.dissem.bitmessage.ports.Inventory,
val inventory: Inventory,
val nodeRegistry: NodeRegistry,
val networkHandler: NetworkHandler,
val addressRepository: AddressRepository,
val messageRepository: ch.dissem.bitmessage.ports.MessageRepository,
val labelRepository: LabelRepository,
val messageRepository: MessageRepository,
val proofOfWorkRepository: ProofOfWorkRepository,
val proofOfWorkEngine: ProofOfWorkEngine,
val customCommandHandler: CustomCommandHandler,
@ -216,7 +217,9 @@ class InternalContext(
companion object {
private val LOG = LoggerFactory.getLogger(InternalContext::class.java)
@JvmField val NETWORK_NONCE_TRIALS_PER_BYTE: Long = 1000
@JvmField val NETWORK_EXTRA_BYTES: Long = 1000
@JvmField
val NETWORK_NONCE_TRIALS_PER_BYTE: Long = 1000
@JvmField
val NETWORK_EXTRA_BYTES: Long = 1000
}
}

View File

@ -35,6 +35,7 @@ import java.nio.ByteBuffer
import java.util.*
import java.util.Collections
import kotlin.collections.HashSet
import kotlin.collections.LinkedHashSet
private fun message(encoding: Plaintext.Encoding, subject: String, body: String): ByteArray = when (encoding) {
SIMPLE -> "Subject:$subject\nBody:$body".toByteArray()
@ -254,7 +255,7 @@ class Plaintext private constructor(
received = builder.received,
initialHash = null,
ttl = builder.ttl,
labels = builder.labels,
labels = LinkedHashSet(builder.labels),
status = builder.status ?: Status.RECEIVED
) {
id = builder.id
@ -278,28 +279,30 @@ class Plaintext private constructor(
get() {
val s = Scanner(ByteArrayInputStream(message), "UTF-8")
val firstLine = s.nextLine()
if (encodingCode == EXTENDED.code) {
if (Message.TYPE == extendedData?.type) {
return (extendedData!!.content as? Message)?.subject
return when (encodingCode) {
EXTENDED.code -> if (Message.TYPE == extendedData?.type) {
(extendedData!!.content as? Message)?.subject
} else {
return null
null
}
SIMPLE.code -> firstLine.substring("Subject:".length).trim { it <= ' ' }
else -> {
if (firstLine.length > 50) {
firstLine.substring(0, 50).trim { it <= ' ' } + "..."
} else {
firstLine
}
}
} else if (encodingCode == SIMPLE.code) {
return firstLine.substring("Subject:".length).trim { it <= ' ' }
} else if (firstLine.length > 50) {
return firstLine.substring(0, 50).trim { it <= ' ' } + "..."
} else {
return firstLine
}
}
val text: String?
get() {
if (encodingCode == EXTENDED.code) {
if (Message.TYPE == extendedData?.type) {
return (extendedData?.content as Message?)?.body
return if (Message.TYPE == extendedData?.type) {
(extendedData?.content as Message?)?.body
} else {
return null
null
}
} else {
val text = String(message)
@ -322,20 +325,20 @@ class Plaintext private constructor(
val parents: List<InventoryVector>
get() {
val extendedData = extendedData ?: return emptyList()
if (Message.TYPE == extendedData.type) {
return (extendedData.content as Message).parents
return if (Message.TYPE == extendedData.type) {
(extendedData.content as Message).parents
} else {
return emptyList()
emptyList()
}
}
val files: List<Attachment>
get() {
val extendedData = extendedData ?: return emptyList()
if (Message.TYPE == extendedData.type) {
return (extendedData.content as Message).files
return if (Message.TYPE == extendedData.type) {
(extendedData.content as Message).files
} else {
return emptyList()
emptyList()
}
}
@ -378,8 +381,8 @@ class Plaintext private constructor(
override fun toString(): String {
val subject = subject
if (subject?.isNotEmpty() ?: false) {
return subject!!
if (subject?.isNotEmpty() == true) {
return subject
} else {
return Strings.hex(
initialHash ?: return super.toString()
@ -529,32 +532,43 @@ class Plaintext private constructor(
}
class Builder(internal val type: Type) {
internal var id: Any? = null
internal var inventoryVector: InventoryVector? = null
internal var from: BitmessageAddress? = null
internal var to: BitmessageAddress? = null
private var addressVersion: Long = 0
private var stream: Long = 0
private var behaviorBitfield: Int = 0
private var publicSigningKey: ByteArray? = null
private var publicEncryptionKey: ByteArray? = null
private var nonceTrialsPerByte: Long = 0
private var extraBytes: Long = 0
private var destinationRipe: ByteArray? = null
private var preventAck: Boolean = false
internal var encoding: Long = 0
internal var message = ByteArray(0)
internal var ackData: ByteArray? = null
internal var ackMessage: ByteArray? = null
internal var signature: ByteArray? = null
internal var sent: Long? = null
internal var received: Long? = null
internal var status: Status? = null
internal val labels = LinkedHashSet<Label>()
internal var ttl: Long = 0
internal var retries: Int = 0
internal var nextTry: Long? = null
internal var conversation: UUID? = null
var id: Any? = null
var inventoryVector: InventoryVector? = null
var from: BitmessageAddress? = null
var to: BitmessageAddress? = null
set(value) {
if (value != null) {
if (type != MSG && to != null)
throw IllegalArgumentException("recipient address only allowed for msg")
field = value
}
}
var addressVersion: Long = 0
var stream: Long = 0
var behaviorBitfield: Int = 0
var publicSigningKey: ByteArray? = null
var publicEncryptionKey: ByteArray? = null
var nonceTrialsPerByte: Long = 0
var extraBytes: Long = 0
var destinationRipe: ByteArray? = null
set(value) {
if (type != MSG && value != null) throw IllegalArgumentException("ripe only allowed for msg")
field = value
}
var preventAck: Boolean = false
var encoding: Long = 0
var message = ByteArray(0)
var ackData: ByteArray? = null
var ackMessage: ByteArray? = null
var signature: ByteArray? = null
var sent: Long? = null
var received: Long? = null
var status: Status? = null
var labels: Collection<Label> = emptySet()
var ttl: Long = 0
var retries: Int = 0
var nextTry: Long? = null
var conversation: UUID? = null
fun id(id: Any): Builder {
this.id = id
@ -572,11 +586,7 @@ class Plaintext private constructor(
}
fun to(address: BitmessageAddress?): Builder {
if (address != null) {
if (type != MSG && to != null)
throw IllegalArgumentException("recipient address only allowed for msg")
to = address
}
to = address
return this
}
@ -616,7 +626,6 @@ class Plaintext private constructor(
}
fun destinationRipe(ripe: ByteArray?): Builder {
if (type != MSG && ripe != null) throw IllegalArgumentException("ripe only allowed for msg")
this.destinationRipe = ripe
return this
}
@ -692,7 +701,7 @@ class Plaintext private constructor(
}
fun labels(labels: Collection<Label>): Builder {
this.labels.addAll(labels)
this.labels = labels
return this
}
@ -743,6 +752,12 @@ class Plaintext private constructor(
return this
}
@JvmSynthetic
inline fun build(block: Builder.() -> Unit): Plaintext {
block(this)
return build()
}
fun build(): Plaintext {
return Plaintext(this)
}
@ -774,5 +789,13 @@ class Plaintext private constructor(
.message(Decode.varBytes(input))
.ackMessage(if (type == MSG) Decode.varBytes(input) else null)
}
@JvmSynthetic
inline fun build(type: Type, block: Builder.() -> Unit): Plaintext {
val builder = Builder(type)
block(builder)
return builder.build()
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2017 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.bitmessage.ports
import ch.dissem.bitmessage.entity.valueobject.Label
import ch.dissem.bitmessage.utils.SqlStrings.join
abstract class AbstractLabelRepository : LabelRepository {
override fun getLabels(): List<Label> {
return find("1=1")
}
override fun getLabels(vararg types: Label.Type): List<Label> {
return find("type IN (${join(*types)})")
}
protected abstract fun find(where: String): List<Label>
}

View File

@ -21,8 +21,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.Plaintext
import ch.dissem.bitmessage.entity.valueobject.InventoryVector
import ch.dissem.bitmessage.entity.valueobject.Label
import ch.dissem.bitmessage.exception.ApplicationException
import ch.dissem.bitmessage.utils.SqlStrings.join
import ch.dissem.bitmessage.utils.Collections.single
import ch.dissem.bitmessage.utils.Strings
import ch.dissem.bitmessage.utils.UnixTime
import java.util.*
@ -114,25 +113,6 @@ abstract class AbstractMessageRepository : MessageRepository, InternalContext.Co
return find("conversation=X'${conversationId.toString().replace("-", "")}'")
}
override fun getLabels(): List<Label> {
return findLabels("1=1")
}
override fun getLabels(vararg types: Label.Type): List<Label> {
return findLabels("type IN (${join(*types)})")
}
protected abstract fun findLabels(where: String): List<Label>
protected fun <T> single(collection: Collection<T>): T? {
return when (collection.size) {
0 -> null
1 -> collection.iterator().next()
else -> throw ApplicationException("This shouldn't happen, found ${collection.size} items, one or none was expected")
}
}
/**
* Finds messages that mach the given where statement, with optional offset and limit. If the limit is set to 0,
* offset and limit are ignored.

View File

@ -35,9 +35,9 @@ open class DefaultLabeler : Labeler, InternalContext.ContextHolder {
msg.status = RECEIVED
val labelsToAdd =
if (msg.type == BROADCAST) {
ctx.messageRepository.getLabels(Label.Type.BROADCAST, Label.Type.UNREAD)
ctx.labelRepository.getLabels(Label.Type.BROADCAST, Label.Type.UNREAD)
} else {
ctx.messageRepository.getLabels(Label.Type.INBOX, Label.Type.UNREAD)
ctx.labelRepository.getLabels(Label.Type.INBOX, Label.Type.UNREAD)
}
msg.addLabels(labelsToAdd)
listener?.invoke(msg, labelsToAdd, emptyList())
@ -45,7 +45,7 @@ open class DefaultLabeler : Labeler, InternalContext.ContextHolder {
override fun markAsDraft(msg: Plaintext) {
msg.status = DRAFT
val labelsToAdd = ctx.messageRepository.getLabels(Label.Type.DRAFT)
val labelsToAdd = ctx.labelRepository.getLabels(Label.Type.DRAFT)
msg.addLabels(labelsToAdd)
listener?.invoke(msg, labelsToAdd, emptyList())
}
@ -58,7 +58,7 @@ open class DefaultLabeler : Labeler, InternalContext.ContextHolder {
}
val labelsToRemove = msg.labels.filter { it.type == Label.Type.DRAFT }
msg.removeLabel(Label.Type.DRAFT)
val labelsToAdd = ctx.messageRepository.getLabels(Label.Type.OUTBOX)
val labelsToAdd = ctx.labelRepository.getLabels(Label.Type.OUTBOX)
msg.addLabels(labelsToAdd)
listener?.invoke(msg, labelsToAdd, labelsToRemove)
}
@ -67,7 +67,7 @@ open class DefaultLabeler : Labeler, InternalContext.ContextHolder {
msg.status = SENT
val labelsToRemove = msg.labels.filter { it.type == Label.Type.OUTBOX }
msg.removeLabel(Label.Type.OUTBOX)
val labelsToAdd = ctx.messageRepository.getLabels(Label.Type.SENT)
val labelsToAdd = ctx.labelRepository.getLabels(Label.Type.SENT)
msg.addLabels(labelsToAdd)
listener?.invoke(msg, labelsToAdd, labelsToRemove)
}
@ -83,7 +83,7 @@ open class DefaultLabeler : Labeler, InternalContext.ContextHolder {
}
override fun markAsUnread(msg: Plaintext) {
val labelsToAdd = ctx.messageRepository.getLabels(Label.Type.UNREAD)
val labelsToAdd = ctx.labelRepository.getLabels(Label.Type.UNREAD)
msg.addLabels(labelsToAdd)
listener?.invoke(msg, labelsToAdd, emptyList())
}
@ -91,7 +91,7 @@ open class DefaultLabeler : Labeler, InternalContext.ContextHolder {
override fun delete(msg: Plaintext) {
val labelsToRemove = msg.labels.toSet()
msg.labels.clear()
val labelsToAdd = ctx.messageRepository.getLabels(Label.Type.TRASH)
val labelsToAdd = ctx.labelRepository.getLabels(Label.Type.TRASH)
msg.addLabels(labelsToAdd)
listener?.invoke(msg, labelsToAdd, labelsToRemove)
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2017 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.bitmessage.ports
import ch.dissem.bitmessage.entity.valueobject.Label
interface LabelRepository {
fun getLabels(): List<Label>
fun getLabels(vararg types: Label.Type): List<Label>
fun save(label: Label)
}

View File

@ -24,12 +24,6 @@ import ch.dissem.bitmessage.entity.valueobject.Label
import java.util.*
interface MessageRepository {
fun getLabels(): List<Label>
fun getLabels(vararg types: Label.Type): List<Label>
fun save(label: Label)
fun countUnread(label: Label?): Int
fun getAllMessages(): List<Plaintext>

View File

@ -16,6 +16,7 @@
package ch.dissem.bitmessage.utils
import ch.dissem.bitmessage.exception.ApplicationException
import java.util.*
object Collections {
@ -67,4 +68,12 @@ object Collections {
}
throw IllegalArgumentException("Empty collection? Size: " + collection.size)
}
@JvmStatic fun <T> single(collection: Collection<T>): T? {
return when (collection.size) {
0 -> null
1 -> collection.iterator().next()
else -> throw ApplicationException("This shouldn't happen, found ${collection.size} items, one or none was expected")
}
}
}

View File

@ -98,6 +98,7 @@ class BitmessageContextTest {
cryptography = BouncyCryptography()
inventory = testInventory
listener = testListener
labelRepo = mock()
messageRepo = mock()
networkHandler = mock {
on { getNetworkStatus() } doReturn Property("test", "mocked")

View File

@ -113,6 +113,7 @@ object TestUtils {
nodeRegistry: NodeRegistry = mock {},
networkHandler: NetworkHandler = mock {},
addressRepository: AddressRepository = mock {},
labelRepository: LabelRepository = mock {},
messageRepository: MessageRepository = mock {},
proofOfWorkRepository: ProofOfWorkRepository = mock {},
proofOfWorkEngine: ProofOfWorkEngine = mock {},
@ -129,6 +130,7 @@ object TestUtils {
nodeRegistry,
networkHandler,
addressRepository,
labelRepository,
messageRepository,
proofOfWorkRepository,
proofOfWorkEngine,