Skip to content

Commit

Permalink
Merge pull request #1 from UB-CSE/initial-setup
Browse files Browse the repository at this point in the history
Initial setup
  • Loading branch information
jessehartloff authored Apr 23, 2020
2 parents 74c06a3 + 992e76f commit ca745fb
Show file tree
Hide file tree
Showing 14 changed files with 473 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.class
*.log
.idea/
47 changes: 47 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
FROM ubuntu:18.04

RUN apt-get update

RUN apt-get install -y wget

#Java Setup
RUN apt-get update --fix-missing
RUN apt-get install -y default-jdk

#Scala setup
RUN apt-get remove scala-library scala
RUN wget http://scala-lang.org/files/archive/scala-2.12.6.deb
RUN dpkg -i scala-2.12.6.deb
RUN apt-get update
RUN apt-get install -y scala

RUN apt-get install -y gnupg2
RUN echo "deb https://dl.bintray.com/sbt/debian /" | tee -a /etc/apt/sources.list.d/sbt.list
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
RUN apt-get update
RUN apt-get install -y sbt

#Maven Setup
RUN apt-get install -y maven


# Set the home directory to /root and cd into that directory
ENV HOME /root
WORKDIR /root


# Copy all app files into the image
COPY . .

# Download dependancies and build the app
RUN mvn package


# Allow port 8080 to be accessed from outside the container
EXPOSE 8080

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait /wait
RUN chmod +x /wait

# Run the app
CMD /wait && java -jar target/todo-scheduler-0.0.1-jar-with-dependencies.jar
19 changes: 19 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: '3.3'
services:
nginx:
build: ./nginx
ports:
- '9006:80'
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: 'changeme'
MYSQL_DATABASE: 'todo'
MYSQL_USER: 'sqluser'
MYSQL_PASSWORD: 'changeme'
app:
build: .
environment:
WAIT_HOSTS: mysql:3306
DB_USERNAME: 'sqluser'
DB_PASSWORD: 'changeme'
6 changes: 6 additions & 0 deletions nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM nginx

COPY ./public /usr/share/nginx/html
COPY ./default.conf /etc/nginx/conf.d

