2019-06-02 20:59:05 +02:00
package org.hso.texturesyncclient.controller.net
import com.google.gson.*
2019-06-03 15:50:09 +02:00
import javafx.scene.image.Image
import org.hso.texturesyncclient.model.Sha256
2019-06-02 22:32:19 +02:00
import org.hso.texturesyncclient.model.Texture
2019-06-02 20:59:05 +02:00
import java.io.*
2019-06-03 18:40:37 +02:00
import java.net.InetAddress
2019-06-04 16:10:11 +02:00
import java.net.InetSocketAddress
2019-06-03 18:40:37 +02:00
import java.net.Socket
2019-06-03 15:18:42 +02:00
import java.util.*
2019-06-02 20:59:05 +02:00
@Suppress ( " MemberVisibilityCanBePrivate " )
2019-06-03 15:03:25 +02:00
class Connection ( val address : InetAddress , val port : Int = 10796 ) : Closeable {
2019-06-02 20:59:05 +02:00
var socket : Socket ? = null
var output : DataOutputStream ? = null
var input : DataInputStream ? = null
@Throws ( IOException :: class )
2019-06-03 15:03:25 +02:00
@Synchronized
2019-06-08 21:25:56 +02:00
private fun < R > withStreams ( retry : Boolean = true , inner : ( DataInputStream , DataOutputStream ) -> R ) : R {
2019-06-02 20:59:05 +02:00
val i : DataInputStream
val o : DataOutputStream
if ( socket == null || ! socket !! . isConnected ) {
2019-06-04 16:10:11 +02:00
val sock = Socket ( )
sock . soTimeout = 10 _000 /*ms*/
sock . keepAlive = true
sock . connect ( InetSocketAddress ( address , port ) , 1 _000 /*ms*/ )
2019-06-02 20:59:05 +02:00
i = DataInputStream ( BufferedInputStream ( sock . getInputStream ( ) ) )
o = DataOutputStream ( BufferedOutputStream ( sock . getOutputStream ( ) ) )
input = i
output = o
socket = sock
} else {
i = input !!
o = output !!
}
2019-06-08 21:25:56 +02:00
return try {
inner ( i , o )
} catch ( e : IOException ) {
if ( retry ) {
println ( " Got IOException; Retry ${e.message} " )
close ( )
withStreams ( false , inner )
} else {
throw e
}
}
2019-06-02 20:59:05 +02:00
}
2019-06-03 15:03:25 +02:00
@Throws ( IOException :: class , ConnectionException :: class )
2019-06-02 20:59:05 +02:00
fun ping ( ) {
2019-06-08 21:25:56 +02:00
return withStreams { i , o ->
2019-06-02 20:59:05 +02:00
2019-06-08 21:25:56 +02:00
val obj = JsonObject ( )
obj . add ( " ping " , JsonObject ( ) )
2019-06-02 20:59:05 +02:00
2019-06-08 21:25:56 +02:00
JsonPackage ( obj ) . write ( o )
2019-06-02 20:59:05 +02:00
2019-06-08 21:25:56 +02:00
when ( val pkg = Package . read ( i ) ) {
is JsonPackage -> return @withStreams
is BinaryPackage -> throw ConnectionUnexpectedPacketException ( )
is ErrorPackage -> throw ConnectionErrorException ( pkg )
else -> throw RuntimeException ( " Unreachable " )
}
2019-06-17 22:40:52 +02:00
}
2019-06-02 22:04:01 +02:00
}
2019-06-03 15:03:25 +02:00
@Throws ( IOException :: class , ConnectionException :: class )
2019-06-03 16:33:19 +02:00
fun query ( query : Array < String > ) : Array < Texture > {
2019-06-08 21:25:56 +02:00
return withStreams { i , o ->
val obj = JsonObject ( )
obj . add ( " query " , {
val inner = JsonObject ( )
inner . add ( " query " , {
val array = JsonArray ( )
for ( queryString in query ) {
array . add ( queryString )
}
array
} ( ) )
inner
2019-06-02 22:04:01 +02:00
} ( ) )
2019-06-08 21:25:56 +02:00
JsonPackage ( obj ) . write ( o )
when ( val pkg = Package . read ( i ) ) {
is JsonPackage -> {
try {
return @withStreams Gson ( ) . fromJson < Array < InternalTexture > > (
pkg . content ,
Array < InternalTexture > :: class . java
)
. map { tex ->
tex . toTexture ( )
} . toTypedArray ( )
} catch ( e : JsonSyntaxException ) {
throw ConnectionInvalidJsonException ( )
}
2019-06-02 22:04:01 +02:00
}
2019-06-08 21:25:56 +02:00
is BinaryPackage -> throw ConnectionUnexpectedPacketException ( )
is ErrorPackage -> throw ConnectionErrorException ( pkg )
else -> throw RuntimeException ( " Unreachable " )
2019-06-02 22:04:01 +02:00
}
2019-06-02 20:59:05 +02:00
}
}
2019-06-03 15:18:42 +02:00
@Throws ( IOException :: class , ConnectionException :: class )
2019-06-03 18:40:37 +02:00
fun getTextureById ( id : UUID ) : Texture ? {
2019-06-08 21:25:56 +02:00
return withStreams { i , o ->
2019-06-03 15:18:42 +02:00
2019-06-08 21:25:56 +02:00
val obj = JsonObject ( )
obj . add ( " get_texture " , {
val inner = JsonObject ( )
inner . addProperty ( " id " , id . toString ( ) )
inner
} ( ) )
2019-06-03 15:18:42 +02:00
2019-06-08 21:25:56 +02:00
JsonPackage ( obj ) . write ( o )
when ( val pkg = Package . read ( i ) ) {
is JsonPackage -> {
return @withStreams if ( pkg . content . isJsonNull ) {
null
} else {
try {
Gson ( )
. fromJson < InternalTexture > ( pkg . content , InternalTexture :: class . java )
. toTexture ( )
} catch ( e : JsonSyntaxException ) {
throw ConnectionInvalidJsonException ( )
}
2019-06-03 15:18:42 +02:00
}
}
2019-06-08 21:25:56 +02:00
is BinaryPackage -> throw ConnectionUnexpectedPacketException ( )
is ErrorPackage -> throw ConnectionErrorException ( pkg )
else -> throw RuntimeException ( " Unreachable " )
2019-06-03 15:18:42 +02:00
}
2019-06-08 21:25:56 +02:00
}
2019-06-03 15:18:42 +02:00
}
@Throws ( IOException :: class , ConnectionException :: class )
2019-06-03 18:40:37 +02:00
fun getTextureByName ( name : String ) : Texture ? {
2019-06-08 21:25:56 +02:00
return withStreams { i , o ->
2019-06-03 15:18:42 +02:00
2019-06-08 21:25:56 +02:00
val obj = JsonObject ( )
obj . add ( " get_texture " , {
val inner = JsonObject ( )
inner . addProperty ( " name " , name )
inner
} ( ) )
2019-06-03 15:18:42 +02:00
2019-06-08 21:25:56 +02:00
JsonPackage ( obj ) . write ( o )
when ( val pkg = Package . read ( i ) ) {
is JsonPackage -> {
return @withStreams if ( pkg . content . isJsonNull ) {
null
} else {
try {
Gson ( )
. fromJson < InternalTexture > ( pkg . content , InternalTexture :: class . java )
. toTexture ( )
} catch ( e : JsonSyntaxException ) {
throw ConnectionInvalidJsonException ( )
}
2019-06-03 15:18:42 +02:00
}
}
2019-06-08 21:25:56 +02:00
is BinaryPackage -> throw ConnectionUnexpectedPacketException ( )
is ErrorPackage -> throw ConnectionErrorException ( pkg )
else -> throw RuntimeException ( " Unreachable " )
2019-06-03 15:18:42 +02:00
}
}
}
2019-06-03 15:50:09 +02:00
@Throws ( IOException :: class , ConnectionException :: class )
2019-06-03 16:33:19 +02:00
fun getTextureFile ( hash : Sha256 ) : ByteArray {
2019-06-08 21:25:56 +02:00
return withStreams { i , o ->
val obj = JsonObject ( )
obj . add ( " get_texture_file " , {
val inner = JsonObject ( )
inner . addProperty ( " texture_hash " , hash . toString ( ) )
inner
} ( ) )
2019-06-03 15:50:09 +02:00
2019-06-08 21:25:56 +02:00
JsonPackage ( obj ) . write ( o )
when ( val pkg = Package . read ( i ) ) {
is JsonPackage -> throw ConnectionUnexpectedPacketException ( )
is BinaryPackage -> return @withStreams pkg . content
is ErrorPackage -> throw ConnectionErrorException ( pkg )
else -> throw RuntimeException ( " Unreachable " )
}
}
2019-06-03 15:50:09 +02:00
}
@Throws ( IOException :: class , ConnectionException :: class )
2019-06-03 16:33:19 +02:00
fun getTexturePreview ( hash : Sha256 ) : Image {
2019-06-08 21:25:56 +02:00
return withStreams { i , o ->
val obj = JsonObject ( )
obj . add ( " get_texture_preview " , {
val inner = JsonObject ( )
inner . addProperty ( " texture_hash " , hash . toString ( ) )
inner . addProperty ( " desired_format " , " jpeg " )
inner
} ( ) )
JsonPackage ( obj ) . write ( o )
when ( val pkg = Package . read ( i ) ) {
is JsonPackage -> throw ConnectionUnexpectedPacketException ( )
is BinaryPackage -> {
return @withStreams Image ( ByteArrayInputStream ( pkg . content ) )
}
is ErrorPackage -> throw ConnectionErrorException ( pkg )
else -> throw RuntimeException ( " Unreachable " )
2019-06-03 15:50:09 +02:00
}
}
}
2019-06-03 16:31:04 +02:00
@Throws ( IOException :: class , ConnectionException :: class , IllegalArgumentException :: class )
2019-06-03 16:33:19 +02:00
private fun replaceTexture ( old : Texture ? , new : Texture ? , image : ByteArray ? ) {
2019-06-08 21:25:56 +02:00
return withStreams { i , o ->
2019-06-03 16:31:04 +02:00
2019-06-08 21:25:56 +02:00
val obj = JsonObject ( )
obj . add ( " replace_texture " , {
val inner = JsonObject ( )
if ( old != null ) {
inner . add ( " old " , Gson ( ) . toJsonTree ( InternalTexture ( old ) , InternalTexture :: class . java ) )
} else {
inner . add ( " old " , null )
}
if ( new != null ) {
inner . add ( " new " , Gson ( ) . toJsonTree ( InternalTexture ( new ) , InternalTexture :: class . java ) )
} else {
inner . add ( " new " , null )
}
inner
} ( ) )
JsonPackage ( obj ) . write ( o )
when ( val pkg = Package . read ( i ) ) {
is JsonPackage -> {
when {
pkg . content == JsonPrimitive ( true ) -> // everthing is fine!
return @withStreams
image != null -> {
// should be { "get_texture_file": { texture_hash : <Hash> }}
// we don't check, since there is no good way to handle it.
BinaryPackage ( image ) . write ( o )
when ( val ipkg = Package . read ( i ) ) {
is JsonPackage -> {
if ( ipkg . content != JsonPrimitive ( true ) ) {
// Protokoll Assertion failed
throw ConnectionUnexpectedPacketException ( )
}
2019-06-07 14:01:16 +02:00
}
2019-06-08 21:25:56 +02:00
is BinaryPackage -> throw ConnectionUnexpectedPacketException ( )
is ErrorPackage -> throw ConnectionErrorException ( ipkg )
else -> throw RuntimeException ( " Unreachable " )
2019-06-03 16:31:04 +02:00
}
}
2019-06-08 21:25:56 +02:00
else -> {
ErrorPackage ( 404 , " Texture not found! " ) . write ( o )
close ( ) // gets re-opened on next request.
throw IllegalArgumentException ( " Image Argument was needed. " )
}
2019-06-07 14:01:16 +02:00
}
2019-06-03 16:31:04 +02:00
}
2019-06-08 21:25:56 +02:00
is BinaryPackage -> throw ConnectionUnexpectedPacketException ( )
is ErrorPackage -> throw ConnectionErrorException ( pkg )
else -> throw RuntimeException ( " Unreachable " )
2019-06-03 16:31:04 +02:00
}
}
}
@Throws ( IOException :: class , ConnectionException :: class , IllegalArgumentException :: class )
2019-06-03 16:33:19 +02:00
fun uploadTexture ( texture : Texture , image : ByteArray ) {
if ( texture . textureHash != Sha256 ( image ) ) {
2019-06-03 16:31:04 +02:00
throw IllegalArgumentException ( " Sha256 of Image does not Match with Texture. " )
}
replaceTexture ( null , texture , image )
}
@Throws ( IOException :: class , ConnectionException :: class , IllegalArgumentException :: class )
2019-06-03 16:33:19 +02:00
fun updateTexture ( old : Texture , new : Texture , image : ByteArray ) {
if ( new . textureHash != Sha256 ( image ) ) {
2019-06-03 16:31:04 +02:00
throw IllegalArgumentException ( " Sha256 of Image does not Match with Texture. " )
}
replaceTexture ( old , new , image )
}
@Throws ( IOException :: class , ConnectionException :: class , IllegalArgumentException :: class )
2019-06-03 16:33:19 +02:00
fun deleteTexture ( texture : Texture ) {
replaceTexture ( texture , null , null )
2019-06-03 16:31:04 +02:00
}
2019-06-02 20:59:05 +02:00
@Throws ( IOException :: class )
override fun close ( ) {
2019-06-08 21:25:56 +02:00
try {
if ( output != null ) {
output !! . close ( )
output = null
}
if ( input != null ) {
input !! . close ( )
input = null
}
if ( socket != null ) {
socket !! . close ( )
socket = null
}
} catch ( e : IOException ) {
// Ignore
2019-06-02 20:59:05 +02:00
}
}
2019-06-09 13:03:20 +02:00
}