From 553ca5d52ae29a8932a393d11a9d354d27f4e458 Mon Sep 17 00:00:00 2001 From: tobias <thinkdifferent055@gmail.com> Date: Thu, 16 Feb 2017 22:28:05 +0100 Subject: [PATCH] Add basic websocket implementation for sync --- .../tobias/playpad/server/PlayPadServer.scala | 44 ++++++- .../server/project/ProjectHandler.scala | 111 ++++++++++++++++++ 2 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 src/main/scala/de/tobias/playpad/server/server/project/ProjectHandler.scala diff --git a/src/main/scala/de/tobias/playpad/server/PlayPadServer.scala b/src/main/scala/de/tobias/playpad/server/PlayPadServer.scala index ff74eb3..723fcd7 100644 --- a/src/main/scala/de/tobias/playpad/server/PlayPadServer.scala +++ b/src/main/scala/de/tobias/playpad/server/PlayPadServer.scala @@ -1,12 +1,16 @@ package de.tobias.playpad.server import java.nio.file.{Files, Paths} +import java.sql.{Driver, DriverManager} import com.j256.ormlite.dao.{Dao, DaoManager} import com.j256.ormlite.jdbc.JdbcConnectionSource import com.j256.ormlite.table.TableUtils +import de.tobias.playpad.server.account.{Account, Session} import de.tobias.playpad.server.plugin.Plugin +import de.tobias.playpad.server.server.account._ import de.tobias.playpad.server.server.plugin.{PluginGet, PluginList} +import de.tobias.playpad.server.server.project.ProjectHandler import de.tobias.playpad.server.settings.SettingsHandler import de.tobias.playpad.server.transformer.JsonTransformer import spark.Spark._ @@ -27,13 +31,21 @@ object PlayPadServer extends App { private val settings = settingsLoader.load(settingsPath) - private val databaseUrl = "jdbc:mysql://" + settings.db_host + ":" + settings.db_port + "/" + settings.db_database + private val databaseUrl = "jdbc:mysql://" + settings.db_host + ":" + settings.db_port + "/" + settings.db_database + "?autoReconnect=true" var connectionSource = new JdbcConnectionSource(databaseUrl) connectionSource.setUsername(settings.db_username) connectionSource.setPassword(settings.db_password) - val dao: Dao[Plugin, Int] = DaoManager.createDao(connectionSource, classOf[Plugin]) + private val databaseConnection = DriverManager.getConnection(databaseUrl, settings.db_username, settings.db_password) + + val pluginDao: Dao[Plugin, Int] = DaoManager.createDao(connectionSource, classOf[Plugin]) + val accountDao: Dao[Account, Int] = DaoManager.createDao(connectionSource, classOf[Account]) + val sessionDao: Dao[Session, Int] = DaoManager.createDao(connectionSource, classOf[Session]) + TableUtils.createTableIfNotExists(connectionSource, classOf[Plugin]) + TableUtils.createTableIfNotExists(connectionSource, classOf[Account]) + TableUtils.createTableIfNotExists(connectionSource, classOf[Session]) + // Setup Http Server port(8090) @@ -43,10 +55,32 @@ object PlayPadServer extends App { secure("deploy/keystore.jks", settings.keystorePassword, null, null) - get("/plugins/:id", new PluginGet(dao), new JsonTransformer) - get("/plugins", new PluginList(dao), new JsonTransformer) + // PlayWall Cloud + webSocket("/project", new ProjectHandler(sessionDao, databaseConnection)) - RouteOverview.enableRouteOverview() + // Plugins + get("/plugins/:id", new PluginGet(pluginDao), new JsonTransformer) + get("/plugins", new PluginList(pluginDao), new JsonTransformer) + + // Account + post("/accounts", new AccountPost(accountDao), new JsonTransformer) + put("/accounts", new AccountPut(accountDao), new JsonTransformer) + post("/sessions", new SessionPost(accountDao), new JsonTransformer) + delete("/sessions", new SessionDelete(accountDao), new JsonTransformer) + get("/sessions", new SessionGet(accountDao), new JsonTransformer) + + // DEBUG + exception(classOf[Exception], (exception, _, _) => { + exception.printStackTrace() + halt(500, "internal error: " + exception.getLocalizedMessage) + }) + + RouteOverview.enableRouteOverview() SettingsHandler.saver.save(settings, settingsPath) + + Runtime.getRuntime.addShutdownHook(new Thread(() => { + databaseConnection.close() + stop() + })) } diff --git a/src/main/scala/de/tobias/playpad/server/server/project/ProjectHandler.scala b/src/main/scala/de/tobias/playpad/server/server/project/ProjectHandler.scala new file mode 100644 index 0000000..8e61f0e --- /dev/null +++ b/src/main/scala/de/tobias/playpad/server/server/project/ProjectHandler.scala @@ -0,0 +1,111 @@ +package de.tobias.playpad.server.server.project + +import java.sql.Connection + +import com.google.gson.{JsonObject, JsonParser} +import com.j256.ormlite.dao.Dao +import com.sun.xml.internal.bind.v2.model.core.ID +import de.tobias.playpad.server.account +import de.tobias.playpad.server.account.Account +import org.eclipse.jetty.websocket.api.Session +import org.eclipse.jetty.websocket.api.annotations.{OnWebSocketClose, OnWebSocketConnect, OnWebSocketMessage, WebSocket} + +import scala.collection.{Map, mutable} + +/** + * Created by tobias on 13.02.17. + */ +@WebSocket class ProjectHandler(sessionDao: Dao[account.Session, Int], connection: Connection) { + + // TODO mutable.HashSet --> Set + private var sessions: Map[Account, mutable.HashSet[Session]] = new mutable.HashMap[Account, mutable.HashSet[Session]]() + + @OnWebSocketConnect def onConnect(serverSession: Session): Unit = { + val key = serverSession.getUpgradeRequest.getHeader("key") + if (key == null) { + serverSession.close(500, "Invalid Key") + } + + val sessions = sessionDao.queryForEq("key", key) + if (sessions.size() == 1) { + val session = sessions.get(0) + if (!this.sessions.contains(session.getAccount)) { + this.sessions += (session.getAccount -> new mutable.HashSet[Session]()) + } + this.sessions(session.getAccount) += serverSession + } else { + serverSession.close(500, "Invalid Key") + } + } + + @OnWebSocketClose def onClose(serverSession: Session, status: Int, reason: String): Unit = { + val key = serverSession.getUpgradeRequest.getHeader("key") + if (key == null) { + serverSession.close(500, "Invalid Key") + } + + val sessions = sessionDao.queryForEq("key", key) + if (sessions.size() == 1) { + val session = sessions.get(0) + this.sessions(session.getAccount) -= serverSession + } + } + + @OnWebSocketMessage def onMessage(serverSession: Session, text: String): Unit = { + // Store in Database + try { + val json = new JsonParser().parse(text) + json match { + case jsonObject: JsonObject => + val className = jsonObject.get("class").getAsString + val field = jsonObject.get("field").getAsString + val id = jsonObject.get("id").getAsString + + val sql = "INSERT INTO " + className + " (id, " + field + ") VALUES(?, ?) " + + "ON DUPLICATE KEY UPDATE " + field + "=?" + val preparedStatement = connection.prepareStatement(sql) + + preparedStatement.setString(1, id) + + val valueType = Class.forName(jsonObject.get("type").getAsString) + if (valueType == classOf[String]) { + val value = jsonObject.get("value").getAsString + preparedStatement.setString(2, value) + preparedStatement.setString(3, value) + } else if (valueType == classOf[Boolean]) { + val value = jsonObject.get("value").getAsBoolean + preparedStatement.setBoolean(2, value) + preparedStatement.setBoolean(3, value) + } else if (valueType == classOf[Integer]){ + val value = jsonObject.get("value").getAsInt + preparedStatement.setInt(2, value) + preparedStatement.setInt(3, value) + } else if (valueType == classOf[Double]){ + val value = jsonObject.get("value").getAsDouble + preparedStatement.setDouble(2, value) + preparedStatement.setDouble(3, value) + } + + preparedStatement.execute() + preparedStatement.close() + case _ => + } + + // Push to clients + val key = serverSession.getUpgradeRequest.getHeader("key") + if (key == null) { + serverSession.close(500, "Invalid Key") + } + + val sessions = sessionDao.queryForEq("key", key) + if (sessions.size() == 1) { + val session = sessions.get(0) + this.sessions(session.getAccount) + .filter(s => s != serverSession) + .foreach(s => s.getRemote.sendStringByFuture(text)) + } + } catch { + case e: Exception => e.printStackTrace() + } + } +} \ No newline at end of file -- GitLab