EXPOSE 80
17 changes: 17 additions & 0 deletions nginx/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
server {
listen 80;
server_name localhost;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

location /socket.io {
proxy_pass http://app:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}

}
24 changes: 24 additions & 0 deletions nginx/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Office Hours</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
</head>
<body>

<br/>

<h3 id="message">Welcome!</h3>

<label for="title">Title: </label><input type="text" id="title"/><br/>
<label for="desc">Description: </label><input type="text" id="desc"/><br/>
<button onclick="addTask();">Add Task</button>
<br/><br/>

<div id="tasks"></div>

<script src="todo.js"></script>

</body>
</html>
32 changes: 32 additions & 0 deletions nginx/public/todo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const socket = io.connect("http://localhost:8080", {transports: ['websocket']});

socket.on('all_tasks', displayTasks);
socket.on('message', displayMessage);

function displayMessage(newMessage) {
document.getElementById("message").innerHTML = newMessage;
}

function displayTasks(tasksJSON) {
const tasks = JSON.parse(tasksJSON);
let formattedTasks = "";
for (const task of tasks) {
formattedTasks += "<hr/>";
formattedTasks += "<b>" + task['title'] + "</b> - " + task['description'] + "<br/>";
formattedTasks += "<button onclick='completeTask(\"" + task['id'] + "\")'>Task Complete</button>";
}
document.getElementById("tasks").innerHTML = formattedTasks;
}


function addTask() {
let title = document.getElementById("title").value;
let desc = document.getElementById("desc").value;
socket.emit("add_task", JSON.stringify({"title": title, "description": desc}));
document.getElementById("title").value = "";
document.getElementById("desc").value = "";
}

function completeTask(taskId) {
socket.emit("complete_task", taskId);
}
88 changes: 88 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<project xmlns="http://maven.apache.org/POM/4.0.0">
<groupId>edu.buffalo.cse</groupId>
<artifactId>todo-scheduler</artifactId>
<modelVersion>4.0.0</modelVersion>
<url>http://maven.apache.org</url>
<version>0.0.1</version>

<properties>
<encoding>UTF-8</encoding>
<scala.version>2.12.9</scala.version>
</properties>

<dependencies>

<!-- https://mvnrepository.com/artifact/com.typesafe.play/play-json -->
<dependency>
<groupId>com.typesafe.play</groupId>
<artifactId>play-json_2.12</artifactId>
<version>2.7.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>

<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.12</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.28</version>
</dependency>

</dependencies>

<build>
<plugins>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>model.TodoServer</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.15.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>

</project>
7 changes: 7 additions & 0 deletions src/main/scala/model/Configuration.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package model

object Configuration {

val DEV_MODE = true

}
41 changes: 41 additions & 0 deletions src/main/scala/model/Task.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package model

import play.api.libs.json.{JsValue, Json}


object Task {

var nextId: Int = 0

def cleanString(input: String, maxLength: Int = 100): String = {
var output = input
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
if (output.length > maxLength) {
output = output.slice(0, maxLength) + "..."
}
output
}

def apply(title: String, description: String): Task = {
val thisId = nextId
nextId += 1
new Task(cleanString(title), cleanString(description, 1000), thisId.toString)
}


}

class Task(val title: String, val description: String, val id: String) {

def asJsValue(): JsValue ={
val taskMap: Map[String, JsValue] = Map(
"title" -> Json.toJson(title),
"description" -> Json.toJson(description),
"id" -> Json.toJson(id)
)
Json.toJson(taskMap)
}

}
89 changes: 89 additions & 0 deletions src/main/scala/model/TodoServer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package model

import com.corundumstudio.socketio.listener.{ConnectListener, DataListener}
import com.corundumstudio.socketio.{AckRequest, Configuration, SocketIOClient, SocketIOServer}
import model.database.{Database, DatabaseAPI, TestingDatabase}
import play.api.libs.json.{JsValue, Json}


class TodoServer() {

val database: DatabaseAPI = if (Configuration.DEV_MODE) {
new TestingDatabase
} else {
new Database
}

setNextId()

var usernameToSocket: Map[String, SocketIOClient] = Map()
var socketToUsername: Map[SocketIOClient, String] = Map()

val config: Configuration = new Configuration {
setHostname("0.0.0.0")
setPort(8080)
}

val server: SocketIOServer = new SocketIOServer(config)

server.addConnectListener(new ConnectionListener(this))
server.addEventListener("add_task", classOf[String], new AddTaskListener(this))
server.addEventListener("complete_task", classOf[String], new CompleteTaskListener(this))

server.start()

def tasksJSON(): String = {
val tasks: List[Task] = database.getTasks
val tasksJSON: List[JsValue] = tasks.map((entry: Task) => entry.asJsValue())
Json.stringify(Json.toJson(tasksJSON))
}

def setNextId(): Unit = {
val tasks = database.getTasks
if (tasks.nonEmpty) {
Task.nextId = tasks.map(_.id.toInt).max + 1
}
}

}

object TodoServer {
def main(args: Array[String]): Unit = {
new TodoServer()
}
}


class ConnectionListener(server: TodoServer) extends ConnectListener {

override def onConnect(socket: SocketIOClient): Unit = {
socket.sendEvent("all_tasks", server.tasksJSON())
}

}


class AddTaskListener(server: TodoServer) extends DataListener[String] {

override def onData(socket: SocketIOClient, taskJSON: String, ackRequest: AckRequest): Unit = {
val task: JsValue = Json.parse(taskJSON)
val title: String = (task \ "title").as[String]
val description: String = (task \ "description").as[String]

server.database.addTask(Task(title, description))
server.server.getBroadcastOperations.sendEvent("all_tasks", server.tasksJSON())
}

}


class CompleteTaskListener(server: TodoServer) extends DataListener[String] {

override def onData(socket: SocketIOClient, taskId: String, ackRequest: AckRequest): Unit = {
server.database.completeTask(taskId)
server.server.getBroadcastOperations.sendEvent("all_tasks", server.tasksJSON())
}

}


Loading

0 comments on commit ca745fb

Please sign in to comment.