├── .gitignore ├── LICENSE ├── README.md ├── check-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── skb │ │ │ │ └── checkservice │ │ │ │ ├── CheckServiceApplication.java │ │ │ │ ├── config │ │ │ │ ├── kafka │ │ │ │ │ ├── KafkaConsumerConfig.java │ │ │ │ │ └── KafkaProducerConfig.java │ │ │ │ └── redis │ │ │ │ │ ├── ClusterConfigurationProperties.java │ │ │ │ │ └── RedisConfig.java │ │ │ │ ├── controller │ │ │ │ └── ConnectController.java │ │ │ │ ├── domain │ │ │ │ ├── User │ │ │ │ │ └── UserDto.java │ │ │ │ └── WatchInfo │ │ │ │ │ ├── WatchInfo.java │ │ │ │ │ └── WatchInfoDto.java │ │ │ │ ├── message │ │ │ │ ├── MessageListener.java │ │ │ │ └── MessageSender.java │ │ │ │ └── service │ │ │ │ ├── CheckViewingService.java │ │ │ │ └── RedisClusterService.java │ │ └── resources │ │ │ └── application.yml │ └── test │ │ └── java │ │ └── com │ │ └── skb │ │ └── checkservice │ │ ├── CheckViewingServiceTest.java │ │ └── RedisClusterServiceTest.java └── watch-info.json └── push-service ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── skb │ │ └── pushservice │ │ ├── PushServiceApplication.java │ │ ├── config │ │ ├── MessageConsumerConfig.java │ │ ├── MessageProducerConfig.java │ │ ├── WebSecurityConfig.java │ │ └── WebSocketConfig.java │ │ ├── controller │ │ └── NotificationController.java │ │ ├── domain │ │ ├── Exist │ │ │ └── ExistDto.java │ │ └── WatchInfo │ │ │ ├── WatchInfo.java │ │ │ └── WatchInfoDto.java │ │ ├── message │ │ ├── MessageListener.java │ │ └── MessageSender.java │ │ └── service │ │ ├── ConnectService.java │ │ └── NotificationService.java └── resources │ ├── application.yaml │ ├── static │ ├── css │ │ └── main.css │ ├── img │ │ ├── logo.png │ │ └── play_img.png │ └── js │ │ ├── sockjs-0.3.4.min.js │ │ └── stomp.min.js │ └── templates │ ├── index.html │ └── notifications.html └── test └── java └── com └── skb └── pushservice └── PushServiceApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .java-version 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yumin Kim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 👀 simultaneous-viewing-limit-system 2 | Limit simultaneous viewing with Apache Kafka & Spring Boot Microservices, Redis 3 | 4 | 5 | 6 | ## Overview 7 | 8 | 9 | ### 📲 About OTT Service.. 10 | 11 |
12 | 13 | Most people use media services. Recently, users (especially because of the covid-19 😷 ) have been staying indoors, and usage has been increasing. In particular, it uses OTT services, which have subscription-based pricing services. 14 | 15 | Therefore, there is a trend that users **share their accounts** and use them together. Are there any **policy or technical issues** that could arise from this? 16 | 17 | If it is an account sharing service, there is an issue about **`simultaneous viewing restrictions`**. Then, let's implement this simultaneous viewing limit asynchronously and expandable from the basics through various micro-services! 👩🏻‍💻👨‍💻 18 | 19 |
20 | 21 | ### 📮 Why Redis? 22 | 23 | In release server, there will be a lot of connections, and watch logs. 24 | 25 | This system checks log with `key = set-top-box_id` & `hashkey = unique vod episode_id` for searching faster 26 | 27 |
28 |
29 | 30 | ### 🧤 Why Kafka? 31 | 32 | Micro services need to response each other async. 33 | 34 | So, use Kafka as a `Message Queue`. 35 | 36 | Produce topic, and Consume each topics that micro service need to subscribe. 37 | 38 |
39 |
40 |
41 | 42 | ## 📝 Before we start.. 43 | 44 | 1. Push service ( Spring Boot Micro service ) 45 | 46 | 2. Check service ( Spring Boot Micro service) 47 | 48 | 3. Redis Cluster 49 | - 3 master nodes ↔ 3 slave nodes 50 | 51 | 4. Kafka Cluster 52 | - 3 kafka cluster ↔ 1 zookeeper server (Actually, 3 zookeeper server is proper) 53 | 54 | All of these servers are executed by `localhost:portnumber` 55 | 56 |
57 |
58 | 59 | ### System Architecture 60 | 61 | ![image](https://user-images.githubusercontent.com/46887352/91052403-5ffcbc80-e65c-11ea-9f86-510c228c4e37.png) 62 | 63 | 64 |
65 |
66 | 67 | ## System Requirements 68 | + Java 8 69 | + Apache Kafka 2.3.0 70 | + Redis 6.0.x 71 | 72 |
73 |
74 | 75 | ### You can start Redis & Kafka cluster easily with these documents! 76 | #### Redis Cluster tutorial 77 | [😎 Start Redis cluster easily](https://github.com/kimyuuum/redis-cluster-tutorial) 78 |
79 | 80 | #### Kafka tutorial 81 | [🙄 Start Kafka easily](https://github.com/kimyuuum/kafka-cluster-tutorial) 82 | 83 |
84 |
85 | 86 | ### Server ports 87 | 88 | Push Service : `9002` 89 |
90 | Check Service : `9000` 91 |
92 | Redis Cluster : `7001 ~ 7005` 93 |
94 | Kafka Cluster : `9093 ~ 9095` 95 |
96 | Zookeeper : `2182` 97 |
98 |
99 | 100 | ### Required Parameter 101 | 102 | **Request Params for Start VOD** 103 | 104 | ``` 105 | { 106 | pcid = "username", 107 | episode_id = "episode_id", 108 | stb_id = "stb_id", 109 | play_start = "YYYY-mm-dd'T'HH:mm:ss", 110 | mac_address = "mac_address", 111 | running = true 112 | 113 | } 114 | ``` 115 | 116 | **Request Params for Stop VOD** 117 | 118 | ``` 119 | { 120 | pcid = "username", 121 | episode_id = "episode_id", 122 | stb_id = "stb_id", 123 | play_start = "YYYY-mm-dd'T'HH:mm:ss", 124 | play_end = "YYYY-mm-dd'T'HH:mm:ss", 125 | mac_address = "mac_address", 126 | running = false 127 | } 128 | 129 | ``` 130 | 131 |
132 |
133 | 134 | ## API Server 135 | 136 | 137 | | METHOD | PATH | DESCRIPTION | 138 | | ------ | ------ | ------------------------------------------------------------ | 139 | | POST | :9002/send | Send request data for start watching VOD when new user connects | 140 | | POST | :9002/force | Send request data for start watching VOD when another user exists | 141 | | PATCH | :9002/stop | Send request data for stop watching VOD | 142 | | POST | :9000/connect | Send request data for check connected user | 143 | 144 |
145 |
146 | 147 | | PATH | DESCRIPTION | 148 | | ----------------- | ---------------------------------------------------------- | 149 | | /queue/notify | send notify to user who subscribed each channel | 150 | | /queue/disconnect | send disconnect notify to user who subscribed each channel | 151 | | /queue/connect | send success response to user who subscribed each channel | 152 | 153 | 154 |
155 |
156 | 157 | ## Execute Spring boot application 158 | 159 | 1. Execute with mvnw 160 | 161 | ```bash 162 | $ ./mvnw spring-boot:run 163 | ``` 164 |
165 | 166 | 2. Maven Packaging 167 | 168 | ```bash 169 | $ mvn package 170 | ``` 171 |
172 | 173 | 3. Execute with jar file 174 | 175 | ```bash 176 | $ java -jar target/push-service-0.0.1-SNAPSHOT.jar 177 | $ java -jar target/check-service-0.0.1-SNAPSHOT.jar 178 | ``` 179 | 180 |
181 |
182 | 183 | ## Conclusion 184 | 185 | Now you can use this Viewing-Limit-System. 186 | 187 | Contact with my github profiles / dbals4818@gmail.com 188 | 189 | -------------------------------------------------------------------------------- /check-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | !**/src/main/**/target/ 3 | !**/src/test/**/target/ 4 | 5 | ## Maven: 6 | !.mvn/wrapper/maven-wrapper.jar 7 | /.mvn/ 8 | /mvnw 9 | /mvnw.bat 10 | mvnw.cmd 11 | /log/ 12 | *.log* 13 | target/ 14 | pom.xml.tag 15 | pom.xml.releaseBackup 16 | pom.xml.next 17 | release.properties 18 | 19 | ### STS ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | 28 | ### IntelliJ IDEA ### 29 | .idea 30 | *.iws 31 | *.iml 32 | *.ipr 33 | 34 | ### NetBeans ### 35 | /nbproject/private/ 36 | /nbbuild/ 37 | /dist/ 38 | /nbdist/ 39 | /.nb-gradle/ 40 | build/ 41 | !**/src/main/**/build/ 42 | !**/src/test/**/build/ 43 | 44 | ### VS Code ### 45 | .vscode/ 46 | 47 | ### Java ### 48 | # Compiled class file 49 | *.class 50 | 51 | 52 | # Log file 53 | *.log 54 | 55 | # Package Files # 56 | *.jar 57 | *.war 58 | *.nar 59 | *.ear 60 | *.zip 61 | *.tar.gz 62 | *.rar 63 | 64 | ### macOS ### 65 | # General 66 | .DS_Store 67 | .AppleDouble 68 | .LSOverride 69 | 70 | -------------------------------------------------------------------------------- /check-service/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /check-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimyuuum/simultaneous-viewing-limit-system/ce2be6f253633a1c47149e3ede0e73562743d923/check-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /check-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /check-service/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ]; then 38 | 39 | if [ -f /etc/mavenrc ]; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ]; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false 51 | darwin=false 52 | mingw=false 53 | case "$(uname)" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true ;; 56 | Darwin*) 57 | darwin=true 58 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 59 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 60 | if [ -z "$JAVA_HOME" ]; then 61 | if [ -x "/usr/libexec/java_home" ]; then 62 | export JAVA_HOME="$(/usr/libexec/java_home)" 63 | else 64 | export JAVA_HOME="/Library/Java/Home" 65 | fi 66 | fi 67 | ;; 68 | esac 69 | 70 | if [ -z "$JAVA_HOME" ]; then 71 | if [ -r /etc/gentoo-release ]; then 72 | JAVA_HOME=$(java-config --jre-home) 73 | fi 74 | fi 75 | 76 | if [ -z "$M2_HOME" ]; then 77 | ## resolve links - $0 may be a link to maven's home 78 | PRG="$0" 79 | 80 | # need this for relative symlinks 81 | while [ -h "$PRG" ]; do 82 | ls=$(ls -ld "$PRG") 83 | link=$(expr "$ls" : '.*-> \(.*\)$') 84 | if expr "$link" : '/.*' >/dev/null; then 85 | PRG="$link" 86 | else 87 | PRG="$(dirname "$PRG")/$link" 88 | fi 89 | done 90 | 91 | saveddir=$(pwd) 92 | 93 | M2_HOME=$(dirname "$PRG")/.. 94 | 95 | # make it fully qualified 96 | M2_HOME=$(cd "$M2_HOME" && pwd) 97 | 98 | cd "$saveddir" 99 | # echo Using m2 at $M2_HOME 100 | fi 101 | 102 | # For Cygwin, ensure paths are in UNIX format before anything is touched 103 | if $cygwin; then 104 | [ -n "$M2_HOME" ] && 105 | M2_HOME=$(cygpath --unix "$M2_HOME") 106 | [ -n "$JAVA_HOME" ] && 107 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 108 | [ -n "$CLASSPATH" ] && 109 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 110 | fi 111 | 112 | # For Mingw, ensure paths are in UNIX format before anything is touched 113 | if $mingw; then 114 | [ -n "$M2_HOME" ] && 115 | M2_HOME="$( ( 116 | cd "$M2_HOME" 117 | pwd 118 | ))" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="$( ( 121 | cd "$JAVA_HOME" 122 | pwd 123 | ))" 124 | fi 125 | 126 | if [ -z "$JAVA_HOME" ]; then 127 | javaExecutable="$(which javac)" 128 | if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then 129 | # readlink(1) is not available as standard on Solaris 10. 130 | readLink=$(which readlink) 131 | if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then 132 | if $darwin; then 133 | javaHome="$(dirname \"$javaExecutable\")" 134 | javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac" 135 | else 136 | javaExecutable="$(readlink -f \"$javaExecutable\")" 137 | fi 138 | javaHome="$(dirname \"$javaExecutable\")" 139 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 140 | JAVA_HOME="$javaHome" 141 | export JAVA_HOME 142 | fi 143 | fi 144 | fi 145 | 146 | if [ -z "$JAVACMD" ]; then 147 | if [ -n "$JAVA_HOME" ]; then 148 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 149 | # IBM's JDK on AIX uses strange locations for the executables 150 | JAVACMD="$JAVA_HOME/jre/sh/java" 151 | else 152 | JAVACMD="$JAVA_HOME/bin/java" 153 | fi 154 | else 155 | JAVACMD="$(which java)" 156 | fi 157 | fi 158 | 159 | if [ ! -x "$JAVACMD" ]; then 160 | echo "Error: JAVA_HOME is not defined correctly." >&2 161 | echo " We cannot execute $JAVACMD" >&2 162 | exit 1 163 | fi 164 | 165 | if [ -z "$JAVA_HOME" ]; then 166 | echo "Warning: JAVA_HOME environment variable is not set." 167 | fi 168 | 169 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 170 | 171 | # traverses directory structure from process work directory to filesystem root 172 | # first directory with .mvn subdirectory is considered project base directory 173 | find_maven_basedir() { 174 | 175 | if [ -z "$1" ]; then 176 | echo "Path not specified to find_maven_basedir" 177 | return 1 178 | fi 179 | 180 | basedir="$1" 181 | wdir="$1" 182 | while [ "$wdir" != '/' ]; do 183 | if [ -d "$wdir"/.mvn ]; then 184 | basedir=$wdir 185 | break 186 | fi 187 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 188 | if [ -d "${wdir}" ]; then 189 | wdir=$( 190 | cd "$wdir/.." 191 | pwd 192 | ) 193 | fi 194 | # end of workaround 195 | done 196 | echo "${basedir}" 197 | } 198 | 199 | # concatenates all lines of a file 200 | concat_lines() { 201 | if [ -f "$1" ]; then 202 | echo "$(tr -s '\n' ' ' <"$1")" 203 | fi 204 | } 205 | 206 | BASE_DIR=$(find_maven_basedir "$(pwd)") 207 | if [ -z "$BASE_DIR" ]; then 208 | exit 1 209 | fi 210 | 211 | ########################################################################################## 212 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 213 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 214 | ########################################################################################## 215 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 216 | if [ "$MVNW_VERBOSE" = true ]; then 217 | echo "Found .mvn/wrapper/maven-wrapper.jar" 218 | fi 219 | else 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 222 | fi 223 | if [ -n "$MVNW_REPOURL" ]; then 224 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 225 | else 226 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 227 | fi 228 | while IFS="=" read key value; do 229 | case "$key" in wrapperUrl) 230 | jarUrl="$value" 231 | break 232 | ;; 233 | esac 234 | done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 235 | if [ "$MVNW_VERBOSE" = true ]; then 236 | echo "Downloading from: $jarUrl" 237 | fi 238 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 239 | if $cygwin; then 240 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 241 | fi 242 | 243 | if command -v wget >/dev/null; then 244 | if [ "$MVNW_VERBOSE" = true ]; then 245 | echo "Found wget ... using wget" 246 | fi 247 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 248 | wget "$jarUrl" -O "$wrapperJarPath" 249 | else 250 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 251 | fi 252 | elif command -v curl >/dev/null; then 253 | if [ "$MVNW_VERBOSE" = true ]; then 254 | echo "Found curl ... using curl" 255 | fi 256 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 257 | curl -o "$wrapperJarPath" "$jarUrl" -f 258 | else 259 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 260 | fi 261 | 262 | else 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo "Falling back to using Java to download" 265 | fi 266 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 267 | # For Cygwin, switch paths to Windows format before running javac 268 | if $cygwin; then 269 | javaClass=$(cygpath --path --windows "$javaClass") 270 | fi 271 | if [ -e "$javaClass" ]; then 272 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Compiling MavenWrapperDownloader.java ..." 275 | fi 276 | # Compiling the Java class 277 | ("$JAVA_HOME/bin/javac" "$javaClass") 278 | fi 279 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 280 | # Running the downloader 281 | if [ "$MVNW_VERBOSE" = true ]; then 282 | echo " - Running MavenWrapperDownloader.java ..." 283 | fi 284 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 285 | fi 286 | fi 287 | fi 288 | fi 289 | ########################################################################################## 290 | # End of extension 291 | ########################################################################################## 292 | 293 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 294 | if [ "$MVNW_VERBOSE" = true ]; then 295 | echo $MAVEN_PROJECTBASEDIR 296 | fi 297 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 298 | 299 | # For Cygwin, switch paths to Windows format before running java 300 | if $cygwin; then 301 | [ -n "$M2_HOME" ] && 302 | M2_HOME=$(cygpath --path --windows "$M2_HOME") 303 | [ -n "$JAVA_HOME" ] && 304 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 305 | [ -n "$CLASSPATH" ] && 306 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 307 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 308 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 309 | fi 310 | 311 | # Provide a "standardized" way to retrieve the CLI args that will 312 | # work with both Windows and non-Windows executions. 313 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 314 | export MAVEN_CMD_LINE_ARGS 315 | 316 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 317 | 318 | exec "$JAVACMD" \ 319 | $MAVEN_OPTS \ 320 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 321 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 322 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 323 | -------------------------------------------------------------------------------- /check-service/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /check-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.2.RELEASE 9 | 10 | 11 | com.skb 12 | check-service 13 | 0.0.1-SNAPSHOT 14 | check-service 15 | check service with redis cluster 16 | 17 | 18 | 1.8 19 | 3.1.0 20 | 5.1.38 21 | 2.2.0.RELEASE 22 | 1.18.12 23 | 5.4.10.Final 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-data-redis 30 | 31 | 32 | redis.clients 33 | jedis 34 | ${jedis.version} 35 | jar 36 | 37 | 38 | org.springframework.kafka 39 | spring-kafka 40 | 41 | 42 | org.springframework.kafka 43 | spring-kafka-test 44 | test 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-web 49 | 50 | 51 | mysql 52 | mysql-connector-java 53 | ${mysql.version} 54 | 55 | 56 | org.springframework.data 57 | spring-data-jpa 58 | ${jpa.version} 59 | 60 | 61 | org.projectlombok 62 | lombok 63 | ${lombok.version} 64 | provided 65 | 66 | 67 | org.hibernate 68 | hibernate-core 69 | ${hibernate.version} 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-configuration-processor 74 | true 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-test 79 | test 80 | 81 | 82 | org.junit.vintage 83 | junit-vintage-engine 84 | 85 | 86 | 87 | 88 | com.googlecode.json-simple 89 | json-simple 90 | 1.1.1 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 100 | 101 | org.jacoco 102 | jacoco-maven-plugin 103 | 0.8.4 104 | 105 | 106 | 107 | prepare-agent 108 | 109 | 110 | 111 | report 112 | prepare-package 113 | 114 | report 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/CheckServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CheckServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CheckServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/config/kafka/KafkaConsumerConfig.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.config.kafka; 2 | 3 | import com.skb.checkservice.domain.WatchInfo.WatchInfoDto; 4 | import org.apache.kafka.clients.consumer.ConsumerConfig; 5 | import org.apache.kafka.common.serialization.StringDeserializer; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.kafka.annotation.EnableKafka; 10 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 11 | import org.springframework.kafka.config.KafkaListenerContainerFactory; 12 | import org.springframework.kafka.core.ConsumerFactory; 13 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 14 | import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; 15 | import org.springframework.kafka.support.serializer.JsonDeserializer; 16 | 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | @Configuration 21 | @EnableKafka 22 | public class KafkaConsumerConfig { 23 | 24 | @Value(value = "${spring.kafka.bootstrap-servers}") 25 | private String bootStrapServers; 26 | 27 | @Bean 28 | Map consumerConfigs() { 29 | Map props = new HashMap<>(); 30 | props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootStrapServers); 31 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 32 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); 33 | return props; 34 | } 35 | 36 | @Bean 37 | public ConsumerFactory consumerFactory() { 38 | Map props = consumerConfigs(); 39 | props.put(ConsumerConfig.GROUP_ID_CONFIG, "watch-info-group"); 40 | return new DefaultKafkaConsumerFactory<>(props, 41 | new StringDeserializer(), 42 | new JsonDeserializer<>(WatchInfoDto.Request.class, false)); 43 | } 44 | 45 | @Bean 46 | public KafkaListenerContainerFactory> newKafkaListenerContainerFactory() { 47 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 48 | factory.setConsumerFactory(consumerFactory()); 49 | return factory; 50 | } 51 | 52 | @Bean 53 | public ConsumerFactory forceConsumerFactory() { 54 | Map props = consumerConfigs(); 55 | props.put(ConsumerConfig.GROUP_ID_CONFIG, "force-info-group"); 56 | return new DefaultKafkaConsumerFactory<>(props, 57 | new StringDeserializer(), 58 | new JsonDeserializer<>(WatchInfoDto.Request.class, false)); 59 | } 60 | 61 | @Bean 62 | public KafkaListenerContainerFactory> forceKafkaListenerContainerFactory() { 63 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 64 | factory.setConsumerFactory(forceConsumerFactory()); 65 | return factory; 66 | } 67 | 68 | @Bean 69 | public ConsumerFactory disconnectConsumerFactory() { 70 | Map props = consumerConfigs(); 71 | props.put(ConsumerConfig.GROUP_ID_CONFIG, "disconnect-info-group"); 72 | return new DefaultKafkaConsumerFactory<>(props, 73 | new StringDeserializer(), 74 | new JsonDeserializer<>(WatchInfoDto.Request.class, false)); 75 | } 76 | 77 | @Bean 78 | public KafkaListenerContainerFactory> disconnectKafkaListenerContainerFactory() { 79 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 80 | factory.setConsumerFactory(disconnectConsumerFactory()); 81 | return factory; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/config/kafka/KafkaProducerConfig.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.config.kafka; 2 | 3 | 4 | import com.skb.checkservice.domain.User.UserDto; 5 | import org.apache.kafka.clients.producer.ProducerConfig; 6 | import org.apache.kafka.common.serialization.StringSerializer; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.autoconfigure.kafka.KafkaProperties; 9 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.kafka.annotation.EnableKafka; 13 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 14 | import org.springframework.kafka.core.KafkaTemplate; 15 | import org.springframework.kafka.core.ProducerFactory; 16 | import org.springframework.kafka.support.serializer.JsonSerializer; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | @Configuration 22 | @EnableKafka 23 | @EnableConfigurationProperties(KafkaProperties.class) 24 | public class KafkaProducerConfig { 25 | 26 | @Value(value = "${spring.kafka.bootstrap-servers}") 27 | private String bootStrapServers; 28 | 29 | @Bean 30 | public Map stringProducerConfigs(){ 31 | Map props = new HashMap<>(); 32 | props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootStrapServers); 33 | props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 34 | props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 35 | 36 | return props; 37 | } 38 | 39 | @Bean 40 | public Map dtoProducerConfigs(){ 41 | Map props = new HashMap<>(); 42 | props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootStrapServers); 43 | props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class); 44 | props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,JsonSerializer.class); 45 | 46 | return props; 47 | } 48 | 49 | @Bean 50 | public ProducerFactory stringProducerFactory(){ 51 | return new DefaultKafkaProducerFactory<>(stringProducerConfigs()); 52 | } 53 | 54 | @Bean 55 | public ProducerFactory dtoProducerFactory(){ 56 | return new DefaultKafkaProducerFactory<>(dtoProducerConfigs()); 57 | } 58 | 59 | @Bean 60 | public KafkaTemplate stringKafkaTemplate(){ 61 | return new KafkaTemplate<>(stringProducerFactory()); 62 | } 63 | 64 | @Bean 65 | public KafkaTemplate dtoKafkaTemplate(){ return new KafkaTemplate<>(dtoProducerFactory());} 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/config/redis/ClusterConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.config.redis; 2 | 3 | 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.List; 8 | 9 | @Component 10 | @ConfigurationProperties(prefix = "spring.redis.cluster") 11 | public class ClusterConfigurationProperties { //spring boot가 설정 파일에서 읽어온 값을 관리할 객체를 나타냄 12 | 13 | List nodes; 14 | 15 | public List getNodes(){ 16 | return nodes; 17 | } 18 | 19 | public void setNodes(List nodes){ 20 | this.nodes = nodes; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/config/redis/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.config.redis; 2 | 3 | import com.skb.checkservice.domain.WatchInfo.WatchInfo; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.data.redis.connection.RedisClusterConfiguration; 7 | import org.springframework.data.redis.connection.RedisConnectionFactory; 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 9 | import org.springframework.data.redis.core.RedisTemplate; 10 | import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; 11 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 12 | import org.springframework.data.redis.serializer.StringRedisSerializer; 13 | 14 | @Configuration(proxyBeanMethods = true) 15 | @EnableRedisRepositories 16 | class RedisConfig { 17 | 18 | private final ClusterConfigurationProperties clusterConfigurationProperties; 19 | 20 | public RedisConfig(ClusterConfigurationProperties clusterConfigurationProperties) { 21 | this.clusterConfigurationProperties = clusterConfigurationProperties; 22 | } 23 | 24 | @Bean 25 | public RedisConnectionFactory redisConnectionFactory() { 26 | RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); 27 | 28 | clusterConfigurationProperties.getNodes() 29 | .forEach(s -> { 30 | //parse host and port 31 | String[] url = s.split(":"); 32 | redisClusterConfiguration.clusterNode(url[0], Integer.parseInt(url[1])); 33 | }); 34 | 35 | return new JedisConnectionFactory(redisClusterConfiguration); 36 | } 37 | 38 | @Bean 39 | public RedisTemplate redisTemplate() { 40 | RedisTemplate redisTemplate = new RedisTemplate<>(); 41 | 42 | redisTemplate.setConnectionFactory(this.redisConnectionFactory()); 43 | 44 | //if default serializer == JdkSerializationRedisSerializer -> unicode error occur 45 | //new Jackson2JsonRedisSerializer<>() -> json 포맷으로 데이터를 저장하는 경우 46 | 47 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 48 | redisTemplate.setValueSerializer(new StringRedisSerializer()); 49 | 50 | redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 51 | redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(WatchInfo.class)); 52 | 53 | return redisTemplate; 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/controller/ConnectController.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.controller; 2 | 3 | import com.skb.checkservice.domain.WatchInfo.WatchInfo; 4 | import com.skb.checkservice.domain.WatchInfo.WatchInfoDto; 5 | import com.skb.checkservice.service.CheckViewingService; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class ConnectController { 12 | 13 | private final CheckViewingService checkViewingService; 14 | 15 | public ConnectController(CheckViewingService checkViewingService) { 16 | this.checkViewingService = checkViewingService; 17 | } 18 | 19 | @PostMapping("/connect") 20 | public boolean connectUser(@RequestBody WatchInfoDto.Request request) { 21 | return checkViewingService.checkLog(request); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/domain/User/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.domain.User; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 5 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | 9 | 10 | public class UserDto { 11 | 12 | @Getter 13 | @Builder 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 16 | public static class Response { 17 | 18 | private String newUser; 19 | 20 | private String existUser; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/domain/WatchInfo/WatchInfo.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.domain.WatchInfo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import org.springframework.data.redis.core.RedisHash; 10 | 11 | import java.io.Serializable; 12 | 13 | 14 | @RedisHash("watchLog") 15 | @Getter 16 | @Builder 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | public class WatchInfo implements Serializable { 21 | 22 | private String pcid; 23 | 24 | private String episodeId; 25 | 26 | private String stbId; 27 | 28 | private String playStart; 29 | 30 | private String macAddress; 31 | 32 | private String playEnd; 33 | 34 | private boolean running; 35 | 36 | } -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/domain/WatchInfo/WatchInfoDto.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.domain.WatchInfo; 2 | 3 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 4 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 5 | import lombok.Getter; 6 | 7 | 8 | public class WatchInfoDto { 9 | 10 | @Getter 11 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 12 | public static class Request { 13 | 14 | private String pcid; 15 | 16 | private String episodeId; 17 | 18 | private String stbId; 19 | 20 | private String macAddress; 21 | 22 | private String playStart; 23 | 24 | private String playEnd; 25 | 26 | private boolean running; 27 | 28 | public WatchInfo toEntity() { 29 | return WatchInfo.builder() 30 | .pcid(pcid) 31 | .episodeId(episodeId) 32 | .stbId(stbId) 33 | .macAddress(macAddress) 34 | .playStart(playStart) 35 | .playEnd(playEnd) 36 | .running(running) 37 | .build(); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/message/MessageListener.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.message; 2 | 3 | 4 | import com.skb.checkservice.domain.WatchInfo.WatchInfoDto; 5 | import com.skb.checkservice.service.CheckViewingService; 6 | import org.springframework.kafka.annotation.KafkaListener; 7 | import org.springframework.messaging.handler.annotation.Payload; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | public class MessageListener { 12 | 13 | private final CheckViewingService checkViewingService; 14 | 15 | public MessageListener(CheckViewingService checkViewingService) { 16 | this.checkViewingService = checkViewingService; 17 | } 18 | 19 | @KafkaListener(topics = "forceConnect", containerFactory = "forceKafkaListenerContainerFactory", groupId = "force-info-group") 20 | public void consumeForceConnectInfo(@Payload WatchInfoDto.Request payload) { 21 | checkViewingService.updateLog(payload); 22 | } 23 | 24 | @KafkaListener(topics = "disconnect", containerFactory = "disconnectKafkaListenerContainerFactory", groupId = "disconnect-info-group") 25 | public void consumeDisconnect(@Payload WatchInfoDto.Request payload) { 26 | checkViewingService.updateLog(payload); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/message/MessageSender.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.message; 2 | 3 | import com.skb.checkservice.domain.User.UserDto; 4 | import lombok.RequiredArgsConstructor; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.kafka.core.KafkaTemplate; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @RequiredArgsConstructor 12 | public class MessageSender { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(MessageSender.class); 15 | 16 | private final KafkaTemplate dtoKafkaTemplate; 17 | private final KafkaTemplate stringKafkaTemplate; 18 | 19 | public void sendMessage(String topic, UserDto.Response dto) { 20 | dtoKafkaTemplate.send(topic, dto); 21 | logger.info("produce user exists complete"); 22 | } 23 | 24 | public void sendMessage(String topic, String message){ 25 | stringKafkaTemplate.send(topic,message); 26 | logger.info("Produce success response : update user"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/service/CheckViewingService.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.service; 2 | 3 | import com.skb.checkservice.domain.User.UserDto; 4 | import com.skb.checkservice.domain.WatchInfo.WatchInfoDto; 5 | import com.skb.checkservice.message.MessageSender; 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class CheckViewingService { 10 | 11 | private final RedisClusterService redisClusterService; 12 | private final MessageSender messageSender; 13 | 14 | private static final String existTopic = "userExists"; 15 | 16 | public CheckViewingService(RedisClusterService redisClusterService, MessageSender messageSender) { 17 | this.redisClusterService = redisClusterService; 18 | this.messageSender = messageSender; 19 | } 20 | 21 | public boolean checkLog(WatchInfoDto.Request request) { 22 | 23 | boolean isCreated = false; 24 | UserDto.Response response; 25 | 26 | boolean isRunning = redisClusterService.checkIsRunning(request.getStbId(), request.getEpisodeId()); 27 | if (isRunning) { 28 | String existUser = redisClusterService.getExistUser(request.getStbId(), request.getEpisodeId()); 29 | 30 | response = UserDto.Response.builder() 31 | .newUser(request.getPcid()) 32 | .existUser(existUser) 33 | .build(); 34 | 35 | messageSender.sendMessage(existTopic, response); 36 | } else { 37 | redisClusterService.create(request); 38 | isCreated = true; 39 | } 40 | 41 | return isCreated; 42 | 43 | } 44 | 45 | public void updateLog(WatchInfoDto.Request dto) { 46 | redisClusterService.update(dto); 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /check-service/src/main/java/com/skb/checkservice/service/RedisClusterService.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice.service; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.skb.checkservice.domain.WatchInfo.WatchInfo; 5 | import com.skb.checkservice.domain.WatchInfo.WatchInfoDto; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | public class RedisClusterService { 12 | 13 | @Autowired 14 | ObjectMapper objectMapper; 15 | 16 | private final RedisTemplate redisTemplate; 17 | 18 | public RedisClusterService(RedisTemplate redisTemplate) { 19 | this.redisTemplate = redisTemplate; 20 | } 21 | 22 | public void create(WatchInfoDto.Request dto) { 23 | redisTemplate.opsForHash() 24 | .put(dto.getStbId(), dto.getEpisodeId(), dto.toEntity()); 25 | } 26 | 27 | public void update(WatchInfoDto.Request dto) { 28 | //Update redis Data 29 | redisTemplate.opsForHash() 30 | .delete(dto.getStbId(), dto.getEpisodeId()); 31 | create(dto); 32 | } 33 | 34 | public boolean checkIsRunning(String stbId, String episodeId) { 35 | //if VOD Log presents check is running 36 | if (redisTemplate.opsForHash() 37 | .hasKey(stbId, episodeId)) { 38 | WatchInfo user = objectMapper.convertValue(redisTemplate.opsForHash() 39 | .get(stbId, episodeId), WatchInfo.class); 40 | return user.isRunning(); 41 | } else { 42 | return false; 43 | } 44 | } 45 | 46 | public String getExistUser(String stbId, String episodeId) { 47 | WatchInfo existUser = objectMapper.convertValue(redisTemplate.opsForHash() 48 | .get(stbId, episodeId), WatchInfo.class); 49 | return existUser.getPcid(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /check-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9000 3 | spring: 4 | jackson: 5 | serialization: 6 | WRITE_DATES_AS_TIMESTAMPS: false 7 | application: 8 | name: check-service 9 | jpa: 10 | database-platform: org.hibernate.dialect.MYSQL5Dialect 11 | open-in-view: false 12 | show-sql: false 13 | properties: 14 | hibernate: 15 | generate_statistics: false 16 | dialect: org.hibernate.dialect.MySQL5Dialect 17 | hibernate: 18 | naming: 19 | physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 20 | redis: 21 | cluster: 22 | nodes: 23 | - 127.0.0.1:7000 24 | - 127.0.0.1:7001 25 | - 127.0.0.1:7002 26 | - 127.0.0.1:7003 27 | - 127.0.0.1:7004 28 | - 127.0.0.1:7005 29 | kafka: 30 | consumer: 31 | auto-offset-reset: earliest 32 | bootstrap-servers: localhost:9093,localhost:9094,localhost:9095 -------------------------------------------------------------------------------- /check-service/src/test/java/com/skb/checkservice/CheckViewingServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.skb.checkservice.domain.WatchInfo.WatchInfoDto; 5 | import com.skb.checkservice.service.CheckViewingService; 6 | import org.json.simple.JSONObject; 7 | import org.json.simple.parser.JSONParser; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import java.io.File; 13 | import java.io.FileReader; 14 | 15 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 16 | public class CheckViewingServiceTest { 17 | 18 | @Autowired 19 | private CheckViewingService checkViewingService; 20 | 21 | public WatchInfoDto.Request createDtoFromJson() { 22 | 23 | File file = new File("/Users/kim-yumin/project/check-service/watch-info.json"); 24 | JSONParser jsonParser = new JSONParser(); 25 | WatchInfoDto.Request result = null; 26 | try { 27 | JSONObject jsonObject = (JSONObject) jsonParser.parse(new FileReader(file)); 28 | ObjectMapper objectMapper = new ObjectMapper(); 29 | result = objectMapper.readValue(jsonObject.toString(), WatchInfoDto.Request.class); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | } 33 | 34 | return result; 35 | } 36 | 37 | @Test 38 | public void 시청이력_확인() { 39 | 40 | //given 41 | WatchInfoDto.Request dto = createDtoFromJson(); 42 | 43 | //when 44 | checkViewingService.checkLog(dto); 45 | 46 | } 47 | 48 | @Test 49 | public void 시청이력_업데이트() { 50 | 51 | //given 52 | WatchInfoDto.Request dto = createDtoFromJson(); 53 | 54 | //when 55 | checkViewingService.updateLog(dto); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /check-service/src/test/java/com/skb/checkservice/RedisClusterServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.skb.checkservice; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.skb.checkservice.domain.WatchInfo.WatchInfoDto; 5 | import com.skb.checkservice.service.RedisClusterService; 6 | import org.assertj.core.api.Assertions; 7 | import org.json.simple.JSONObject; 8 | import org.json.simple.parser.JSONParser; 9 | import org.junit.Before; 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | 14 | import java.io.File; 15 | import java.io.FileReader; 16 | 17 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 18 | public class RedisClusterServiceTest { 19 | 20 | @Autowired 21 | private RedisClusterService redisClusterService; 22 | 23 | public WatchInfoDto.Request createDtoFromJson() { 24 | 25 | File file = new File("/Users/kim-yumin/project/check-service/watch-info.json"); 26 | JSONParser jsonParser = new JSONParser(); 27 | WatchInfoDto.Request result = null; 28 | try { 29 | JSONObject jsonObject = (JSONObject) jsonParser.parse(new FileReader(file)); 30 | ObjectMapper objectMapper = new ObjectMapper(); 31 | result = objectMapper.readValue(jsonObject.toString(), WatchInfoDto.Request.class); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | 36 | return result; 37 | } 38 | 39 | private WatchInfoDto.Request createDto(String stb_id, String episode_id, boolean running) { 40 | JSONObject jsonObject = new JSONObject(); 41 | jsonObject.put("stb_id", stb_id); 42 | jsonObject.put("episode_id", episode_id); 43 | jsonObject.put("mac_address", "mac_address2"); 44 | jsonObject.put("play_start", "2020-08-22'T'21:32:00"); 45 | jsonObject.put("pcid", "UserA"); 46 | jsonObject.put("running", running); 47 | 48 | WatchInfoDto.Request dto = null; 49 | try { 50 | ObjectMapper objectMapper = new ObjectMapper(); 51 | dto = objectMapper.readValue(jsonObject.toString(), WatchInfoDto.Request.class); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | 56 | return dto; 57 | } 58 | 59 | @Before 60 | public void WatchInfo_생성() { 61 | //given 62 | WatchInfoDto.Request dto = createDtoFromJson(); 63 | 64 | //when 65 | redisClusterService.create(dto); 66 | } 67 | 68 | @Test 69 | public void WatchInfo_수정() { 70 | //given 71 | WatchInfoDto.Request dto = createDtoFromJson(); 72 | 73 | //when 74 | redisClusterService.update(dto); 75 | } 76 | 77 | @Test 78 | public void 시청여부확인_true() { 79 | //given 80 | String stbId = "stb_id2"; 81 | String episodeId = "episode_id2"; 82 | WatchInfoDto.Request dto = createDto(stbId, episodeId, true); 83 | 84 | //when 85 | Assertions.assertThat(dto) 86 | .isNotNull(); 87 | redisClusterService.create(dto); 88 | 89 | boolean result = redisClusterService.checkIsRunning(dto.getStbId(), dto.getEpisodeId()); 90 | 91 | //then 92 | Assertions.assertThat(result) 93 | .isEqualTo(true); 94 | } 95 | 96 | @Test 97 | public void 시청여부확인_false() { 98 | //given 99 | String stbId = "stb_id3"; 100 | String episodeId = "episode_id3"; 101 | WatchInfoDto.Request dto = createDto(stbId, episodeId, false); 102 | 103 | //when 104 | Assertions.assertThat(dto) 105 | .isNotNull(); 106 | redisClusterService.create(dto); 107 | 108 | boolean result = redisClusterService.checkIsRunning(dto.getStbId(), dto.getEpisodeId()); 109 | 110 | //then 111 | Assertions.assertThat(result) 112 | .isEqualTo(false); 113 | } 114 | 115 | @Test 116 | public void 기존시청자_확인() { 117 | //given 118 | String stbId = "stb_id4"; 119 | String episodeId = "episode_id4"; 120 | WatchInfoDto.Request dto = createDto(stbId, episodeId, true); 121 | String existUser = dto.getPcid(); 122 | 123 | //when 124 | Assertions.assertThat(dto) 125 | .isNotNull(); 126 | redisClusterService.create(dto); 127 | String result = redisClusterService.getExistUser(stbId, episodeId); 128 | 129 | //then 130 | Assertions.assertThat(result) 131 | .isEqualTo(existUser); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /check-service/watch-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "pcid": "pcid", 3 | "episode_id" : "episode_id", 4 | "stb_id" : "stb_id", 5 | "mac_address" : "mac_address", 6 | "play_start" : "2020-08-22'T'19:55:00", 7 | "running" : true 8 | } -------------------------------------------------------------------------------- /push-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | !**/src/main/**/target/ 3 | !**/src/test/**/target/ 4 | 5 | ## Maven: 6 | !.mvn/wrapper/maven-wrapper.jar 7 | /.mvn/ 8 | /mvnw 9 | /mvnw.bat 10 | mvnw.cmd 11 | /log/ 12 | *.log* 13 | target/ 14 | pom.xml.tag 15 | pom.xml.releaseBackup 16 | pom.xml.next 17 | release.properties 18 | 19 | ### STS ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | 28 | ### IntelliJ IDEA ### 29 | .idea 30 | *.iws 31 | *.iml 32 | *.ipr 33 | 34 | ### NetBeans ### 35 | /nbproject/private/ 36 | /nbbuild/ 37 | /dist/ 38 | /nbdist/ 39 | /.nb-gradle/ 40 | build/ 41 | !**/src/main/**/build/ 42 | !**/src/test/**/build/ 43 | 44 | ### VS Code ### 45 | .vscode/ 46 | 47 | ### Java ### 48 | # Compiled class file 49 | *.class 50 | 51 | 52 | # Log file 53 | *.log 54 | 55 | # Package Files # 56 | *.jar 57 | *.war 58 | *.nar 59 | *.ear 60 | *.zip 61 | *.tar.gz 62 | *.rar 63 | 64 | ### macOS ### 65 | # General 66 | .DS_Store 67 | .AppleDouble 68 | .LSOverride 69 | 70 | -------------------------------------------------------------------------------- /push-service/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /push-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimyuuum/simultaneous-viewing-limit-system/ce2be6f253633a1c47149e3ede0e73562743d923/push-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /push-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /push-service/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ]; then 38 | 39 | if [ -f /etc/mavenrc ]; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ]; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false 51 | darwin=false 52 | mingw=false 53 | case "$(uname)" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true ;; 56 | Darwin*) 57 | darwin=true 58 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 59 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 60 | if [ -z "$JAVA_HOME" ]; then 61 | if [ -x "/usr/libexec/java_home" ]; then 62 | export JAVA_HOME="$(/usr/libexec/java_home)" 63 | else 64 | export JAVA_HOME="/Library/Java/Home" 65 | fi 66 | fi 67 | ;; 68 | esac 69 | 70 | if [ -z "$JAVA_HOME" ]; then 71 | if [ -r /etc/gentoo-release ]; then 72 | JAVA_HOME=$(java-config --jre-home) 73 | fi 74 | fi 75 | 76 | if [ -z "$M2_HOME" ]; then 77 | ## resolve links - $0 may be a link to maven's home 78 | PRG="$0" 79 | 80 | # need this for relative symlinks 81 | while [ -h "$PRG" ]; do 82 | ls=$(ls -ld "$PRG") 83 | link=$(expr "$ls" : '.*-> \(.*\)$') 84 | if expr "$link" : '/.*' >/dev/null; then 85 | PRG="$link" 86 | else 87 | PRG="$(dirname "$PRG")/$link" 88 | fi 89 | done 90 | 91 | saveddir=$(pwd) 92 | 93 | M2_HOME=$(dirname "$PRG")/.. 94 | 95 | # make it fully qualified 96 | M2_HOME=$(cd "$M2_HOME" && pwd) 97 | 98 | cd "$saveddir" 99 | # echo Using m2 at $M2_HOME 100 | fi 101 | 102 | # For Cygwin, ensure paths are in UNIX format before anything is touched 103 | if $cygwin; then 104 | [ -n "$M2_HOME" ] && 105 | M2_HOME=$(cygpath --unix "$M2_HOME") 106 | [ -n "$JAVA_HOME" ] && 107 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 108 | [ -n "$CLASSPATH" ] && 109 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 110 | fi 111 | 112 | # For Mingw, ensure paths are in UNIX format before anything is touched 113 | if $mingw; then 114 | [ -n "$M2_HOME" ] && 115 | M2_HOME="$( ( 116 | cd "$M2_HOME" 117 | pwd 118 | ))" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="$( ( 121 | cd "$JAVA_HOME" 122 | pwd 123 | ))" 124 | fi 125 | 126 | if [ -z "$JAVA_HOME" ]; then 127 | javaExecutable="$(which javac)" 128 | if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then 129 | # readlink(1) is not available as standard on Solaris 10. 130 | readLink=$(which readlink) 131 | if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then 132 | if $darwin; then 133 | javaHome="$(dirname \"$javaExecutable\")" 134 | javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac" 135 | else 136 | javaExecutable="$(readlink -f \"$javaExecutable\")" 137 | fi 138 | javaHome="$(dirname \"$javaExecutable\")" 139 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 140 | JAVA_HOME="$javaHome" 141 | export JAVA_HOME 142 | fi 143 | fi 144 | fi 145 | 146 | if [ -z "$JAVACMD" ]; then 147 | if [ -n "$JAVA_HOME" ]; then 148 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 149 | # IBM's JDK on AIX uses strange locations for the executables 150 | JAVACMD="$JAVA_HOME/jre/sh/java" 151 | else 152 | JAVACMD="$JAVA_HOME/bin/java" 153 | fi 154 | else 155 | JAVACMD="$(which java)" 156 | fi 157 | fi 158 | 159 | if [ ! -x "$JAVACMD" ]; then 160 | echo "Error: JAVA_HOME is not defined correctly." >&2 161 | echo " We cannot execute $JAVACMD" >&2 162 | exit 1 163 | fi 164 | 165 | if [ -z "$JAVA_HOME" ]; then 166 | echo "Warning: JAVA_HOME environment variable is not set." 167 | fi 168 | 169 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 170 | 171 | # traverses directory structure from process work directory to filesystem root 172 | # first directory with .mvn subdirectory is considered project base directory 173 | find_maven_basedir() { 174 | 175 | if [ -z "$1" ]; then 176 | echo "Path not specified to find_maven_basedir" 177 | return 1 178 | fi 179 | 180 | basedir="$1" 181 | wdir="$1" 182 | while [ "$wdir" != '/' ]; do 183 | if [ -d "$wdir"/.mvn ]; then 184 | basedir=$wdir 185 | break 186 | fi 187 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 188 | if [ -d "${wdir}" ]; then 189 | wdir=$( 190 | cd "$wdir/.." 191 | pwd 192 | ) 193 | fi 194 | # end of workaround 195 | done 196 | echo "${basedir}" 197 | } 198 | 199 | # concatenates all lines of a file 200 | concat_lines() { 201 | if [ -f "$1" ]; then 202 | echo "$(tr -s '\n' ' ' <"$1")" 203 | fi 204 | } 205 | 206 | BASE_DIR=$(find_maven_basedir "$(pwd)") 207 | if [ -z "$BASE_DIR" ]; then 208 | exit 1 209 | fi 210 | 211 | ########################################################################################## 212 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 213 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 214 | ########################################################################################## 215 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 216 | if [ "$MVNW_VERBOSE" = true ]; then 217 | echo "Found .mvn/wrapper/maven-wrapper.jar" 218 | fi 219 | else 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 222 | fi 223 | if [ -n "$MVNW_REPOURL" ]; then 224 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 225 | else 226 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 227 | fi 228 | while IFS="=" read key value; do 229 | case "$key" in wrapperUrl) 230 | jarUrl="$value" 231 | break 232 | ;; 233 | esac 234 | done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 235 | if [ "$MVNW_VERBOSE" = true ]; then 236 | echo "Downloading from: $jarUrl" 237 | fi 238 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 239 | if $cygwin; then 240 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 241 | fi 242 | 243 | if command -v wget >/dev/null; then 244 | if [ "$MVNW_VERBOSE" = true ]; then 245 | echo "Found wget ... using wget" 246 | fi 247 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 248 | wget "$jarUrl" -O "$wrapperJarPath" 249 | else 250 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 251 | fi 252 | elif command -v curl >/dev/null; then 253 | if [ "$MVNW_VERBOSE" = true ]; then 254 | echo "Found curl ... using curl" 255 | fi 256 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 257 | curl -o "$wrapperJarPath" "$jarUrl" -f 258 | else 259 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 260 | fi 261 | 262 | else 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo "Falling back to using Java to download" 265 | fi 266 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 267 | # For Cygwin, switch paths to Windows format before running javac 268 | if $cygwin; then 269 | javaClass=$(cygpath --path --windows "$javaClass") 270 | fi 271 | if [ -e "$javaClass" ]; then 272 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Compiling MavenWrapperDownloader.java ..." 275 | fi 276 | # Compiling the Java class 277 | ("$JAVA_HOME/bin/javac" "$javaClass") 278 | fi 279 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 280 | # Running the downloader 281 | if [ "$MVNW_VERBOSE" = true ]; then 282 | echo " - Running MavenWrapperDownloader.java ..." 283 | fi 284 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 285 | fi 286 | fi 287 | fi 288 | fi 289 | ########################################################################################## 290 | # End of extension 291 | ########################################################################################## 292 | 293 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 294 | if [ "$MVNW_VERBOSE" = true ]; then 295 | echo $MAVEN_PROJECTBASEDIR 296 | fi 297 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 298 | 299 | # For Cygwin, switch paths to Windows format before running java 300 | if $cygwin; then 301 | [ -n "$M2_HOME" ] && 302 | M2_HOME=$(cygpath --path --windows "$M2_HOME") 303 | [ -n "$JAVA_HOME" ] && 304 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 305 | [ -n "$CLASSPATH" ] && 306 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 307 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 308 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 309 | fi 310 | 311 | # Provide a "standardized" way to retrieve the CLI args that will 312 | # work with both Windows and non-Windows executions. 313 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 314 | export MAVEN_CMD_LINE_ARGS 315 | 316 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 317 | 318 | exec "$JAVACMD" \ 319 | $MAVEN_OPTS \ 320 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 321 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 322 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 323 | -------------------------------------------------------------------------------- /push-service/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /push-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.3.RELEASE 9 | 10 | 11 | com.skb 12 | push-service 13 | 0.0.1-SNAPSHOT 14 | push-service 15 | push-service with websocket 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-thymeleaf 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-security 33 | 34 | 35 | org.springframework.kafka 36 | spring-kafka 37 | 38 | 39 | 40 | org.projectlombok 41 | lombok 42 | true 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-websocket 47 | 48 | 49 | org.springframework 50 | spring-messaging 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-test 55 | test 56 | 57 | 58 | org.junit.vintage 59 | junit-vintage-engine 60 | 61 | 62 | 63 | 64 | org.springframework.kafka 65 | spring-kafka-test 66 | test 67 | 68 | 69 | com.fasterxml.jackson.core 70 | jackson-core 71 | 72 | 73 | 74 | com.fasterxml.jackson.core 75 | jackson-databind 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-maven-plugin 84 | 85 | 86 | org.jacoco 87 | jacoco-maven-plugin 88 | 0.8.4 89 | 90 | 91 | 92 | prepare-agent 93 | 94 | 95 | 96 | report 97 | prepare-package 98 | 99 | report 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/PushServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class PushServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(PushServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/config/MessageConsumerConfig.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.config; 2 | 3 | import com.skb.pushservice.domain.Exist.ExistDto; 4 | import org.apache.kafka.clients.consumer.ConsumerConfig; 5 | import org.apache.kafka.common.serialization.StringDeserializer; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.kafka.annotation.EnableKafka; 10 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 11 | import org.springframework.kafka.config.KafkaListenerContainerFactory; 12 | import org.springframework.kafka.core.ConsumerFactory; 13 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 14 | import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; 15 | import org.springframework.kafka.support.serializer.JsonDeserializer; 16 | 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | @Configuration 21 | @EnableKafka 22 | public class MessageConsumerConfig { 23 | 24 | @Value(value = "${spring.kafka.bootstrap-servers}") 25 | private String bootStrapServers; 26 | 27 | private static final String existGroup = "exist-info-group"; 28 | private static final String successResponse = "success-response-group"; 29 | 30 | @Bean 31 | Map consumerConfigs() { 32 | Map props = new HashMap<>(); 33 | props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootStrapServers); 34 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 35 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); 36 | props.put(ConsumerConfig.GROUP_ID_CONFIG, existGroup); 37 | return props; 38 | } 39 | 40 | @Bean 41 | Map successConsumerConfigs() { 42 | Map props = new HashMap<>(); 43 | props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootStrapServers); 44 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 45 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 46 | props.put(ConsumerConfig.GROUP_ID_CONFIG, successResponse); 47 | return props; 48 | } 49 | 50 | @Bean 51 | public ConsumerFactory existConsumerFactory() { 52 | return new DefaultKafkaConsumerFactory<>(consumerConfigs(), 53 | new StringDeserializer(), 54 | new JsonDeserializer<>(ExistDto.Response.class, false)); 55 | } 56 | 57 | @Bean 58 | public ConsumerFactory successConsumerFactory() { 59 | return new DefaultKafkaConsumerFactory<>(successConsumerConfigs(), new StringDeserializer(), new StringDeserializer()); 60 | } 61 | 62 | @Bean 63 | public KafkaListenerContainerFactory> kafkaListenerContainerFactory() { 64 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 65 | factory.setConsumerFactory(existConsumerFactory()); 66 | return factory; 67 | } 68 | 69 | @Bean 70 | public KafkaListenerContainerFactory> successKafkaListenerContainerFactory() { 71 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 72 | factory.setConsumerFactory(successConsumerFactory()); 73 | return factory; 74 | } 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/config/MessageProducerConfig.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.config; 2 | 3 | import com.skb.pushservice.domain.WatchInfo.WatchInfoDto; 4 | import org.apache.kafka.clients.producer.ProducerConfig; 5 | import org.apache.kafka.common.serialization.StringSerializer; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.boot.autoconfigure.kafka.KafkaProperties; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.kafka.annotation.EnableKafka; 12 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 13 | import org.springframework.kafka.core.KafkaTemplate; 14 | import org.springframework.kafka.core.ProducerFactory; 15 | import org.springframework.kafka.support.serializer.JsonSerializer; 16 | 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | @Configuration 21 | @EnableKafka 22 | @EnableConfigurationProperties(KafkaProperties.class) 23 | public class MessageProducerConfig { 24 | 25 | @Value(value = "${spring.kafka.bootstrap-servers}") 26 | private String bootStrapServers; 27 | 28 | @Bean 29 | public Map dtoProducerConfigs(){ 30 | Map props = new HashMap<>(); 31 | props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootStrapServers); 32 | props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 33 | props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); 34 | 35 | return props; 36 | } 37 | 38 | /* 39 | * TODO : FIX factory & template generic... 40 | */ 41 | 42 | @Bean 43 | public ProducerFactory watchInfoProducerFactory(){ 44 | return new DefaultKafkaProducerFactory<>(dtoProducerConfigs()); 45 | } 46 | 47 | @Bean 48 | public KafkaTemplate watchInfoKafkaTemplate(){ 49 | return new KafkaTemplate<>(watchInfoProducerFactory()); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/config/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.config; 2 | 3 | 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | 10 | @Configuration 11 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 12 | 13 | /** 14 | * Enable authentication with declared user : UserA, UserB 15 | * 16 | * Spring Security provides default login form where insert userName and password. 17 | */ 18 | 19 | @Autowired 20 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ 21 | 22 | auth.inMemoryAuthentication() 23 | .withUser("UserA").password("{noop}UserA").roles("USER") 24 | .and() 25 | .withUser("UserB").password("{noop}UserB").roles("USER"); 26 | 27 | } 28 | 29 | /** 30 | * Disable CSRF to simplify this demo and use default login form. 31 | */ 32 | 33 | @Override 34 | protected void configure(HttpSecurity httpSecurity) throws Exception{ 35 | httpSecurity.csrf().disable() 36 | .authorizeRequests().anyRequest().authenticated() 37 | .and() 38 | .formLogin() 39 | .and() 40 | .httpBasic(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/config/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.config; 2 | 3 | 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; 6 | import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; 7 | import org.springframework.web.socket.config.annotation.StompEndpointRegistry; 8 | 9 | @Configuration 10 | @EnableWebSocketMessageBroker 11 | public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { 12 | 13 | @Override 14 | public void registerStompEndpoints(StompEndpointRegistry registry){ 15 | //Register WebSocket => "/ws" endpoint, enable the SockJS protocol 16 | //SockJS can allow messaging options if websocket is not available! 17 | registry.addEndpoint("/ws") 18 | .setAllowedOrigins("*") 19 | .withSockJS(); 20 | } 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/controller/NotificationController.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.controller; 2 | 3 | import com.skb.pushservice.domain.Exist.ExistDto; 4 | import com.skb.pushservice.domain.WatchInfo.WatchInfoDto; 5 | import com.skb.pushservice.service.ConnectService; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | @Controller 12 | public class NotificationController { 13 | 14 | private final ConnectService connectService; 15 | 16 | public NotificationController(ConnectService connectService) { 17 | this.connectService = connectService; 18 | } 19 | 20 | @RequestMapping("/") 21 | public String index() { 22 | return "index"; 23 | } 24 | 25 | @RequestMapping("/notifications") 26 | public String notifications() { 27 | return "notifications"; 28 | } 29 | 30 | @PostMapping("/send") 31 | @ResponseBody 32 | public ResponseEntity send(@RequestBody WatchInfoDto.Request request) { 33 | //connect user to watch VOD 34 | connectService.connectUser(request); 35 | return new ResponseEntity<>(HttpStatus.OK); 36 | } 37 | 38 | @PostMapping("/force") 39 | public ResponseEntity forceConnect(@RequestBody ExistDto.Request request) { 40 | 41 | connectService.forceConnect(request); 42 | 43 | return new ResponseEntity<>(HttpStatus.OK); 44 | 45 | } 46 | 47 | @PatchMapping("/stop") 48 | public ResponseEntity stopConnect(@RequestBody WatchInfoDto.Request request) { 49 | 50 | connectService.stopConnect(request); 51 | 52 | return new ResponseEntity<>(HttpStatus.OK); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/domain/Exist/ExistDto.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.domain.Exist; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 5 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 6 | import com.skb.pushservice.domain.WatchInfo.WatchInfoDto; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | 10 | public class ExistDto { 11 | 12 | @Getter 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 15 | public static class Request{ 16 | 17 | private String newUser; 18 | 19 | private String existUser; 20 | 21 | private WatchInfoDto.Request watchInfo; 22 | 23 | } 24 | 25 | @Getter 26 | @NoArgsConstructor 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 29 | public static class Response{ 30 | 31 | private String newUser; 32 | 33 | private String existUser; 34 | 35 | public Response(String dtoNewUser, String dtoExistUser){ 36 | newUser = dtoNewUser; 37 | existUser = dtoExistUser; 38 | } 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/domain/WatchInfo/WatchInfo.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.domain.WatchInfo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 5 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Getter; 9 | 10 | @Getter 11 | @Builder 12 | @AllArgsConstructor 13 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 14 | public class WatchInfo { 15 | 16 | @JsonProperty 17 | private String pcid; 18 | 19 | private String episodeId; 20 | 21 | private String stbId; 22 | 23 | private String playStart; 24 | 25 | private String macAddress; 26 | 27 | private String playEnd; 28 | 29 | private boolean running; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/domain/WatchInfo/WatchInfoDto.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.domain.WatchInfo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 5 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 6 | import lombok.Getter; 7 | 8 | public class WatchInfoDto { 9 | 10 | @Getter 11 | @JsonInclude(JsonInclude.Include.NON_NULL) 12 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 13 | public static class Request { 14 | 15 | private String pcid; 16 | 17 | private String episodeId; 18 | 19 | private String stbId; 20 | 21 | private String playStart; 22 | 23 | private String macAddress; 24 | 25 | private String playEnd; 26 | 27 | private boolean running; 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/message/MessageListener.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.message; 2 | 3 | import com.skb.pushservice.domain.Exist.ExistDto; 4 | import com.skb.pushservice.service.NotificationService; 5 | import org.springframework.kafka.annotation.KafkaListener; 6 | import org.springframework.messaging.handler.annotation.Payload; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class MessageListener { 11 | 12 | private static final String existTopic = "userExists"; 13 | private static final String success = "success"; 14 | private final NotificationService notificationService; 15 | 16 | public MessageListener(NotificationService notificationService) { 17 | this.notificationService = notificationService; 18 | } 19 | 20 | @KafkaListener(topics = existTopic, containerFactory = "kafkaListenerContainerFactory", groupId = "exist-info-group") 21 | public void consumeExistUserInfo(@Payload ExistDto.Response payload) { 22 | notificationService.notifyUserExists(payload); 23 | } 24 | 25 | @KafkaListener(topics = success, containerFactory = "successKafkaListenerContainerFactory", groupId = "success-response-group") 26 | public void successResponse(@Payload String payload) { 27 | notificationService.notifySuccess(payload, "Update info success."); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/message/MessageSender.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.message; 2 | 3 | 4 | import com.skb.pushservice.domain.WatchInfo.WatchInfoDto; 5 | import lombok.RequiredArgsConstructor; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.kafka.core.KafkaTemplate; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | public class MessageSender { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(MessageSender.class); 16 | 17 | private final KafkaTemplate watchInfoKafkaTemplate; 18 | 19 | public void sendMessage(String topic, WatchInfoDto.Request dto) { 20 | watchInfoKafkaTemplate.send(topic, dto); 21 | logger.info(topic + " produced."); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/service/ConnectService.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.service; 2 | 3 | import com.skb.pushservice.domain.Exist.ExistDto; 4 | import com.skb.pushservice.domain.WatchInfo.WatchInfoDto; 5 | import com.skb.pushservice.message.MessageSender; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.web.client.RestTemplate; 8 | import org.springframework.web.util.UriComponents; 9 | import org.springframework.web.util.UriComponentsBuilder; 10 | 11 | @Service 12 | public class ConnectService { 13 | 14 | private final MessageSender messageSender; 15 | private final NotificationService notificationService; 16 | 17 | private static final String newTopic = "connectNewUser"; 18 | private static final String forceTopic = "forceConnect"; 19 | private static final String disconnectTopic = "disconnect"; 20 | private static final String url = "http://localhost:9000/connect"; 21 | 22 | public ConnectService(MessageSender messageSender, NotificationService notificationService) { 23 | this.messageSender = messageSender; 24 | this.notificationService = notificationService; 25 | } 26 | 27 | public void connectUser(WatchInfoDto.Request request) { 28 | //connect User with restTemplate 29 | UriComponents uri = UriComponentsBuilder.fromHttpUrl(url) 30 | .build(); 31 | 32 | RestTemplate restTemplate = new RestTemplate(); 33 | boolean isConnected = restTemplate.postForObject(uri.toUriString(), request, boolean.class); 34 | if (!isConnected) { 35 | notificationService.notifySuccess(request.getPcid(), "Update info success"); 36 | } 37 | } 38 | 39 | public void forceConnect(ExistDto.Request request) { 40 | 41 | //push alert to exist user 42 | notificationService.notifyDisconnect(new ExistDto.Response(request.getNewUser(), request.getExistUser())); 43 | 44 | //update watch info in redis 45 | messageSender.sendMessage(forceTopic, request.getWatchInfo()); 46 | 47 | } 48 | 49 | public void stopConnect(WatchInfoDto.Request request) { 50 | 51 | messageSender.sendMessage(disconnectTopic, request); 52 | 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /push-service/src/main/java/com/skb/pushservice/service/NotificationService.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice.service; 2 | 3 | 4 | import com.skb.pushservice.domain.Exist.ExistDto; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.messaging.simp.SimpMessagingTemplate; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class NotificationService { 11 | 12 | @Autowired 13 | private SimpMessagingTemplate simpMessagingTemplate; 14 | 15 | /** 16 | * Send notify to user who subscribed channel "/user/queue/notify". 17 | * The message will be sent to given client(user) 18 | * @Param newUser = new client 19 | * @Param existUser = exist client 20 | */ 21 | 22 | public void notifyUserExists(ExistDto.Response dto){ 23 | simpMessagingTemplate.convertAndSendToUser(dto.getNewUser(),"/queue/notify",dto); 24 | } 25 | 26 | /* 27 | * Send notify to user who subscribed channel "/user/queue/disconnect". 28 | * The message will be sent to given client(user) 29 | * 30 | */ 31 | 32 | public void notifyDisconnect(ExistDto.Response dto){ 33 | simpMessagingTemplate.convertAndSendToUser(dto.getExistUser(),"/queue/disconnect",dto); 34 | } 35 | 36 | public void notifySuccess(String user,String message){ 37 | simpMessagingTemplate.convertAndSendToUser(user,"/queue/connect",message); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /push-service/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9002 3 | 4 | spring: 5 | kafka: 6 | bootstrap-servers: localhost:9093,localhost:9094,localhost:9095 7 | consumer: 8 | auto-offset-reset: earliest -------------------------------------------------------------------------------- /push-service/src/main/resources/static/css/main.css: -------------------------------------------------------------------------------- 1 | /* CSS VARIABLES */ 2 | :root { 3 | --primary: #141414; 4 | --light: #F3F3F3; 5 | --dark: #686868; 6 | } 7 | 8 | html, body { 9 | width: 100vw; 10 | min-height: 100vh; 11 | margin: 0; 12 | padding: 0; 13 | background-color: #100031; 14 | color: var(--light); 15 | font-family: Arial, Helvetica, sans-serif; 16 | box-sizing: border-box; 17 | line-height: 1.4; 18 | } 19 | 20 | img { 21 | max-width: 100%; 22 | } 23 | 24 | h1 { 25 | padding-top: 60px; 26 | } 27 | 28 | .wrapper { 29 | margin: 0; 30 | padding: 0; 31 | } 32 | 33 | /* HEADER */ 34 | header { 35 | padding: 20px 20px 0 20px; 36 | position: fixed; 37 | top: 0; 38 | display: grid; 39 | grid-gap:5px; 40 | grid-template-columns: 1fr 4fr 1fr; 41 | grid-template-areas: 42 | "nt mn mn sb . . . "; 43 | background-color: #0c0137;; 44 | width: 100%; 45 | margin-bottom: 0px; 46 | } 47 | 48 | .btvLogo { 49 | grid-area: nt; 50 | object-fit: cover; 51 | width: 100px; 52 | max-height: 100%; 53 | 54 | padding-left: 30px; 55 | padding-top: 0px; 56 | } 57 | 58 | .btvLogo img { 59 | height: 35px; 60 | } 61 | 62 | #logo { 63 | color: #E50914; 64 | margin: 0; 65 | padding: 0; 66 | } 67 | 68 | 69 | .main-nav { 70 | grid-area: mn; 71 | padding: 0 30px 0 20px; 72 | } 73 | 74 | .main-nav a { 75 | color: var(--light); 76 | text-decoration: none; 77 | margin: 5px; 78 | } 79 | 80 | .main-nav a:hover { 81 | color: var(--dark); 82 | } 83 | 84 | .sub-nav { 85 | grid-area: sb; 86 | padding: 0 40px 0 40px; 87 | } 88 | 89 | .sub-nav a { 90 | color: var(--light); 91 | text-decoration: none; 92 | margin: 5px; 93 | } 94 | 95 | .sub-nav a:hover { 96 | color: var(--dark); 97 | } 98 | 99 | 100 | /* MAIN CONTIANER */ 101 | .main-container { 102 | padding: 50px; 103 | } 104 | 105 | .box { 106 | display: grid; 107 | grid-gap: 20px; 108 | grid-template-columns: repeat(6, minmax(100px, 1fr)); 109 | } 110 | 111 | .box a { 112 | transition: transform .3s; 113 | } 114 | 115 | .box a:hover { 116 | transition: transform .3s; 117 | -ms-transform: scale(1.4); 118 | -webkit-transform: scale(1.4); 119 | transform: scale(1.4); 120 | } 121 | 122 | .box img { 123 | border-radius: 2px; 124 | } 125 | 126 | /* LINKS */ 127 | .link { 128 | padding: 50px; 129 | } 130 | 131 | .sub-links ul { 132 | list-style: none; 133 | padding: 0; 134 | display: grid; 135 | grid-gap: 20px; 136 | grid-template-columns: repeat(4, 1fr); 137 | } 138 | 139 | .sub-links a { 140 | color: var(--dark); 141 | text-decoration: none; 142 | } 143 | 144 | .sub-links a:hover { 145 | color: var(--dark); 146 | text-decoration: underline; 147 | } 148 | 149 | .logos{ 150 | padding: 10px; 151 | } 152 | 153 | .logo { 154 | color: var(--dark); 155 | } 156 | 157 | 158 | /* FOOTER */ 159 | footer { 160 | padding: 20px; 161 | text-align: center; 162 | color: var(--dark); 163 | margin: 10px; 164 | } 165 | 166 | /* MEDIA QUERIES */ 167 | 168 | @media(max-width: 900px) { 169 | 170 | header { 171 | display: grid; 172 | grid-gap: 20px; 173 | grid-template-columns: repeat(2, 1fr); 174 | grid-template-areas: 175 | "nt nt nt . . . sb . . . " 176 | "mn mn mn mn mn mn mn mn mn mn"; 177 | } 178 | 179 | .box { 180 | display: grid; 181 | grid-gap: 20px; 182 | grid-template-columns: repeat(4, minmax(100px, 1fr)); 183 | } 184 | 185 | } 186 | 187 | @media(max-width: 700px) { 188 | 189 | header { 190 | display: grid; 191 | grid-gap: 20px; 192 | grid-template-columns: repeat(2, 1fr); 193 | grid-template-areas: 194 | "nt nt nt . . . sb . . . " 195 | "mn mn mn mn mn mn mn mn mn mn"; 196 | } 197 | 198 | .box { 199 | display: grid; 200 | grid-gap: 20px; 201 | grid-template-columns: repeat(3, minmax(100px, 1fr)); 202 | } 203 | 204 | .sub-links ul { 205 | display: grid; 206 | grid-gap: 20px; 207 | grid-template-columns: repeat(3, 1fr); 208 | } 209 | 210 | } 211 | 212 | @media(max-width: 500px) { 213 | 214 | .wrapper { 215 | font-size: 15px; 216 | } 217 | 218 | header { 219 | margin: 0; 220 | padding: 20px 0 0 0; 221 | position: static; 222 | display: grid; 223 | grid-gap: 20px; 224 | grid-template-columns: repeat(1, 1fr); 225 | grid-template-areas: 226 | "nt" 227 | "mn" 228 | "sb"; 229 | text-align: center; 230 | } 231 | 232 | .btvLogo { 233 | max-width: 100%; 234 | margin: auto; 235 | padding-right: 20px; 236 | } 237 | 238 | .main-nav { 239 | display: grid; 240 | grid-gap: 0px; 241 | grid-template-columns: repeat(1, 1fr); 242 | text-align: center; 243 | } 244 | 245 | h1 { 246 | text-align: center; 247 | font-size: 18px; 248 | } 249 | 250 | 251 | 252 | .box { 253 | display: grid; 254 | grid-gap: 20px; 255 | grid-template-columns: repeat(1, 1fr); 256 | text-align: center; 257 | } 258 | 259 | .box a:hover { 260 | transition: transform .3s; 261 | -ms-transform: scale(1); 262 | -webkit-transform: scale(1); 263 | transform: scale(1.2); 264 | } 265 | 266 | .logos { 267 | display: grid; 268 | grid-gap: 20px; 269 | grid-template-columns: repeat(2, 1fr); 270 | text-align: center; 271 | } 272 | 273 | .sub-links ul { 274 | display: grid; 275 | grid-gap: 20px; 276 | grid-template-columns: repeat(1, 1fr); 277 | text-align: center; 278 | font-size: 15px; 279 | } 280 | 281 | 282 | 283 | 284 | 285 | } -------------------------------------------------------------------------------- /push-service/src/main/resources/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimyuuum/simultaneous-viewing-limit-system/ce2be6f253633a1c47149e3ede0e73562743d923/push-service/src/main/resources/static/img/logo.png -------------------------------------------------------------------------------- /push-service/src/main/resources/static/img/play_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimyuuum/simultaneous-viewing-limit-system/ce2be6f253633a1c47149e3ede0e73562743d923/push-service/src/main/resources/static/img/play_img.png -------------------------------------------------------------------------------- /push-service/src/main/resources/static/js/sockjs-0.3.4.min.js: -------------------------------------------------------------------------------- 1 | /* SockJS client, version 0.3.4, http://sockjs.org, MIT License 2 | 3 | Copyright (c) 2011-2012 VMware, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // JSON2 by Douglas Crockford (minified). 25 | var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c1?this._listeners[a]=d.slice(0,e).concat(d.slice(e+1)):delete this._listeners[a];return}return},d.prototype.dispatchEvent=function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d=3e3&&a<=4999},c.countRTO=function(a){var b;return a>100?b=3*a:b=a+200,b},c.log=function(){b.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)},c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}},c.flatUrl=function(a){return a.indexOf("?")===-1&&a.indexOf("#")===-1},c.amendUrl=function(b){var d=a.location;if(!b)throw new Error("Wrong url for SockJS");if(!c.flatUrl(b))throw new Error("Only basic urls are supported in SockJS");return b.indexOf("//")===0&&(b=d.protocol+b),b.indexOf("/")===0&&(b=d.protocol+"//"+d.host+b),b=b.replace(/[/]+$/,""),b},c.arrIndexOf=function(a,b){for(var c=0;c=0},c.delay=function(a,b){return typeof a=="function"&&(b=a,a=0),setTimeout(b,a)};var i=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j={"\0":"\\u0000","\x01":"\\u0001","\x02":"\\u0002","\x03":"\\u0003","\x04":"\\u0004","\x05":"\\u0005","\x06":"\\u0006","\x07":"\\u0007","\b":"\\b","\t":"\\t","\n":"\\n","\x0b":"\\u000b","\f":"\\f","\r":"\\r","\x0e":"\\u000e","\x0f":"\\u000f","\x10":"\\u0010","\x11":"\\u0011","\x12":"\\u0012","\x13":"\\u0013","\x14":"\\u0014","\x15":"\\u0015","\x16":"\\u0016","\x17":"\\u0017","\x18":"\\u0018","\x19":"\\u0019","\x1a":"\\u001a","\x1b":"\\u001b","\x1c":"\\u001c","\x1d":"\\u001d","\x1e":"\\u001e","\x1f":"\\u001f",'"':'\\"',"\\":"\\\\","\x7f":"\\u007f","\x80":"\\u0080","\x81":"\\u0081","\x82":"\\u0082","\x83":"\\u0083","\x84":"\\u0084","\x85":"\\u0085","\x86":"\\u0086","\x87":"\\u0087","\x88":"\\u0088","\x89":"\\u0089","\x8a":"\\u008a","\x8b":"\\u008b","\x8c":"\\u008c","\x8d":"\\u008d","\x8e":"\\u008e","\x8f":"\\u008f","\x90":"\\u0090","\x91":"\\u0091","\x92":"\\u0092","\x93":"\\u0093","\x94":"\\u0094","\x95":"\\u0095","\x96":"\\u0096","\x97":"\\u0097","\x98":"\\u0098","\x99":"\\u0099","\x9a":"\\u009a","\x9b":"\\u009b","\x9c":"\\u009c","\x9d":"\\u009d","\x9e":"\\u009e","\x9f":"\\u009f","\xad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"},k=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,l,m=JSON&&JSON.stringify||function(a){return i.lastIndex=0,i.test(a)&&(a=a.replace(i,function(a){return j[a]})),'"'+a+'"'},n=function(a){var b,c={},d=[];for(b=0;b<65536;b++)d.push(String.fromCharCode(b));return a.lastIndex=0,d.join("").replace(a,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),a.lastIndex=0,c};c.quote=function(a){var b=m(a);return k.lastIndex=0,k.test(b)?(l||(l=n(k)),b.replace(k,function(a){return l[a]})):b};var o=["websocket","xdr-streaming","xhr-streaming","iframe-eventsource","iframe-htmlfile","xdr-polling","xhr-polling","iframe-xhr-polling","jsonp-polling"];c.probeProtocols=function(){var a={};for(var b=0;b0&&h(a)};return c.websocket!==!1&&h(["websocket"]),d["xhr-streaming"]&&!c.null_origin?e.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-streaming"):h(["iframe-eventsource","iframe-htmlfile"]),d["xhr-polling"]&&!c.null_origin?e.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-polling"):h(["iframe-xhr-polling","jsonp-polling"]),e};var p="_sockjs_global";c.createHook=function(){var a="a"+c.random_string(8);if(!(p in b)){var d={};b[p]=function(a){return a in d||(d[a]={id:a,del:function(){delete d[a]}}),d[a]}}return b[p](a)},c.attachMessage=function(a){c.attachEvent("message",a)},c.attachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.addEventListener(c,d,!1):(a.attachEvent("on"+c,d),b.attachEvent("on"+c,d))},c.detachMessage=function(a){c.detachEvent("message",a)},c.detachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.removeEventListener(c,d,!1):(a.detachEvent("on"+c,d),b.detachEvent("on"+c,d))};var q={},r=!1,s=function(){for(var a in q)q[a](),delete q[a]},t=function(){if(r)return;r=!0,s()};c.attachEvent("unload",t),c.unload_add=function(a){var b=c.random_string(8);return q[b]=a,r&&c.delay(s),b},c.unload_del=function(a){a in q&&delete q[a]},c.createIframe=function(b,d){var e=a.createElement("iframe"),f,g,h=function(){clearTimeout(f);try{e.onload=null}catch(a){}e.onerror=null},i=function(){e&&(h(),setTimeout(function(){e&&e.parentNode.removeChild(e),e=null},0),c.unload_del(g))},j=function(a){e&&(i(),d(a))},k=function(a,b){try{e&&e.contentWindow&&e.contentWindow.postMessage(a,b)}catch(c){}};return e.src=b,e.style.display="none",e.style.position="absolute",e.onerror=function(){j("onerror")},e.onload=function(){clearTimeout(f),f=setTimeout(function(){j("onload timeout")},2e3)},a.body.appendChild(e),f=setTimeout(function(){j("timeout")},15e3),g=c.unload_add(i),{post:k,cleanup:i,loaded:h}},c.createHtmlfile=function(a,d){var e=new ActiveXObject("htmlfile"),f,g,i,j=function(){clearTimeout(f)},k=function(){e&&(j(),c.unload_del(g),i.parentNode.removeChild(i),i=e=null,CollectGarbage())},l=function(a){e&&(k(),d(a))},m=function(a,b){try{i&&i.contentWindow&&i.contentWindow.postMessage(a,b)}catch(c){}};e.open(),e.write(' 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 20 | 28 | 34 |
35 | 36 | 37 | 38 |
39 |
40 |

