Use SLF4J for logging.
This commit is contained in:
parent
8bafd62e35
commit
2cc4928a99
17 changed files with 82 additions and 148 deletions
|
@ -17,6 +17,7 @@ dependencies {
|
||||||
compile 'com.mobsandgeeks:adapter-kit:0.5.3'
|
compile 'com.mobsandgeeks:adapter-kit:0.5.3'
|
||||||
compile 'com.google.zxing:android-integration:3.2.1'
|
compile 'com.google.zxing:android-integration:3.2.1'
|
||||||
compile 'com.google.zxing:core:3.2.1'
|
compile 'com.google.zxing:core:3.2.1'
|
||||||
|
compile 'org.slf4j:slf4j-android:1.7.21'
|
||||||
compile project(path: ':core')
|
compile project(path: ':core')
|
||||||
androidTestCompile 'com.android.support:multidex-instrumentation:1.0.1',
|
androidTestCompile 'com.android.support:multidex-instrumentation:1.0.1',
|
||||||
{ exclude module: 'multidex' }
|
{ exclude module: 'multidex' }
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package com.nutomic.ensichat
|
package com.nutomic.ensichat
|
||||||
|
|
||||||
import android.support.multidex.MultiDexApplication
|
import android.support.multidex.MultiDexApplication
|
||||||
import com.nutomic.ensichat.core.interfaces.Log
|
import com.nutomic.ensichat.util.PRNGFixes
|
||||||
import com.nutomic.ensichat.util.{Logging, PRNGFixes}
|
|
||||||
|
|
||||||
class App extends MultiDexApplication {
|
class App extends MultiDexApplication {
|
||||||
|
|
||||||
override def onCreate(): Unit = {
|
override def onCreate(): Unit = {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Log.setLogInstance(new Logging())
|
|
||||||
PRNGFixes.apply()
|
PRNGFixes.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package com.nutomic.ensichat.util
|
|
||||||
|
|
||||||
import android.util
|
|
||||||
import com.nutomic.ensichat.core.interfaces.Log
|
|
||||||
|
|
||||||
class Logging extends Log {
|
|
||||||
|
|
||||||
def v(tag: String, message: String, tr: Throwable = null) = util.Log.v(tag, message, tr)
|
|
||||||
def d(tag: String, message: String, tr: Throwable = null) = util.Log.d(tag, message, tr)
|
|
||||||
def i(tag: String, message: String, tr: Throwable = null) = util.Log.i(tag, message, tr)
|
|
||||||
def w(tag: String, message: String, tr: Throwable = null) = util.Log.w(tag, message, tr)
|
|
||||||
def e(tag: String, message: String, tr: Throwable = null) = util.Log.e(tag, message, tr)
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ dependencies {
|
||||||
compile 'org.scala-lang:scala-library:2.11.7'
|
compile 'org.scala-lang:scala-library:2.11.7'
|
||||||
compile 'com.h2database:h2:1.4.191'
|
compile 'com.h2database:h2:1.4.191'
|
||||||
compile 'com.typesafe.slick:slick_2.11:3.1.1'
|
compile 'com.typesafe.slick:slick_2.11:3.1.1'
|
||||||
|
compile 'com.typesafe.scala-logging:scala-logging_2.11:3.4.0'
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
core/src/main/resources/logback.xml
Normal file
15
core/src/main/resources/logback.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<target>System.out</target>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="slick" level="INFO" />
|
||||||
|
|
||||||
|
<root level="DEBUG">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
</root>
|
||||||
|
</configuration>
|
|
@ -7,6 +7,7 @@ import com.nutomic.ensichat.core.header.ContentHeader
|
||||||
import com.nutomic.ensichat.core.interfaces._
|
import com.nutomic.ensichat.core.interfaces._
|
||||||
import com.nutomic.ensichat.core.internet.InternetInterface
|
import com.nutomic.ensichat.core.internet.InternetInterface
|
||||||
import com.nutomic.ensichat.core.util.{Database, FutureHelper}
|
import com.nutomic.ensichat.core.util.{Database, FutureHelper}
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ final class ConnectionHandler(settings: SettingsInterface, database: Database,
|
||||||
callbacks: CallbackInterface, crypto: Crypto,
|
callbacks: CallbackInterface, crypto: Crypto,
|
||||||
maxInternetConnections: Int) {
|
maxInternetConnections: Int) {
|
||||||
|
|
||||||
private val Tag = "ConnectionHandler"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
private var transmissionInterfaces = Set[TransmissionInterface]()
|
private var transmissionInterfaces = Set[TransmissionInterface]()
|
||||||
|
|
||||||
|
@ -45,8 +46,8 @@ final class ConnectionHandler(settings: SettingsInterface, database: Database,
|
||||||
additionalInterfaces.foreach(transmissionInterfaces += _)
|
additionalInterfaces.foreach(transmissionInterfaces += _)
|
||||||
FutureHelper {
|
FutureHelper {
|
||||||
crypto.generateLocalKeys()
|
crypto.generateLocalKeys()
|
||||||
Log.i(Tag, "Service started, address is " + crypto.localAddress)
|
logger.info("Service started, address is " + crypto.localAddress)
|
||||||
Log.i(Tag, "Local user is " + settings.get(SettingsInterface.KeyUserName, "none") +
|
logger.info("Local user is " + settings.get(SettingsInterface.KeyUserName, "none") +
|
||||||
" with status '" + settings.get(SettingsInterface.KeyUserStatus, "") + "'")
|
" with status '" + settings.get(SettingsInterface.KeyUserStatus, "") + "'")
|
||||||
transmissionInterfaces += new InternetInterface(this, crypto, settings, maxInternetConnections)
|
transmissionInterfaces += new InternetInterface(this, crypto, settings, maxInternetConnections)
|
||||||
transmissionInterfaces.foreach(_.create())
|
transmissionInterfaces.foreach(_.create())
|
||||||
|
@ -83,11 +84,11 @@ final class ConnectionHandler(settings: SettingsInterface, database: Database,
|
||||||
*/
|
*/
|
||||||
def onMessageReceived(msg: Message): Unit = {
|
def onMessageReceived(msg: Message): Unit = {
|
||||||
if (router.isMessageSeen(msg)) {
|
if (router.isMessageSeen(msg)) {
|
||||||
Log.v(Tag, "Ignoring message from " + msg.header.origin + " that we already received")
|
logger.trace("Ignoring message from " + msg.header.origin + " that we already received")
|
||||||
} else if (msg.header.target == crypto.localAddress) {
|
} else if (msg.header.target == crypto.localAddress) {
|
||||||
crypto.verifyAndDecrypt(msg) match {
|
crypto.verifyAndDecrypt(msg) match {
|
||||||
case Some(m) => onNewMessage(m)
|
case Some(m) => onNewMessage(m)
|
||||||
case None => Log.i(Tag, "Ignoring message with invalid signature from " + msg.header.origin)
|
case None => logger.info("Ignoring message with invalid signature from " + msg.header.origin)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
router.forwardMessage(msg)
|
router.forwardMessage(msg)
|
||||||
|
@ -127,34 +128,34 @@ final class ConnectionHandler(settings: SettingsInterface, database: Database,
|
||||||
val maxConnections = settings.get(SettingsInterface.KeyMaxConnections,
|
val maxConnections = settings.get(SettingsInterface.KeyMaxConnections,
|
||||||
SettingsInterface.DefaultMaxConnections.toString).toInt
|
SettingsInterface.DefaultMaxConnections.toString).toInt
|
||||||
if (connections().size == maxConnections) {
|
if (connections().size == maxConnections) {
|
||||||
Log.i(Tag, "Maximum number of connections reached")
|
logger.info("Maximum number of connections reached")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val info = msg.body.asInstanceOf[ConnectionInfo]
|
val info = msg.body.asInstanceOf[ConnectionInfo]
|
||||||
val sender = crypto.calculateAddress(info.key)
|
val sender = crypto.calculateAddress(info.key)
|
||||||
if (sender == Address.Broadcast || sender == Address.Null) {
|
if (sender == Address.Broadcast || sender == Address.Null) {
|
||||||
Log.i(Tag, "Ignoring ConnectionInfo message with invalid sender " + sender)
|
logger.info("Ignoring ConnectionInfo message with invalid sender " + sender)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crypto.havePublicKey(sender) && !crypto.verify(msg, Option(crypto.getPublicKey(sender)))) {
|
if (crypto.havePublicKey(sender) && !crypto.verify(msg, Option(crypto.getPublicKey(sender)))) {
|
||||||
Log.i(Tag, "Ignoring ConnectionInfo message with invalid signature")
|
logger.info("Ignoring ConnectionInfo message with invalid signature")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized {
|
synchronized {
|
||||||
if (!crypto.havePublicKey(sender)) {
|
if (!crypto.havePublicKey(sender)) {
|
||||||
crypto.addPublicKey(sender, info.key)
|
crypto.addPublicKey(sender, info.key)
|
||||||
Log.i(Tag, "Added public key for new device " + sender.toString)
|
logger.info("Added public key for new device " + sender.toString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log with username if we know it.
|
// Log with username if we know it.
|
||||||
if (allKnownUsers().map(_.address).contains(sender))
|
if (allKnownUsers().map(_.address).contains(sender))
|
||||||
Log.i(Tag, "Node " + getUser(sender).name + " (" + sender + ") connected")
|
logger.info("Node " + getUser(sender).name + " (" + sender + ") connected")
|
||||||
else
|
else
|
||||||
Log.i(Tag, "Node " + sender + " connected")
|
logger.info("Node " + sender + " connected")
|
||||||
|
|
||||||
sendTo(sender, new UserInfo(settings.get(SettingsInterface.KeyUserName, ""),
|
sendTo(sender, new UserInfo(settings.get(SettingsInterface.KeyUserName, ""),
|
||||||
settings.get(SettingsInterface.KeyUserStatus, "")))
|
settings.get(SettingsInterface.KeyUserStatus, "")))
|
||||||
|
|
|
@ -9,7 +9,8 @@ import javax.crypto.{Cipher, CipherOutputStream, KeyGenerator, SecretKey}
|
||||||
import com.nutomic.ensichat.core.Crypto._
|
import com.nutomic.ensichat.core.Crypto._
|
||||||
import com.nutomic.ensichat.core.body._
|
import com.nutomic.ensichat.core.body._
|
||||||
import com.nutomic.ensichat.core.header.ContentHeader
|
import com.nutomic.ensichat.core.header.ContentHeader
|
||||||
import com.nutomic.ensichat.core.interfaces.{Log, SettingsInterface}
|
import com.nutomic.ensichat.core.interfaces.SettingsInterface
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
object Crypto {
|
object Crypto {
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ object Crypto {
|
||||||
*/
|
*/
|
||||||
class Crypto(settings: SettingsInterface, keyFolder: File) {
|
class Crypto(settings: SettingsInterface, keyFolder: File) {
|
||||||
|
|
||||||
private val Tag = "Crypto"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new key pair using [[keyFolder]] with [[PublicKeySize]] bits and stores the
|
* Generates a new key pair using [[keyFolder]] with [[PublicKeySize]] bits and stores the
|
||||||
|
@ -104,7 +105,7 @@ class Crypto(settings: SettingsInterface, keyFolder: File) {
|
||||||
|
|
||||||
saveKey(PrivateKeyAlias, keyPair.getPrivate)
|
saveKey(PrivateKeyAlias, keyPair.getPrivate)
|
||||||
saveKey(PublicKeyAlias, keyPair.getPublic)
|
saveKey(PublicKeyAlias, keyPair.getPublic)
|
||||||
Log.i(Tag, "Generated cryptographic keys, address is " + address)
|
logger.info("Generated cryptographic keys, address is " + address)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,7 +185,7 @@ class Crypto(settings: SettingsInterface, keyFolder: File) {
|
||||||
fos = Option(new FileOutputStream(path))
|
fos = Option(new FileOutputStream(path))
|
||||||
fos.foreach(_.write(key.getEncoded))
|
fos.foreach(_.write(key.getEncoded))
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.w(Tag, "Failed to save key for alias " + alias, e)
|
case e: IOException => logger.warn("Failed to save key for alias " + alias, e)
|
||||||
} finally {
|
} finally {
|
||||||
fos.foreach(_.close())
|
fos.foreach(_.close())
|
||||||
}
|
}
|
||||||
|
@ -212,7 +213,7 @@ class Crypto(settings: SettingsInterface, keyFolder: File) {
|
||||||
data = new Array[Byte](path.length().asInstanceOf[Int])
|
data = new Array[Byte](path.length().asInstanceOf[Int])
|
||||||
fis.foreach(_.read(data))
|
fis.foreach(_.read(data))
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.e(Tag, "Failed to load key for alias " + alias, e)
|
case e: IOException => logger.error("Failed to load key for alias " + alias, e)
|
||||||
} finally {
|
} finally {
|
||||||
fis.foreach(_.close())
|
fis.foreach(_.close())
|
||||||
}
|
}
|
||||||
|
@ -240,7 +241,7 @@ class Crypto(settings: SettingsInterface, keyFolder: File) {
|
||||||
None
|
None
|
||||||
} catch {
|
} catch {
|
||||||
case e: InvalidKeyException =>
|
case e: InvalidKeyException =>
|
||||||
Log.w(Tag, "Failed to verify or decrypt message", e)
|
logger.warn("Failed to verify or decrypt message", e)
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package com.nutomic.ensichat.core.interfaces
|
|
||||||
|
|
||||||
object Log {
|
|
||||||
|
|
||||||
def setLogInstance(log: Log) = instance = Option(log)
|
|
||||||
|
|
||||||
private var instance: Option[Log] = None
|
|
||||||
|
|
||||||
def v(tag: String, message: String, tr: Throwable = null) = instance.foreach(_.v(tag, message, tr))
|
|
||||||
def d(tag: String, message: String, tr: Throwable = null) = instance.foreach(_.d(tag, message, tr))
|
|
||||||
def i(tag: String, message: String, tr: Throwable = null) = instance.foreach(_.i(tag, message, tr))
|
|
||||||
def w(tag: String, message: String, tr: Throwable = null) = instance.foreach(_.w(tag, message, tr))
|
|
||||||
def e(tag: String, message: String, tr: Throwable = null) = instance.foreach(_.e(tag, message, tr))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Log {
|
|
||||||
|
|
||||||
def v(tag: String, message: String, tr: Throwable = null)
|
|
||||||
def d(tag: String, message: String, tr: Throwable = null)
|
|
||||||
def i(tag: String, message: String, tr: Throwable = null)
|
|
||||||
def w(tag: String, message: String, tr: Throwable = null)
|
|
||||||
def e(tag: String, message: String, tr: Throwable = null)
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,8 +6,8 @@ import java.net.{InetAddress, Socket}
|
||||||
import com.nutomic.ensichat.core.Message.ReadMessageException
|
import com.nutomic.ensichat.core.Message.ReadMessageException
|
||||||
import com.nutomic.ensichat.core.body.ConnectionInfo
|
import com.nutomic.ensichat.core.body.ConnectionInfo
|
||||||
import com.nutomic.ensichat.core.header.MessageHeader
|
import com.nutomic.ensichat.core.header.MessageHeader
|
||||||
import com.nutomic.ensichat.core.interfaces.Log
|
|
||||||
import com.nutomic.ensichat.core.{Address, Crypto, Message}
|
import com.nutomic.ensichat.core.{Address, Crypto, Message}
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates an active connection to another node.
|
* Encapsulates an active connection to another node.
|
||||||
|
@ -15,14 +15,14 @@ import com.nutomic.ensichat.core.{Address, Crypto, Message}
|
||||||
class InternetConnectionThread(socket: Socket, crypto: Crypto, onDisconnected: (InternetConnectionThread) => Unit,
|
class InternetConnectionThread(socket: Socket, crypto: Crypto, onDisconnected: (InternetConnectionThread) => Unit,
|
||||||
onReceive: (Message, InternetConnectionThread) => Unit) extends Thread {
|
onReceive: (Message, InternetConnectionThread) => Unit) extends Thread {
|
||||||
|
|
||||||
private val Tag = "InternetConnectionThread"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
private val inStream: InputStream =
|
private val inStream: InputStream =
|
||||||
try {
|
try {
|
||||||
socket.getInputStream
|
socket.getInputStream
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException =>
|
case e: IOException =>
|
||||||
Log.e(Tag, "Failed to open stream", e)
|
logger.error("Failed to open stream", e)
|
||||||
close()
|
close()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ class InternetConnectionThread(socket: Socket, crypto: Crypto, onDisconnected: (
|
||||||
socket.getOutputStream
|
socket.getOutputStream
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException =>
|
case e: IOException =>
|
||||||
Log.e(Tag, "Failed to open stream", e)
|
logger.error("Failed to open stream", e)
|
||||||
close()
|
close()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ class InternetConnectionThread(socket: Socket, crypto: Crypto, onDisconnected: (
|
||||||
}
|
}
|
||||||
|
|
||||||
override def run(): Unit = {
|
override def run(): Unit = {
|
||||||
Log.i(Tag, "Connection opened to " + socket.getInetAddress)
|
logger.info("Connection opened to " + socket.getInetAddress)
|
||||||
|
|
||||||
send(crypto.sign(new Message(new MessageHeader(ConnectionInfo.Type,
|
send(crypto.sign(new Message(new MessageHeader(ConnectionInfo.Type,
|
||||||
Address.Null, Address.Null, 0), new ConnectionInfo(crypto.getLocalPublicKey))))
|
Address.Null, Address.Null, 0), new ConnectionInfo(crypto.getLocalPublicKey))))
|
||||||
|
@ -51,13 +51,13 @@ class InternetConnectionThread(socket: Socket, crypto: Crypto, onDisconnected: (
|
||||||
socket.setKeepAlive(true)
|
socket.setKeepAlive(true)
|
||||||
while (socket.isConnected) {
|
while (socket.isConnected) {
|
||||||
val msg = Message.read(inStream)
|
val msg = Message.read(inStream)
|
||||||
Log.v(Tag, "Received " + msg)
|
logger.trace("Received " + msg)
|
||||||
|
|
||||||
onReceive(msg, this)
|
onReceive(msg, this)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e @ (_: ReadMessageException | _: IOException) =>
|
case e @ (_: ReadMessageException | _: IOException) =>
|
||||||
Log.w(Tag, "Failed to read incoming message", e)
|
logger.warn("Failed to read incoming message", e)
|
||||||
close()
|
close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class InternetConnectionThread(socket: Socket, crypto: Crypto, onDisconnected: (
|
||||||
try {
|
try {
|
||||||
outStream.write(msg.write)
|
outStream.write(msg.write)
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.e(Tag, "Failed to write message", e)
|
case e: IOException => logger.error("Failed to write message", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,9 +76,9 @@ class InternetConnectionThread(socket: Socket, crypto: Crypto, onDisconnected: (
|
||||||
try {
|
try {
|
||||||
socket.close()
|
socket.close()
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.w(Tag, "Failed to close socket", e)
|
case e: IOException => logger.warn("Failed to close socket", e)
|
||||||
}
|
}
|
||||||
Log.d(Tag, "Connection to " + socket.getInetAddress + " closed")
|
logger.debug("Connection to " + socket.getInetAddress + " closed")
|
||||||
onDisconnected(this)
|
onDisconnected(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package com.nutomic.ensichat.core.internet
|
package com.nutomic.ensichat.core.internet
|
||||||
|
|
||||||
import java.io.IOException
|
|
||||||
import java.net.{InetAddress, Socket}
|
import java.net.{InetAddress, Socket}
|
||||||
|
|
||||||
import com.nutomic.ensichat.core.body.ConnectionInfo
|
import com.nutomic.ensichat.core.body.ConnectionInfo
|
||||||
import com.nutomic.ensichat.core.interfaces.{Log, SettingsInterface, TransmissionInterface}
|
import com.nutomic.ensichat.core.interfaces.{SettingsInterface, TransmissionInterface}
|
||||||
import com.nutomic.ensichat.core.util.FutureHelper
|
import com.nutomic.ensichat.core.util.FutureHelper
|
||||||
import com.nutomic.ensichat.core.{Address, ConnectionHandler, Crypto, Message}
|
import com.nutomic.ensichat.core.{Address, ConnectionHandler, Crypto, Message}
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.concurrent.Future
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
object InternetInterface {
|
object InternetInterface {
|
||||||
|
@ -26,7 +27,7 @@ class InternetInterface(connectionHandler: ConnectionHandler, crypto: Crypto,
|
||||||
settings: SettingsInterface, maxConnections: Int)
|
settings: SettingsInterface, maxConnections: Int)
|
||||||
extends TransmissionInterface {
|
extends TransmissionInterface {
|
||||||
|
|
||||||
private val Tag = "InternetInterface"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
private lazy val serverThread =
|
private lazy val serverThread =
|
||||||
new InternetServerThread(crypto, onConnected, onDisconnected, onReceiveMessage)
|
new InternetServerThread(crypto, onConnected, onDisconnected, onReceiveMessage)
|
||||||
|
@ -83,15 +84,14 @@ class InternetInterface(connectionHandler: ConnectionHandler, crypto: Crypto,
|
||||||
* Opens connection to the specified IP address in client mode.
|
* Opens connection to the specified IP address in client mode.
|
||||||
*/
|
*/
|
||||||
private def openConnection(address: String, port: Int): Unit = {
|
private def openConnection(address: String, port: Int): Unit = {
|
||||||
Log.i(Tag, s"Attempting connection to $address:$port")
|
logger.info(s"Attempting connection to $address:$port")
|
||||||
try {
|
Future {
|
||||||
val socket = new Socket(InetAddress.getByName(address), port)
|
val socket = new Socket(InetAddress.getByName(address), port)
|
||||||
val ct = new InternetConnectionThread(socket, crypto, onDisconnected, onReceiveMessage)
|
val ct = new InternetConnectionThread(socket, crypto, onDisconnected, onReceiveMessage)
|
||||||
connections += ct
|
connections += ct
|
||||||
ct.start()
|
ct.start()
|
||||||
} catch {
|
}.onFailure { case e =>
|
||||||
case e: IOException =>
|
logger.warn("Failed to open connection to " + address + ":" + port, e)
|
||||||
Log.w(Tag, "Failed to open connection to " + address + ":" + port, e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class InternetInterface(connectionHandler: ConnectionHandler, crypto: Crypto,
|
||||||
|
|
||||||
private def onDisconnected(connectionThread: InternetConnectionThread): Unit = {
|
private def onDisconnected(connectionThread: InternetConnectionThread): Unit = {
|
||||||
addressDeviceMap.find(_._2 == connectionThread).foreach { ad =>
|
addressDeviceMap.find(_._2 == connectionThread).foreach { ad =>
|
||||||
Log.d(Tag, "Connection closed to " + ad._1)
|
logger.trace("Connection closed to " + ad._1)
|
||||||
connections -= connectionThread
|
connections -= connectionThread
|
||||||
addressDeviceMap -= ad._1
|
addressDeviceMap -= ad._1
|
||||||
connectionHandler.onConnectionClosed()
|
connectionHandler.onConnectionClosed()
|
||||||
|
@ -112,7 +112,7 @@ class InternetInterface(connectionHandler: ConnectionHandler, crypto: Crypto,
|
||||||
case info: ConnectionInfo =>
|
case info: ConnectionInfo =>
|
||||||
val address = crypto.calculateAddress(info.key)
|
val address = crypto.calculateAddress(info.key)
|
||||||
if (address == crypto.localAddress) {
|
if (address == crypto.localAddress) {
|
||||||
Log.i(Tag, "Address " + address + " is me, not connecting to myself")
|
logger.info("Address " + address + " is me, not connecting to myself")
|
||||||
thread.close()
|
thread.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ class InternetInterface(connectionHandler: ConnectionHandler, crypto: Crypto,
|
||||||
|
|
||||||
def connectionChanged(): Unit = {
|
def connectionChanged(): Unit = {
|
||||||
FutureHelper {
|
FutureHelper {
|
||||||
Log.i(Tag, "Network has changed. Closing all connections and connecting to bootstrap nodes again")
|
logger.info("Network has changed. Closing all connections and connecting to bootstrap nodes again")
|
||||||
connections.foreach(_.close())
|
connections.foreach(_.close())
|
||||||
openAllConnections(maxConnections)
|
openAllConnections(maxConnections)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,19 @@ package com.nutomic.ensichat.core.internet
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
|
|
||||||
import com.nutomic.ensichat.core.interfaces.Log
|
|
||||||
import com.nutomic.ensichat.core.{Crypto, Message}
|
import com.nutomic.ensichat.core.{Crypto, Message}
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
class InternetServerThread(crypto: Crypto, onConnected: (InternetConnectionThread) => Unit,
|
class InternetServerThread(crypto: Crypto, onConnected: (InternetConnectionThread) => Unit,
|
||||||
onDisconnected: (InternetConnectionThread) => Unit, onReceive: (Message, InternetConnectionThread) => Unit) extends Thread {
|
onDisconnected: (InternetConnectionThread) => Unit, onReceive: (Message, InternetConnectionThread) => Unit) extends Thread {
|
||||||
|
|
||||||
private val Tag = "InternetServerThread"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
private lazy val socket: Option[ServerSocket] = try {
|
private lazy val socket: Option[ServerSocket] = try {
|
||||||
Option(new ServerSocket(InternetInterface.ServerPort))
|
Option(new ServerSocket(InternetInterface.ServerPort))
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException =>
|
case e: IOException =>
|
||||||
Log.w(Tag, "Failed to create server socket", e)
|
logger.warn("Failed to create server socket", e)
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class InternetServerThread(crypto: Crypto, onConnected: (InternetConnectionThrea
|
||||||
connection.start()
|
connection.start()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.w(Tag, "Failed to accept connection", e)
|
case e: IOException => logger.warn("Failed to accept connection", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class InternetServerThread(crypto: Crypto, onConnected: (InternetConnectionThrea
|
||||||
try {
|
try {
|
||||||
socket.get.close()
|
socket.get.close()
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.w(Tag, "Failed to close socket", e)
|
case e: IOException => logger.warn("Failed to close socket", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@ import java.util.Date
|
||||||
|
|
||||||
import com.nutomic.ensichat.core.body.Text
|
import com.nutomic.ensichat.core.body.Text
|
||||||
import com.nutomic.ensichat.core.header.ContentHeader
|
import com.nutomic.ensichat.core.header.ContentHeader
|
||||||
import com.nutomic.ensichat.core.interfaces.{Log, CallbackInterface}
|
import com.nutomic.ensichat.core.interfaces.CallbackInterface
|
||||||
import com.nutomic.ensichat.core.{Address, Message, User}
|
import com.nutomic.ensichat.core.{Address, Message, User}
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
import slick.driver.H2Driver.api._
|
import slick.driver.H2Driver.api._
|
||||||
import slick.jdbc.meta.MTable
|
|
||||||
|
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
@ -21,7 +21,7 @@ import scala.concurrent.duration.Duration
|
||||||
*/
|
*/
|
||||||
class Database(path: File, callbackInterface: CallbackInterface) {
|
class Database(path: File, callbackInterface: CallbackInterface) {
|
||||||
|
|
||||||
private val Tag = "Database"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
private class Messages(tag: Tag) extends Table[Message](tag, "MESSAGES") {
|
private class Messages(tag: Tag) extends Table[Message](tag, "MESSAGES") {
|
||||||
def id = primaryKey("id", (origin, messageId))
|
def id = primaryKey("id", (origin, messageId))
|
||||||
|
@ -63,7 +63,7 @@ class Database(path: File, callbackInterface: CallbackInterface) {
|
||||||
// H2 appends a .mv.db suffix to the path which we can't change, so we have to check that file.
|
// H2 appends a .mv.db suffix to the path which we can't change, so we have to check that file.
|
||||||
val dbFile = new File(path.getAbsolutePath + ".mv.db")
|
val dbFile = new File(path.getAbsolutePath + ".mv.db")
|
||||||
if (!dbFile.exists()) {
|
if (!dbFile.exists()) {
|
||||||
Log.i(Tag, "Database does not exist, creating tables")
|
logger.info("Database does not exist, creating tables")
|
||||||
Await.result(db.run((messages.schema ++ contacts.schema).create), Duration.Inf)
|
Await.result(db.run((messages.schema ++ contacts.schema).create), Duration.Inf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.nutomic.ensichat.core.util
|
package com.nutomic.ensichat.core.util
|
||||||
|
|
||||||
import com.nutomic.ensichat.core.interfaces.Log
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||||
*/
|
*/
|
||||||
object FutureHelper {
|
object FutureHelper {
|
||||||
|
|
||||||
private val Tag = "FutureHelper"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
def apply[A](action: => A)(implicit executor: ExecutionContext): Future[A] = {
|
def apply[A](action: => A)(implicit executor: ExecutionContext): Future[A] = {
|
||||||
val f = Future(action)
|
val f = Future(action)
|
||||||
|
@ -20,7 +20,7 @@ object FutureHelper {
|
||||||
// HACK: Android does not close app when crash occurs in background thread, and there's no
|
// HACK: Android does not close app when crash occurs in background thread, and there's no
|
||||||
// cross-platform way to execute on the foreground thread.
|
// cross-platform way to execute on the foreground thread.
|
||||||
// We use this to make sure exceptions are not hidden in the logs.
|
// We use this to make sure exceptions are not hidden in the logs.
|
||||||
Log.e(Tag, "Exception in Future", e)
|
logger.error("Exception in Future", e)
|
||||||
//System.exit(-1)
|
//System.exit(-1)
|
||||||
}
|
}
|
||||||
f
|
f
|
||||||
|
|
|
@ -5,6 +5,7 @@ dependencies {
|
||||||
compile 'org.scala-lang:scala-library:2.11.7'
|
compile 'org.scala-lang:scala-library:2.11.7'
|
||||||
compile project(path: ':core')
|
compile project(path: ':core')
|
||||||
compile 'com.github.scopt:scopt_2.10:3.3.0'
|
compile 'com.github.scopt:scopt_2.10:3.3.0'
|
||||||
|
compile 'ch.qos.logback:logback-classic:1.1.7'
|
||||||
}
|
}
|
||||||
|
|
||||||
mainClassName = 'com.nutomic.ensichat.server.Main'
|
mainClassName = 'com.nutomic.ensichat.server.Main'
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
package com.nutomic.ensichat.server
|
|
||||||
|
|
||||||
import java.io.{PrintWriter, StringWriter}
|
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.{Date, Locale}
|
|
||||||
|
|
||||||
import com.nutomic.ensichat.core.interfaces.Log
|
|
||||||
|
|
||||||
import scala.collection.mutable
|
|
||||||
|
|
||||||
class Logging extends Log {
|
|
||||||
|
|
||||||
private val logs = new mutable.Queue[String]()
|
|
||||||
|
|
||||||
def dequeue(): Seq[String] = logs.dequeueAll((String) => true)
|
|
||||||
|
|
||||||
private def enqueue(tag: String, message: String, tr: Option[Throwable]): Unit = {
|
|
||||||
val df = DateFormat.getTimeInstance(DateFormat.DEFAULT, Locale.UK)
|
|
||||||
val throwableString = tr.map { tr =>
|
|
||||||
val sw = new StringWriter()
|
|
||||||
tr.printStackTrace(new PrintWriter(sw))
|
|
||||||
"\n" + sw.toString
|
|
||||||
}
|
|
||||||
logs.enqueue(df.format(new Date()) + " " + tag + ": " + message + throwableString.getOrElse(""))
|
|
||||||
}
|
|
||||||
|
|
||||||
def v(tag: String, message: String, tr: Throwable = null): Unit =
|
|
||||||
enqueue("V/" + tag, message, Option(tr))
|
|
||||||
|
|
||||||
def d(tag: String, message: String, tr: Throwable = null): Unit =
|
|
||||||
enqueue("D/" + tag, message, Option(tr))
|
|
||||||
|
|
||||||
def i(tag: String, message: String, tr: Throwable = null): Unit =
|
|
||||||
enqueue("I/" + tag, message, Option(tr))
|
|
||||||
|
|
||||||
def w(tag: String, message: String, tr: Throwable = null): Unit =
|
|
||||||
enqueue("W/" + tag, message, Option(tr))
|
|
||||||
|
|
||||||
def e(tag: String, message: String, tr: Throwable = null): Unit =
|
|
||||||
enqueue("E/" + tag, message, Option(tr))
|
|
||||||
|
|
||||||
}
|
|
|
@ -5,24 +5,21 @@ import java.nio.file.Paths
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
import com.nutomic.ensichat.core.body.Text
|
import com.nutomic.ensichat.core.body.Text
|
||||||
import com.nutomic.ensichat.core.interfaces.SettingsInterface._
|
import com.nutomic.ensichat.core.interfaces.{CallbackInterface, SettingsInterface}
|
||||||
import com.nutomic.ensichat.core.interfaces.{CallbackInterface, Log, SettingsInterface}
|
|
||||||
import com.nutomic.ensichat.core.util.Database
|
import com.nutomic.ensichat.core.util.Database
|
||||||
import com.nutomic.ensichat.core.{ConnectionHandler, Crypto, Message}
|
import com.nutomic.ensichat.core.{ConnectionHandler, Crypto, Message}
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
import scopt.OptionParser
|
import scopt.OptionParser
|
||||||
|
|
||||||
object Main extends App with CallbackInterface {
|
object Main extends App with CallbackInterface {
|
||||||
|
|
||||||
private val Tag = "Main"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
private val ConfigFolder = Paths.get("").toFile.getAbsoluteFile
|
private val ConfigFolder = Paths.get("").toFile.getAbsoluteFile
|
||||||
private val ConfigFile = new File(ConfigFolder, "config.properties")
|
private val ConfigFile = new File(ConfigFolder, "config.properties")
|
||||||
private val DatabaseFile = new File(ConfigFolder, "database")
|
private val DatabaseFile = new File(ConfigFolder, "database")
|
||||||
private val KeyFolder = new File(ConfigFolder, "keys")
|
private val KeyFolder = new File(ConfigFolder, "keys")
|
||||||
|
|
||||||
private val LogInterval = TimeUnit.SECONDS.toMillis(1)
|
|
||||||
|
|
||||||
private lazy val logInstance = new Logging()
|
|
||||||
private lazy val settings = new Settings(ConfigFile)
|
private lazy val settings = new Settings(ConfigFile)
|
||||||
private lazy val crypto = new Crypto(settings, KeyFolder)
|
private lazy val crypto = new Crypto(settings, KeyFolder)
|
||||||
private lazy val database = new Database(DatabaseFile, this)
|
private lazy val database = new Database(DatabaseFile, this)
|
||||||
|
@ -38,7 +35,6 @@ object Main extends App with CallbackInterface {
|
||||||
private def init(): Unit = {
|
private def init(): Unit = {
|
||||||
ConfigFolder.mkdirs()
|
ConfigFolder.mkdirs()
|
||||||
KeyFolder.mkdirs()
|
KeyFolder.mkdirs()
|
||||||
Log.setLogInstance(logInstance)
|
|
||||||
sys.addShutdownHook(connectionHandler.stop())
|
sys.addShutdownHook(connectionHandler.stop())
|
||||||
|
|
||||||
val parser = new OptionParser[Config]("ensichat") {
|
val parser = new OptionParser[Config]("ensichat") {
|
||||||
|
@ -64,8 +60,7 @@ object Main extends App with CallbackInterface {
|
||||||
|
|
||||||
// Keep alive and print logs
|
// Keep alive and print logs
|
||||||
while (true) {
|
while (true) {
|
||||||
Thread.sleep(LogInterval)
|
Thread.sleep(TimeUnit.SECONDS.toMillis(1))
|
||||||
logInstance.dequeue().foreach(System.out.println)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +73,9 @@ object Main extends App with CallbackInterface {
|
||||||
val address = msg.header.origin
|
val address = msg.header.origin
|
||||||
val name = connectionHandler.getUser(address).name
|
val name = connectionHandler.getUser(address).name
|
||||||
connectionHandler.sendTo(address, new Text("Hello " + name))
|
connectionHandler.sendTo(address, new Text("Hello " + name))
|
||||||
Log.i(Tag, "Received text: " + text.text)
|
logger.info("Received text: " + text.text)
|
||||||
case _ =>
|
case _ =>
|
||||||
Log.i(Tag, "Received msg: " + msg.body)
|
logger.info("Received msg: " + msg.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,14 @@ package com.nutomic.ensichat.server
|
||||||
import java.io._
|
import java.io._
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
import com.nutomic.ensichat.core.interfaces.{Log, SettingsInterface}
|
import com.nutomic.ensichat.core.interfaces.SettingsInterface
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
|
||||||
class Settings(file: File) extends SettingsInterface {
|
class Settings(file: File) extends SettingsInterface {
|
||||||
|
|
||||||
private val Tag = "Settings"
|
private val logger = Logger(this.getClass)
|
||||||
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
file.createNewFile()
|
file.createNewFile()
|
||||||
|
@ -23,7 +24,7 @@ class Settings(file: File) extends SettingsInterface {
|
||||||
p.load(fis)
|
p.load(fis)
|
||||||
fis.close()
|
fis.close()
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.w(Tag, "Failed to load settings from " + file, e)
|
case e: IOException => logger.warn("Failed to load settings from " + file, e)
|
||||||
}
|
}
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
|
@ -35,7 +36,7 @@ class Settings(file: File) extends SettingsInterface {
|
||||||
props.store(fos, "")
|
props.store(fos, "")
|
||||||
fos.close()
|
fos.close()
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => Log.w(Tag, "Failed to write preference for key " + key, e)
|
case e: IOException => logger.warn("Failed to write preference for key " + key, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue