More clearly defined session management. Fixed problem when reconnecting + RMI create callbacks.
This commit is contained in:
parent
b2217f66ee
commit
78374e4dfc
|
@ -30,7 +30,8 @@ import dorkbox.network.connection.IpInfo.Companion.formatCommonAddress
|
|||
import dorkbox.network.connection.ListenerManager.Companion.cleanStackTrace
|
||||
import dorkbox.network.connection.ListenerManager.Companion.cleanStackTraceInternal
|
||||
import dorkbox.network.connection.session.SessionConnection
|
||||
import dorkbox.network.connection.session.SessionManager
|
||||
import dorkbox.network.connection.session.SessionManagerFull
|
||||
import dorkbox.network.connection.session.SessionManagerNoOp
|
||||
import dorkbox.network.exceptions.*
|
||||
import dorkbox.network.handshake.ClientConnectionDriver
|
||||
import dorkbox.network.handshake.ClientHandshake
|
||||
|
@ -141,10 +142,6 @@ open class Client<CONNECTION : Connection>(config: ClientConfiguration = ClientC
|
|||
@Volatile
|
||||
private var connection0: CONNECTION? = null
|
||||
|
||||
|
||||
@Volatile
|
||||
private var pendingMessagesAssigned = false
|
||||
|
||||
private val string0: String by lazy {
|
||||
"EndPoint [Client: ${storage.publicKey.toHexString()}]"
|
||||
}
|
||||
|
@ -701,21 +698,14 @@ open class Client<CONNECTION : Connection>(config: ClientConfiguration = ClientC
|
|||
// is rogue, we do not want to carelessly provide info.
|
||||
|
||||
|
||||
|
||||
// NOTE: this can change depending on what the server specifies!
|
||||
// should we queue messages during a reconnect? This is important if the client/server connection is unstable
|
||||
if (!pendingMessagesAssigned) {
|
||||
sessionManager = if (connectionInfo.enableSession) {
|
||||
pendingMessagesAssigned = true
|
||||
SessionManager(config, aeronDriver, connectionInfo.sessionTimeout)
|
||||
}
|
||||
else {
|
||||
pendingMessagesAssigned = true
|
||||
// this is a NO-OP version! We do not want if/else checks for every message!
|
||||
SessionManager.Companion.NoOp(config, aeronDriver)
|
||||
}
|
||||
if (connectionInfo.enableSession && sessionManager is SessionManagerNoOp) {
|
||||
sessionManager = SessionManagerFull(config, aeronDriver, connectionInfo.sessionTimeout)
|
||||
} else if (!connectionInfo.enableSession && sessionManager is SessionManagerFull) {
|
||||
sessionManager = SessionManagerNoOp()
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
//// RMI
|
||||
///////////////
|
||||
|
@ -791,13 +781,6 @@ open class Client<CONNECTION : Connection>(config: ClientConfiguration = ClientC
|
|||
storage.addRegisteredServerKey(address!!, connectionInfo.publicKey)
|
||||
}
|
||||
|
||||
// in the specific case of using sessions, we don't want to call 'init' or `connect` for a connection that is resuming a session
|
||||
var newSession = true
|
||||
if (sessionManager.enabled()) {
|
||||
newSession = sessionManager.onInit(newConnection as SessionConnection)
|
||||
}
|
||||
|
||||
|
||||
// tell the server our connection handshake is done, and the connection can now listen for data.
|
||||
// also closes the handshake (will also throw connect timeout exception)
|
||||
|
||||
|
@ -820,6 +803,14 @@ open class Client<CONNECTION : Connection>(config: ClientConfiguration = ClientC
|
|||
|
||||
newConnection.setImage()
|
||||
|
||||
// in the specific case of using sessions, we don't want to call 'init' or `connect` for a connection that is resuming a session
|
||||
var newSession = true
|
||||
if (sessionManager.enabled()) {
|
||||
// we want to restore RMI objects BEFORE the connection is fully setup!
|
||||
newSession = sessionManager.onInit(newConnection as SessionConnection)
|
||||
}
|
||||
|
||||
|
||||
// before we finish creating the connection, we initialize it (in case there needs to be logic that happens-before `onConnect` calls
|
||||
if (newSession) {
|
||||
listenerManager.notifyInit(newConnection)
|
||||
|
|
|
@ -20,7 +20,7 @@ import dorkbox.network.aeron.*
|
|||
import dorkbox.network.connection.*
|
||||
import dorkbox.network.connection.IpInfo.Companion.IpListenType
|
||||
import dorkbox.network.connection.ListenerManager.Companion.cleanStackTrace
|
||||
import dorkbox.network.connection.session.SessionManager
|
||||
import dorkbox.network.connection.session.SessionManagerFull
|
||||
import dorkbox.network.connectionType.ConnectionRule
|
||||
import dorkbox.network.exceptions.ServerException
|
||||
import dorkbox.network.handshake.ServerHandshake
|
||||
|
@ -106,16 +106,12 @@ open class Server<CONNECTION : Connection>(config: ServerConfiguration = ServerC
|
|||
}
|
||||
|
||||
init {
|
||||
sessionManager = if (config.enableSessionManagement) {
|
||||
SessionManager(config, aeronDriver, config.sessionTimeoutSeconds)
|
||||
}
|
||||
else {
|
||||
// this is a NO-OP version! We do not want if/else checks for every message!
|
||||
SessionManager.Companion.NoOp(config, aeronDriver)
|
||||
if (config.enableSessionManagement) {
|
||||
// only set this if we need to
|
||||
sessionManager = SessionManagerFull(config, aeronDriver, config.sessionTimeoutSeconds)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final override fun newException(message: String, cause: Throwable?): Throwable {
|
||||
// +2 because we do not want to see the stack for the abstract `newException`
|
||||
val serverException = ServerException(message, cause)
|
||||
|
|
|
@ -26,6 +26,7 @@ import dorkbox.network.aeron.BacklogStat
|
|||
import dorkbox.network.aeron.EventPoller
|
||||
import dorkbox.network.connection.session.SessionConnection
|
||||
import dorkbox.network.connection.session.SessionManager
|
||||
import dorkbox.network.connection.session.SessionManagerNoOp
|
||||
import dorkbox.network.connection.streaming.StreamingControl
|
||||
import dorkbox.network.connection.streaming.StreamingData
|
||||
import dorkbox.network.connection.streaming.StreamingManager
|
||||
|
@ -180,7 +181,12 @@ abstract class EndPoint<CONNECTION : Connection> private constructor(val type: C
|
|||
|
||||
private val streamingManager = StreamingManager<CONNECTION>(logger, config)
|
||||
|
||||
internal lateinit var sessionManager: SessionManager<SessionConnection>
|
||||
/**
|
||||
* By default, this is a NO-OP version! We do not want if/else checks for every message!
|
||||
* this can change of the lifespan of the CLIENT, depending on which/what server a single client connects to.
|
||||
*/
|
||||
@Volatile
|
||||
internal var sessionManager: SessionManager<SessionConnection> = SessionManagerNoOp()
|
||||
|
||||
|
||||
/**
|
||||
|
@ -959,11 +965,18 @@ abstract class EndPoint<CONNECTION : Connection> private constructor(val type: C
|
|||
// when an endpoint closes, the poll-loop shuts down, and removes itself from the list of poll actions that need to be performed.
|
||||
networkEventPoller.close(logger, this)
|
||||
|
||||
|
||||
// Connections MUST be closed first, because we want to make sure that no RMI messages can be received
|
||||
// when we close the RMI support objects (in which case, weird - but harmless - errors show up)
|
||||
// this will wait for RMI timeouts if there are RMI in-progress. (this happens if we close via an RMI method)
|
||||
responseManager.close()
|
||||
// IF CLOSED VIA RMI: this will wait for RMI timeouts if there are RMI in-progress.
|
||||
if (sessionManager.enabled()) {
|
||||
if (closeEverything) {
|
||||
// only close out RMI if we are using session management AND we are closing everything!
|
||||
responseManager.close(logger)
|
||||
}
|
||||
} else {
|
||||
// no session management, so always clear this out.
|
||||
responseManager.close(logger)
|
||||
}
|
||||
|
||||
// don't do these things if we are "closed" from a client connection disconnect
|
||||
// if there are any events going on, we want to schedule them to run AFTER all other events for this endpoint are done
|
||||
|
|
|
@ -22,10 +22,13 @@ import kotlinx.atomicfu.locks.withLock
|
|||
import java.util.concurrent.*
|
||||
|
||||
open class Session<CONNECTION: SessionConnection> {
|
||||
|
||||
|
||||
// the RMI objects are saved when the connection is removed, and restored BEFORE the connection is initialized, so there are no concerns
|
||||
// regarding the collision of RMI IDs and objects
|
||||
private val lock = ReentrantLock()
|
||||
private var oldProxyObjects: List<RemoteObject<*>>? = null
|
||||
private var oldProxyCallbacks: List<Pair<Int, Any.(Int) -> Unit>>? = null
|
||||
private var oldImplObjects: List<Pair<Int, Any>>? = null
|
||||
|
||||
/**
|
||||
|
@ -33,8 +36,13 @@ open class Session<CONNECTION: SessionConnection> {
|
|||
*/
|
||||
val pendingMessagesQueue: LinkedTransferQueue<Any> = LinkedTransferQueue()
|
||||
|
||||
@Volatile lateinit var connection: CONNECTION
|
||||
|
||||
|
||||
fun restore(connection: CONNECTION) {
|
||||
this.connection = connection
|
||||
connection.logger.debug("restoring connection")
|
||||
|
||||
lock.withLock {
|
||||
// this is called, even on a brand-new session, so we must have extra checks in place.
|
||||
val rmi = connection.rmi
|
||||
|
@ -42,6 +50,10 @@ open class Session<CONNECTION: SessionConnection> {
|
|||
rmi.recreateProxyObjects(oldProxyObjects!!)
|
||||
oldProxyObjects = null
|
||||
}
|
||||
if (oldProxyCallbacks != null) {
|
||||
rmi.restoreCallbacks(oldProxyCallbacks!!)
|
||||
oldProxyCallbacks = null
|
||||
}
|
||||
if (oldImplObjects != null) {
|
||||
rmi.restoreImplObjects(oldImplObjects!!)
|
||||
oldImplObjects = null
|
||||
|
@ -50,30 +62,49 @@ open class Session<CONNECTION: SessionConnection> {
|
|||
}
|
||||
|
||||
fun save(connection: CONNECTION) {
|
||||
connection.logger.debug("saving connection")
|
||||
val allProxyObjects = connection.rmi.getAllProxyObjects()
|
||||
val allProxyCallbacks = connection.rmi.getAllCallbacks()
|
||||
val allImplObjects = connection.rmi.getAllImplObjects()
|
||||
|
||||
// we want to save all the connection RMI objects, so they can be recreated on connect
|
||||
lock.withLock {
|
||||
oldProxyObjects = allProxyObjects
|
||||
oldProxyCallbacks = allProxyCallbacks
|
||||
oldImplObjects = allImplObjects
|
||||
}
|
||||
}
|
||||
|
||||
fun queueMessage(connection: SessionConnection, message: Any, abortEarly: Boolean) {
|
||||
fun queueMessage(connection: SessionConnection, message: Any, abortEarly: Boolean): Boolean {
|
||||
if (this.connection != connection) {
|
||||
// we received a message on an OLD connection (which is no longer connected ---- BUT we have a NEW connection that is connected)
|
||||
// this can happen on RMI object that are old
|
||||
val success = this.connection.send(message, abortEarly)
|
||||
if (success) {
|
||||
connection.logger.error("successfully resent message")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (!abortEarly) {
|
||||
// this was a "normal" send (instead of the disconnect message).
|
||||
pendingMessagesQueue.put(message)
|
||||
connection.logger.error("queueing message")
|
||||
}
|
||||
else if (connection.endPoint.aeronDriver.internal.mustRestartDriverOnError) {
|
||||
// the only way we get errors, is if the connection is bad OR if we are sending so fast that the connection cannot keep up.
|
||||
|
||||
// don't restart/reconnect -- there was an internal network error
|
||||
pendingMessagesQueue.put(message)
|
||||
connection.logger.error("queueing message")
|
||||
}
|
||||
else if (!connection.isConnected()) {
|
||||
// there was an issue - the connection should automatically reconnect
|
||||
pendingMessagesQueue.put(message)
|
||||
connection.logger.error("queueing message")
|
||||
}
|
||||
|
||||
connection.logger.error("NOT NOT NOT queueing message")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,10 +26,10 @@ open class SessionConnection(connectionParameters: ConnectionParams<*>): Connect
|
|||
override fun send(message: Any, abortEarly: Boolean): Boolean {
|
||||
val success = super.send(message, abortEarly)
|
||||
if (!success) {
|
||||
session.queueMessage(this, message, abortEarly)
|
||||
return session.queueMessage(this, message, abortEarly)
|
||||
}
|
||||
|
||||
return success
|
||||
return true
|
||||
}
|
||||
|
||||
fun sendPendingMessages() {
|
||||
|
|
|
@ -16,133 +16,9 @@
|
|||
|
||||
package dorkbox.network.connection.session
|
||||
|
||||
import dorkbox.bytes.ByteArrayWrapper
|
||||
import dorkbox.collections.LockFreeHashMap
|
||||
import dorkbox.hex.toHexString
|
||||
import dorkbox.network.Configuration
|
||||
import dorkbox.network.aeron.AeronDriver
|
||||
import dorkbox.network.connection.EndPoint
|
||||
import dorkbox.util.Sys
|
||||
import net.jodah.expiringmap.ExpirationPolicy
|
||||
import net.jodah.expiringmap.ExpiringMap
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.concurrent.*
|
||||
interface SessionManager<CONNECTION : SessionConnection> {
|
||||
|
||||
internal open class SessionManager<CONNECTION: SessionConnection>(config: Configuration, val aeronDriver: AeronDriver, sessionTimeout: Long) {
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(SessionManager::class.java.simpleName)
|
||||
|
||||
class NoOp<CONNECTION: SessionConnection>(config: Configuration, aeronDriver: AeronDriver):
|
||||
SessionManager<CONNECTION>(config, aeronDriver, 0L) {
|
||||
|
||||
override fun enabled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onInit(connection: CONNECTION): Boolean {
|
||||
// do nothing
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDisconnect(connection: CONNECTION) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val sessions = LockFreeHashMap<ByteArrayWrapper, Session<CONNECTION>>()
|
||||
|
||||
|
||||
// note: the expire time here is a 4x longer than the expire time in the client, this way we can adjust for network lag or quick reconnects
|
||||
private val expiringSessions = ExpiringMap.builder()
|
||||
.apply {
|
||||
// connections are extremely difficult to diagnose when the connection timeout is short
|
||||
val timeUnit = if (EndPoint.DEBUG_CONNECTIONS) { TimeUnit.HOURS } else { TimeUnit.NANOSECONDS }
|
||||
|
||||
// we MUST include the publication linger timeout, otherwise we might encounter problems that are NOT REALLY problems
|
||||
this.expiration(TimeUnit.SECONDS.toNanos(config.connectionCloseTimeoutInSeconds.toLong() * 2) + aeronDriver.lingerNs(), timeUnit)
|
||||
}
|
||||
.expirationPolicy(ExpirationPolicy.CREATED)
|
||||
.expirationListener<ByteArrayWrapper, Session<CONNECTION>> { publicKeyWrapped, _ ->
|
||||
// this blocks until it fully runs (which is ok. this is fast)
|
||||
logger.debug("Connection session for ${publicKeyWrapped.bytes.toHexString()} expired.")
|
||||
|
||||
}
|
||||
.build<ByteArrayWrapper, Session<CONNECTION>>()
|
||||
|
||||
|
||||
init {
|
||||
// ignore 0
|
||||
val check = TimeUnit.SECONDS.toNanos(sessionTimeout)
|
||||
val lingerNs = aeronDriver.lingerNs()
|
||||
val required = TimeUnit.SECONDS.toNanos(config.connectionCloseTimeoutInSeconds.toLong())
|
||||
require(check == 0L || check > required + lingerNs) {
|
||||
"The session timeout (${Sys.getTimePretty(check)}) must be longer than the connection close timeout (${Sys.getTimePretty(required)}) + the aeron driver linger timeout (${Sys.getTimePretty(lingerNs)})!"
|
||||
}
|
||||
}
|
||||
|
||||
open fun enabled(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* this must be called when a new connection is created AND when the internal `reconnect` occurs (as a result of a network error)
|
||||
*
|
||||
* @return true if this is a new session, false if it is an existing session
|
||||
*/
|
||||
open fun onInit(connection: CONNECTION): Boolean {
|
||||
val publicKeyWrapped = ByteArrayWrapper.wrap(connection.uuid)
|
||||
|
||||
var isNewSession = false
|
||||
val session = synchronized(sessions) {
|
||||
// always check if we are expiring first...
|
||||
val expiring = expiringSessions.remove(publicKeyWrapped)
|
||||
if (expiring != null) {
|
||||
expiring
|
||||
} else {
|
||||
val existing = sessions[publicKeyWrapped]
|
||||
if (existing != null) {
|
||||
existing
|
||||
} else {
|
||||
isNewSession = true
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val newSession: Session<CONNECTION> = if (connection.endPoint.isServer()) {
|
||||
(connection.endPoint as SessionServer).newSession() as Session<CONNECTION>
|
||||
} else {
|
||||
(connection.endPoint as SessionClient).newSession() as Session<CONNECTION>
|
||||
}
|
||||
|
||||
sessions[publicKeyWrapped] = newSession
|
||||
newSession
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connection.session = session
|
||||
session.restore(connection)
|
||||
|
||||
return isNewSession
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always called when a connection is disconnected from the network
|
||||
*/
|
||||
open fun onDisconnect(connection: CONNECTION) {
|
||||
val publicKeyWrapped = ByteArrayWrapper.wrap(connection.uuid)
|
||||
|
||||
val session = synchronized(sessions) {
|
||||
val session = sessions.remove(publicKeyWrapped)
|
||||
// we want to expire this session after XYZ time
|
||||
expiringSessions[publicKeyWrapped] = session
|
||||
session
|
||||
}
|
||||
|
||||
|
||||
session!!.save(connection)
|
||||
}
|
||||
fun enabled(): Boolean
|
||||
fun onInit(connection: CONNECTION): Boolean
|
||||
fun onDisconnect(connection: CONNECTION)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright 2023 dorkbox, llc
|
||||
*
|
||||
* 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 dorkbox.network.connection.session
|
||||
|
||||
import dorkbox.bytes.ByteArrayWrapper
|
||||
import dorkbox.collections.LockFreeHashMap
|
||||
import dorkbox.hex.toHexString
|
||||
import dorkbox.network.Configuration
|
||||
import dorkbox.network.aeron.AeronDriver
|
||||
import dorkbox.network.connection.EndPoint
|
||||
import dorkbox.util.Sys
|
||||
import net.jodah.expiringmap.ExpirationPolicy
|
||||
import net.jodah.expiringmap.ExpiringMap
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.concurrent.*
|
||||
|
||||
internal open class SessionManagerFull<CONNECTION: SessionConnection>(
|
||||
config: Configuration,
|
||||
val aeronDriver: AeronDriver,
|
||||
sessionTimeout: Long): SessionManager<CONNECTION> {
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(SessionManagerFull::class.java.simpleName)
|
||||
}
|
||||
|
||||
|
||||
private val sessions = LockFreeHashMap<ByteArrayWrapper, Session<CONNECTION>>()
|
||||
|
||||
|
||||
// note: the expire time here is a 4x longer than the expire time in the client, this way we can adjust for network lag or quick reconnects
|
||||
private val expiringSessions = ExpiringMap.builder()
|
||||
.apply {
|
||||
// connections are extremely difficult to diagnose when the connection timeout is short
|
||||
val timeUnit = if (EndPoint.DEBUG_CONNECTIONS) { TimeUnit.HOURS } else { TimeUnit.NANOSECONDS }
|
||||
|
||||
// we MUST include the publication linger timeout, otherwise we might encounter problems that are NOT REALLY problems
|
||||
this.expiration(TimeUnit.SECONDS.toNanos(config.connectionCloseTimeoutInSeconds.toLong() * 2) + aeronDriver.lingerNs(), timeUnit)
|
||||
}
|
||||
.expirationPolicy(ExpirationPolicy.CREATED)
|
||||
.expirationListener<ByteArrayWrapper, Session<CONNECTION>> { publicKeyWrapped, _ ->
|
||||
// this blocks until it fully runs (which is ok. this is fast)
|
||||
logger.debug("Connection session for ${publicKeyWrapped.bytes.toHexString()} expired.")
|
||||
|
||||
}
|
||||
.build<ByteArrayWrapper, Session<CONNECTION>>()
|
||||
|
||||
|
||||
init {
|
||||
// ignore 0
|
||||
val check = TimeUnit.SECONDS.toNanos(sessionTimeout)
|
||||
val lingerNs = aeronDriver.lingerNs()
|
||||
val required = TimeUnit.SECONDS.toNanos(config.connectionCloseTimeoutInSeconds.toLong())
|
||||
require(check == 0L || check > required + lingerNs) {
|
||||
"The session timeout (${Sys.getTimePretty(check)}) must be longer than the connection close timeout (${Sys.getTimePretty(required)}) + the aeron driver linger timeout (${Sys.getTimePretty(lingerNs)})!"
|
||||
}
|
||||
}
|
||||
|
||||
override fun enabled(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* this must be called when a new connection is created AND when the internal `reconnect` occurs (as a result of a network error)
|
||||
*
|
||||
* @return true if this is a new session, false if it is an existing session
|
||||
*/
|
||||
override fun onInit(connection: CONNECTION): Boolean {
|
||||
val publicKeyWrapped = ByteArrayWrapper.wrap(connection.uuid)
|
||||
|
||||
var isNewSession = false
|
||||
val session = synchronized(sessions) {
|
||||
// always check if we are expiring first...
|
||||
val expiring = expiringSessions.remove(publicKeyWrapped)
|
||||
if (expiring != null) {
|
||||
expiring
|
||||
} else {
|
||||
val existing = sessions[publicKeyWrapped]
|
||||
if (existing != null) {
|
||||
existing
|
||||
} else {
|
||||
isNewSession = true
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val newSession: Session<CONNECTION> = if (connection.endPoint.isServer()) {
|
||||
(connection.endPoint as SessionServer).newSession() as Session<CONNECTION>
|
||||
} else {
|
||||
(connection.endPoint as SessionClient).newSession() as Session<CONNECTION>
|
||||
}
|
||||
|
||||
sessions[publicKeyWrapped] = newSession
|
||||
newSession
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connection.session = session
|
||||
session.restore(connection)
|
||||
|
||||
return isNewSession
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always called when a connection is disconnected from the network
|
||||
*/
|
||||
override fun onDisconnect(connection: CONNECTION) {
|
||||
val publicKeyWrapped = ByteArrayWrapper.wrap(connection.uuid)
|
||||
|
||||
val session = synchronized(sessions) {
|
||||
val session = sessions.remove(publicKeyWrapped)
|
||||
// we want to expire this session after XYZ time
|
||||
expiringSessions[publicKeyWrapped] = session
|
||||
session
|
||||
}
|
||||
|
||||
|
||||
session!!.save(connection)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2023 dorkbox, llc
|
||||
*
|
||||
* 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 dorkbox.network.connection.session
|
||||
|
||||
class SessionManagerNoOp<CONNECTION : SessionConnection>: SessionManager<CONNECTION> {
|
||||
override fun enabled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onInit(connection: CONNECTION): Boolean {
|
||||
// do nothing
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDisconnect(connection: CONNECTION) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2020 dorkbox, llc
|
||||
* Copyright 2023 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,13 +13,5 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.network.rmi.messages
|
||||
|
||||
/**
|
||||
* @param rmiId which rmi object was deleted
|
||||
*/
|
||||
data class ConnectionObjectDeleteResponse(val rmiId: Int) : RmiMessage {
|
||||
override fun toString(): String {
|
||||
return "ConnectionObjectDeleteResponse(id: $rmiId)"
|
||||
}
|
||||
}
|
||||
package dorkbox.network.connection.session;
|
Loading…
Reference in New Issue