From 5cd18fc6ee855863f34c281ab8771fe239e65f29 Mon Sep 17 00:00:00 2001 From: Awwabsiddiqui Date: Sun, 22 Jun 2025 13:05:30 +0530 Subject: [PATCH 1/2] The "on duplicate" does not work in hsql DB, changed to on conflict --- .../openfire/plugins/pushserver/dao/PushServerDao.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt b/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt index 96fc92f..39bc90b 100644 --- a/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt +++ b/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt @@ -10,10 +10,16 @@ object PushServerDao { private const val TABLE_NAME = "ofPushServer" +// private const val ADD_PUSH_RECORD = """ +// INSERT INTO $TABLE_NAME (domain, deviceId, token, node, secret, type) +// VALUES (?, ?, ?, ?, ?, ?) +// ON DUPLICATE KEY UPDATE token = VALUES(token) +// """ + private const val ADD_PUSH_RECORD = """ INSERT INTO $TABLE_NAME (domain, deviceId, token, node, secret, type) VALUES (?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE token = VALUES(token) + ON CONFLICT (domain, deviceId) DO UPDATE SET token=?; """ private const val DELETE_PUSH_RECORD = """ DELETE FROM $TABLE_NAME @@ -33,7 +39,7 @@ object PushServerDao { fun addPushRecord(pushRecord: PushRecord): PushRecord? { return DbUtils.doWithConnection( ADD_PUSH_RECORD - , listOf(pushRecord.domain, pushRecord.deviceId, pushRecord.token, pushRecord.node, pushRecord.secret, pushRecord.type.name) + , listOf(pushRecord.domain, pushRecord.deviceId, pushRecord.token, pushRecord.node, pushRecord.secret, pushRecord.type.name, pushRecord.token) , { conn, statement -> statement.executeUpdate() From e92c4805e836d313bf9cac680a57c7e8a1c678d6 Mon Sep 17 00:00:00 2001 From: Awwabsiddiqui Date: Tue, 24 Jun 2025 18:13:49 +0530 Subject: [PATCH 2/2] ISSUE FIXED According to my RnD, the previously implemented "ON DUPLICATE" works only with MySQL DB (this was probably the DB used when developing the plugin). The updated "ON CONFLICT" query works in all major DBs, except for MySQL. However, the default DB for OpenFire (HSQL DB) supports neither of them. We can either change the query according to the DB(as suggested), or provide this implementation. Try/Catch or a case based SQL approach does not wok in DML of any SQL, hence a count query would be more appropriate. Also, I do not know kotlin and would very much appreciate any input in optimizing the code if possible. Note: With this approach, we can effectively remove the unique constraint on the table as it would merely be a precaution with time overhead. --- .../plugins/pushserver/dao/PushServerDao.kt | 202 +++++++++++++++--- 1 file changed, 170 insertions(+), 32 deletions(-) diff --git a/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt b/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt index 39bc90b..5f27b17 100644 --- a/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt +++ b/src/java/org/igniterealtime/openfire/plugins/pushserver/dao/PushServerDao.kt @@ -10,16 +10,29 @@ object PushServerDao { private const val TABLE_NAME = "ofPushServer" -// private const val ADD_PUSH_RECORD = """ -// INSERT INTO $TABLE_NAME (domain, deviceId, token, node, secret, type) -// VALUES (?, ?, ?, ?, ?, ?) -// ON DUPLICATE KEY UPDATE token = VALUES(token) -// """ - + private const val PUSH_RECORD_COUNT = """ + SELECT COUNT(1) + FROM $TABLE_NAME + WHERE domain = ? + AND deviceId = ? + """ private const val ADD_PUSH_RECORD = """ INSERT INTO $TABLE_NAME (domain, deviceId, token, node, secret, type) - VALUES (?, ?, ?, ?, ?, ?) - ON CONFLICT (domain, deviceId) DO UPDATE SET token=?; + VALUES (?, ?, ?, ?, ?, ?); + """ + private const val UPDATE_PUSH_RECORD = """ + UPDATE $TABLE_NAME SET token = ? + WHERE domain = ? AND deviceId = ? + """ + private const val UPSERT_PUSH_RECORD = """ + INSERT INTO $TABLE_NAME (domain, deviceId, token, node, secret, type) + VALUES (?, ?, ?, ?, ?, ?) + ON CONFLICT (domain, deviceId) DO UPDATE SET token=? + """ + private const val UPSERT_PUSH_RECORD_SQL = """ + INSERT INTO $TABLE_NAME (domain, deviceId, token, node, secret, type) + VALUES (?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE token = VALUES(token); """ private const val DELETE_PUSH_RECORD = """ DELETE FROM $TABLE_NAME @@ -36,36 +49,161 @@ object PushServerDao { WHERE domain = ? AND node = ? """ - fun addPushRecord(pushRecord: PushRecord): PushRecord? { - return DbUtils.doWithConnection( - ADD_PUSH_RECORD - , listOf(pushRecord.domain, pushRecord.deviceId, pushRecord.token, pushRecord.node, pushRecord.secret, pushRecord.type.name, pushRecord.token) - , { conn, statement -> - statement.executeUpdate() +fun addPushRecord(pushRecord: PushRecord): PushRecord? { //select based + try { + val count = DbUtils.doWithConnection( + sql = PUSH_RECORD_COUNT, + parameterList = listOf(pushRecord.domain, pushRecord.deviceId), + closure = { _, stmt -> + val rs = stmt.executeQuery() + val count = if (rs.next()) rs.getInt(1) else 0 + rs.close() + count + }, + onFailed = { + logger.error("Failed to check existing push record count.", it) + } + ) ?: 0 - val rs = DbUtils.createStatement( - conn - , SELECT_PUSH_RECORD - , listOf(pushRecord.domain, pushRecord.deviceId) - )?.executeQuery() + if (count > 0) { + DbUtils.doWithConnection( + sql = UPDATE_PUSH_RECORD, + parameterList = listOf( + pushRecord.token, + pushRecord.domain, + pushRecord.deviceId + ), + closure = { _, stmt -> + stmt.executeUpdate() + }, + onFailed = { + logger.error("Failed to update push record.", it) + } + ) + } else { + DbUtils.doWithConnection( + sql = ADD_PUSH_RECORD, + parameterList = listOf( + pushRecord.domain, + pushRecord.deviceId, + pushRecord.token, + pushRecord.node, + pushRecord.secret, + pushRecord.type.name + ), + closure = { _, stmt -> + stmt.executeUpdate() + }, + onFailed = { + logger.error("Failed to insert push record.", it) + } + ) + } - if (rs?.next() == true) { + return DbUtils.doWithConnection( + sql = SELECT_PUSH_RECORD, + parameterList = listOf(pushRecord.domain, pushRecord.deviceId), + closure = { _, stmt -> + val rs = stmt.executeQuery() + if (rs.next()) { PushRecord( - domain = pushRecord.domain - , deviceId = pushRecord.deviceId - , token = rs.getString("token") - , type = rs.getString("type") - , node = rs.getString("node") - , secret = rs.getString("secret") + domain = pushRecord.domain, + deviceId = pushRecord.deviceId, + token = rs.getString("token"), + node = rs.getString("node"), + secret = rs.getString("secret"), + type = rs.getString("type") ) - } else { - null - } + } else null + }, + onFailed = { + logger.error("Failed to fetch push record after insert/update.", it) } - ) { - logger.error("PushRecord couldn't be inserted.", it) - } + ) + } catch (e: Exception) { + logger.error("Unexpected failure in addPushRecord()", e) + return null } +} + + // fun addPushRecord(pushRecord: PushRecord): PushRecord? { // try catch based + // return DbUtils.doWithConnection( + // UPSERT_PUSH_RECORD + // , listOf(pushRecord.domain, pushRecord.deviceId, pushRecord.token, pushRecord.node, pushRecord.secret, pushRecord.type.name, pushRecord.token) + // , { conn, statement -> + // try { + // statement.executeUpdate() + // } catch (e: Exception) { + // val insertStatement = DbUtils.createStatement( + // conn, + // UPSERT_PUSH_RECORD_SQL, + // listOf( + // pushRecord.domain, + // pushRecord.deviceId, + // pushRecord.token, + // pushRecord.node, + // pushRecord.secret, + // pushRecord.type.name + // ) + // ) + // insertStatement?.executeUpdate() + // } + + // val rs = DbUtils.createStatement( + // conn + // , SELECT_PUSH_RECORD + // , listOf(pushRecord.domain, pushRecord.deviceId) + // )?.executeQuery() + + // if (rs?.next() == true) { + // PushRecord( + // domain = pushRecord.domain + // , deviceId = pushRecord.deviceId + // , token = rs.getString("token") + // , type = rs.getString("type") + // , node = rs.getString("node") + // , secret = rs.getString("secret") + // ) + // } else { + // null + // } + // } + // ) { + + // logger.error("PushRecord couldn't be inserted.", it) + // } + // } + + // fun addPushRecordOld(pushRecord: PushRecord): PushRecord? { + // return DbUtils.doWithConnection( + // ADD_PUSH_RECORD + // , listOf(pushRecord.domain, pushRecord.deviceId, pushRecord.token, pushRecord.node, pushRecord.secret, pushRecord.type.name, pushRecord.token) + // , { conn, statement -> + // statement.executeUpdate() + + // val rs = DbUtils.createStatement( + // conn + // , SELECT_PUSH_RECORD + // , listOf(pushRecord.domain, pushRecord.deviceId) + // )?.executeQuery() + + // if (rs?.next() == true) { + // PushRecord( + // domain = pushRecord.domain + // , deviceId = pushRecord.deviceId + // , token = rs.getString("token") + // , type = rs.getString("type") + // , node = rs.getString("node") + // , secret = rs.getString("secret") + // ) + // } else { + // null + // } + // } + // ) { + // logger.error("PushRecord couldn't be inserted.", it) + // } + // } fun deletePushRecord(domain: String, deviceId: String): Boolean? { return DbUtils.doWithConnection(