[8월 4주] 해외드라마 TOP 30

41 |
42 | /*your-vod-thumbnail*/ 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 |
51 | 52 | 53 |

새로 들어온 해외 드라마

54 |
55 | /*your-vod-thumbnail*/ 56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 | 64 |

등골이 오싹해지는 범죄 스릴러 드라마

65 |
66 | /*your-vod-thumbnail*/ 67 | 68 | 69 | 70 | 71 | 72 | 73 |
74 | 75 | 76 |

[8월 3주] 미국/영국 드라마 TOP 30

77 |
78 | /*your-vod-thumbnail*/ 79 | 80 | 81 | 82 | 83 | 84 | 85 |
86 | 87 | 88 |
89 |
90 | 91 | 92 | 194 | 195 | -------------------------------------------------------------------------------- /push-service/src/main/resources/templates/notifications.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 19 | 20 | 21 |
22 |
23 |
24 | 25 | 26 | 27 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /push-service/src/test/java/com/skb/pushservice/PushServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.skb.pushservice; 2 | 3 | import com.skb.pushservice.domain.WatchInfo.WatchInfoDto; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | class PushServiceApplicationTests { 9 | 10 | @Test 11 | void contextLoads() { 12 | } 13 | 14 | } 15 | --------------------------------------------------------------------------------