pangs пре 6 година
комит
71b66bb6eb
51 измењених фајлова са 4907 додато и 0 уклоњено
  1. 33 0
      .gitignore
  2. 114 0
      .mvn/wrapper/MavenWrapperDownloader.java
  3. BIN
      .mvn/wrapper/maven-wrapper.jar
  4. 1 0
      .mvn/wrapper/maven-wrapper.properties
  5. 286 0
      mvnw
  6. 161 0
      mvnw.cmd
  7. 78 0
      pom.xml
  8. 18 0
      src/main/java/com/icontrols/kakao/KakaoApplication.java
  9. 13 0
      src/main/java/com/icontrols/kakao/ServletInitializer.java
  10. 100 0
      src/main/java/com/icontrols/kakao/common/ERROR_INFO.java
  11. 79 0
      src/main/java/com/icontrols/kakao/common/KAKAO_RESULT.java
  12. 36 0
      src/main/java/com/icontrols/kakao/common/KEY_INFO.java
  13. 36 0
      src/main/java/com/icontrols/kakao/common/SERVERINFO.java
  14. 116 0
      src/main/java/com/icontrols/kakao/common/THINGS_INFO.java
  15. 82 0
      src/main/java/com/icontrols/kakao/common/TYPE.java
  16. 73 0
      src/main/java/com/icontrols/kakao/common/utils/AES256Util.java
  17. 14 0
      src/main/java/com/icontrols/kakao/common/utils/BeanUtils.java
  18. 116 0
      src/main/java/com/icontrols/kakao/common/utils/CommonUtil.java
  19. 88 0
      src/main/java/com/icontrols/kakao/common/utils/JWTUtils2.java
  20. 43 0
      src/main/java/com/icontrols/kakao/common/utils/UtilLib.java
  21. 18 0
      src/main/java/com/icontrols/kakao/common/utils/XmlUtil.java
  22. 20 0
      src/main/java/com/icontrols/kakao/config/ApplicationContextProvider.java
  23. 147 0
      src/main/java/com/icontrols/kakao/config/AsyncConfig.java
  24. 85 0
      src/main/java/com/icontrols/kakao/config/AsyncConfigReportState.java
  25. 23 0
      src/main/java/com/icontrols/kakao/config/AsyncExceptionHandler.java
  26. 24 0
      src/main/java/com/icontrols/kakao/config/DanziConfig.java
  27. 32 0
      src/main/java/com/icontrols/kakao/config/DanziFilter.java
  28. 40 0
      src/main/java/com/icontrols/kakao/config/DanziTokenInterceptor.java
  29. 43 0
      src/main/java/com/icontrols/kakao/controller/AuthController.java
  30. 106 0
      src/main/java/com/icontrols/kakao/controller/KakaoDanziController.java
  31. 166 0
      src/main/java/com/icontrols/kakao/controller/MainController.java
  32. 133 0
      src/main/java/com/icontrols/kakao/entity/DonghoInfo.java
  33. 12 0
      src/main/java/com/icontrols/kakao/repo/DonghoInfoRepository.java
  34. 8 0
      src/main/java/com/icontrols/kakao/service/AuthService.java
  35. 15 0
      src/main/java/com/icontrols/kakao/service/KakaoExtensionService.java
  36. 29 0
      src/main/java/com/icontrols/kakao/service/impl/AuthServiceImpl.java
  37. 265 0
      src/main/java/com/icontrols/kakao/service/impl/KakaoExtensionServiceImpl.java
  38. 35 0
      src/main/java/com/icontrols/kakao/socket/utils/IPGenerator.java
  39. 244 0
      src/main/java/com/icontrols/kakao/socket/utils/SSLClient.java
  40. 107 0
      src/main/java/com/icontrols/kakao/socket/utils/XMLConstants.java
  41. 43 0
      src/main/java/com/icontrols/kakao/socket/utils/XMLGenerator.java
  42. 266 0
      src/main/java/com/icontrols/kakao/socket/utils/iMapRequestSync.java
  43. 96 0
      src/main/java/com/icontrols/kakao/socket/wallpad/KakaoEventHandler.java
  44. 680 0
      src/main/java/com/icontrols/kakao/socket/wallpad/KakaoMsgHandler.java
  45. 69 0
      src/main/java/com/icontrols/kakao/socket/wallpad/LogMsgHandler.java
  46. 89 0
      src/main/java/com/icontrols/kakao/socket/wallpad/RecieveiMapMultiSocket.java
  47. 245 0
      src/main/java/com/icontrols/kakao/socket/wallpad/ThreadServerHandler.java
  48. 320 0
      src/main/java/com/icontrols/kakao/socket/wallpad/WallpadMsgSender.java
  49. 16 0
      src/main/resources/application.properties
  50. 28 0
      src/main/resources/logback-spring.xml
  51. 16 0
      src/test/java/com/icontrols/kakao/KakaoApplicationTests.java

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+/target2/
+
+/log/
+
+
+*.class
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+/build/

+ 114 - 0
.mvn/wrapper/MavenWrapperDownloader.java

@@ -0,0 +1,114 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+    /**
+     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+     */
+    private static final String DEFAULT_DOWNLOAD_URL =
+            "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+    /**
+     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+     * use instead of the default one.
+     */
+    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+            ".mvn/wrapper/maven-wrapper.properties";
+
+    /**
+     * Path where the maven-wrapper.jar will be saved to.
+     */
+    private static final String MAVEN_WRAPPER_JAR_PATH =
+            ".mvn/wrapper/maven-wrapper.jar";
+
+    /**
+     * Name of the property which should be used to override the default download url for the wrapper.
+     */
+    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+    public static void main(String args[]) {
+        System.out.println("- Downloader started");
+        File baseDirectory = new File(args[0]);
+        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+        // If the maven-wrapper.properties exists, read it and check if it contains a custom
+        // wrapperUrl parameter.
+        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+        String url = DEFAULT_DOWNLOAD_URL;
+        if(mavenWrapperPropertyFile.exists()) {
+            FileInputStream mavenWrapperPropertyFileInputStream = null;
+            try {
+                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+                Properties mavenWrapperProperties = new Properties();
+                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+            } catch (IOException e) {
+                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+            } finally {
+                try {
+                    if(mavenWrapperPropertyFileInputStream != null) {
+                        mavenWrapperPropertyFileInputStream.close();
+                    }
+                } catch (IOException e) {
+                    // Ignore ...
+                }
+            }
+        }
+        System.out.println("- Downloading from: : " + url);
+
+        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+        if(!outputFile.getParentFile().exists()) {
+            if(!outputFile.getParentFile().mkdirs()) {
+                System.out.println(
+                        "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+            }
+        }
+        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+        try {
+            downloadFileFromURL(url, outputFile);
+            System.out.println("Done");
+            System.exit(0);
+        } catch (Throwable e) {
+            System.out.println("- Error downloading");
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+        URL website = new URL(urlString);
+        ReadableByteChannel rbc;
+        rbc = Channels.newChannel(website.openStream());
+        FileOutputStream fos = new FileOutputStream(destination);
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        fos.close();
+        rbc.close();
+    }
+
+}

BIN
.mvn/wrapper/maven-wrapper.jar


+ 1 - 0
.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip

+ 286 - 0
mvnw

@@ -0,0 +1,286 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+  # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        wget "$jarUrl" -O "$wrapperJarPath"
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        curl -o "$wrapperJarPath" "$jarUrl"
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 161 - 0
mvnw.cmd

@@ -0,0 +1,161 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
+	IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    echo Found %WRAPPER_JAR%
+) else (
+    echo Couldn't find %WRAPPER_JAR%, downloading it ...
+	echo Downloading from: %DOWNLOAD_URL%
+    powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
+    echo Finished downloading %WRAPPER_JAR%
+)
+@REM End of extension
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%

+ 78 - 0
pom.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.1.3.RELEASE</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>com.icontrols.kakao</groupId>
+	<artifactId>kakao</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<packaging>war</packaging>
+	<name>kakao</name>
+	<description>Kakao Home project</description>
+
+	<properties>
+		<java.version>1.8</java.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-jpa</artifactId>
+		</dependency>
+		<!-- <dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency> -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-tomcat</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+			<version>4.5.2</version>
+		</dependency>
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt</artifactId>
+			<version>0.9.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.json</groupId>
+			<artifactId>json</artifactId>
+			<version>20160810</version>
+		</dependency>
+		<dependency>
+    		<groupId>org.mariadb.jdbc</groupId>
+    		<artifactId>mariadb-java-client</artifactId>
+    		<version>2.1.0</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 18 - 0
src/main/java/com/icontrols/kakao/KakaoApplication.java

@@ -0,0 +1,18 @@
+package com.icontrols.kakao;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import com.icontrols.kakao.socket.wallpad.RecieveiMapMultiSocket;
+
+@SpringBootApplication
+public class KakaoApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(KakaoApplication.class, args);
+		
+		// Wallpad에서 전송하는 EventMsg 수신 처리
+		RecieveiMapMultiSocket recieveSocket = new RecieveiMapMultiSocket();
+	}
+
+}

+ 13 - 0
src/main/java/com/icontrols/kakao/ServletInitializer.java

@@ -0,0 +1,13 @@
+package com.icontrols.kakao;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+public class ServletInitializer extends SpringBootServletInitializer {
+
+	@Override
+	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+		return application.sources(KakaoApplication.class);
+	}
+
+}

+ 100 - 0
src/main/java/com/icontrols/kakao/common/ERROR_INFO.java

@@ -0,0 +1,100 @@
+package com.icontrols.kakao.common;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class ERROR_INFO {
+
+	public enum CODE {
+		SUCCESS(200, "정상동작", "Success"),
+
+		FAIL_602(602, "토큰 만료", "Token Expired"), FAIL_606(606, "요청을 수락할 수 없음", "Not Acceptable"),
+		FAIL_661(661, "허용된 HTTP Method가 아님", "Method Not Allowed"),
+		FAIL_662(662, "요청 Content-Type 미지원 오류", "Content-Type Not Allowed"),
+		FAIL_670(670, "유효하지 않은 JSON 문자열", "Invalid JSON Body"),
+		FAIL_671(671, "필수 Body 컨텐츠 누락", "Mandatory Field of Body Content Omitted"),
+		FAIL_672(672, "Body 컨텐츠의 필드 길이, 데이터 타입등 필드 규칙 오류", "Invalid Body Content Field"),
+		FAIL_693(693, "Service Unavailable", "Service Unavailable"),
+
+		FAIL_801(801, "세션키 불일치", "Invalid Session Key"), 
+		FAIL_802(802, "임시 인증키 불일치", "Invalid Temp Key Information"),
+		FAIL_804(804, "등록된 디바이스 없음", "Not Exist Register-Device"), 
+		FAIL_806(806, "단말 모델 존재 하지 않음", "단말 모델정보 없음"),
+		FAIL_807(807, "비밀번호 인증 실패", "password error"), 
+		FAIL_820(820, "이미 등록된 단말", "Already Registered Device"),
+		FAIL_821(821, "존재하지 않는 사용자", "Not find user"), 
+		FAIL_822(822, "권한이 없는 요청", "Permission Denied"),
+		FAIL_830(830, "단말 연결 되지 않음", "Device Connect Failure"), 
+		FAIL_831(831, "단말 응답 시간 초과", "Device Time out"),
+		FAIL_832(832, "단말 응답은 성공, 제어결과 이벤트 응답시간 초과", "Device Event Time out"),
+		FAIL_833(833, "단말 제어결과 싪패", "Device Control Result Fail"),
+		FAIL_834(834, "단말로부터 수신한 메세지 형식 에러", "단말로부터 수신한 메시지 포멧 오류"),
+
+		FAIL_900(900, "허용하지 않은 제휴사 정보 사용", "Invalid Vendor Info"),
+		FAIL_901(901, "허용하지 않는 기능 API Key 사용", "Invalid API Key"),
+		FAIL_902(902, "전문중 Header 항목 누락", "Mandatory Field of Header Omitted"),
+		FAIL_903(903, "등록되지 않은 디바이스 타입 정보 사용", "Device Type Unregistered"),
+		FAIL_904(904, "등록되지 않은 X-HIT-Version 정보 사용", "Version Unregisted"),
+		FAIL_910(910, "제휴사 및 Gateway와 연동간 접속시 실패", "Partnership Connect Refuse"),
+		FAIL_914(914, "해당 사용자 정보에 대해 제휴사 및 Gatewa에서 인증 실패함", "Invalid Partnership Account Authentication"),
+		FAIL_920(920, "프로파일 검증간, Command 값이 누락", "Mandatory Field of Profile Command"),
+		FAIL_921(921, "프로파일 검증간, 필수 파라미터가 누락", "Invalid Parameter"),
+		FAIL_923(923, "프로파일 검증간, 필수 파라미터 값의 범위가 벗어남", "Invalid Profile Parameter Range"),
+		FAIL_927(927, "프로파일 검증간, 해당 command에 정의되지 않은 속성이 존재함", "Command Undefined property exist"),
+		FAIL_941(941, "기기에러", "Device Error"),
+
+		FAIL_970(970, "해당 사용자는 관리사무소 승인을 받지 않음", "Unauthorized Users"),
+		FAIL_971(971, "여러 개 동호를 갖고 있는 사용자", "Multiple Dongho Account Users"),
+		FAIL_972(972, "해당 사용자 현장은 LG 서비스 사용 않음", "Not user LG Service"),
+		FAIL_973(973, "해당 ID는 동호가 없음", "Account does not match Dongho"),
+		FAIL_974(974, "Access token 만료", "Access Token Expiration"),
+		FAIL_975(975, "Refresh token 만료", "Refresh Token Expiration"),
+		FAIL_979(979, "계정관련 기타 에러", "Not Defind ID Error"),
+
+		FAIL_999(999, "정의되지 않은 에러", "Other Error");
+
+		private int mIdx;
+		private String mContent;
+		private String mEngContent;
+
+		private CODE(int idx, final String txt, String txtEng) {
+			this.setmIdx(idx);
+			this.mContent = txt;
+			this.setmEngContent(txtEng);
+		}
+
+		public String getContents() {
+			return mContent;
+		}
+
+		public static String getContents(final CODE kind) {
+			return kind.mContent;
+		}
+
+		public int getmIdx() {
+			return mIdx;
+		}
+
+		public void setmIdx(int mIdx) {
+			this.mIdx = mIdx;
+		}
+
+		public String getmEngContent() {
+			return mEngContent;
+		}
+
+		public void setmEngContent(String mEngContent) {
+			this.mEngContent = mEngContent;
+		}
+	}
+	
+	public static Map<String, Object> getErrorInfo(int code, String message){
+		Map<String, Object> error = new HashMap<String, Object>();
+		
+		error.put("code", Integer.toString(code));
+		error.put("message", message);
+		
+		return error;
+	}
+
+}

+ 79 - 0
src/main/java/com/icontrols/kakao/common/KAKAO_RESULT.java

@@ -0,0 +1,79 @@
+package com.icontrols.kakao.common;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class KAKAO_RESULT {
+	public static final int SUCCESS 			= 200, 
+							FAIL_INTERNAL 		= -200,
+							FAIL_EXPIREDTOKEN 	= -201,
+							FAIL_UNREACHABLE	= -202,
+							FAIL_NONEXIST		= -203,
+							FAIL_THING_INTERNAL	= -204,
+							FAIL_INVALID_REQ	= -205,
+							FAIL_INVALID_TOKEN	= -206,
+							FAIL_INVALID_CLIENT	= -207;
+	
+	public enum CODE {
+		SUCCESS(200, "정상동작", "Success"),
+		
+		FAIL_INTERNAL		(-200, "InternalServerError", 	"내부 서버 에러"),
+		FAIL_EXPIREDTOKEN	(-201, "ExpiredAccessToken", 	"인증토큰 만료"),
+		FAIL_UNREACHABLE	(-202, "ThingUnreachable", 		"Things 연결 실패"),
+		FAIL_NONEXIST		(-203, "ThingNonexistent", 		"정의되지은은 기기 Endpoint"),
+		FAIL_THING_INTERNAL	(-204, "ThingInternalError", 	"기기에서 처리할 수 없는 요청" ),
+		FAIL_INVALID_REQ	(-205, "InvalidRequest", 		"올바르지 않은 요청메세지"),
+		FAIL_INVALID_TOKEN	(-206, "InvalidAccessToken", 	"유효하지 않은 인증토큰"),
+		FAIL_INVALID_CLIENT	(-207, "InvalidClient", 		"Client 인증 실패");
+		
+		private int resultCode;
+		private String message;
+		private String description;
+		
+		private CODE(int resultCode, String message, String description) {
+			this.resultCode = resultCode;
+			this.message = message;
+			this.description = description;
+		}
+
+		public int getResultCode() {
+			return resultCode;
+		}
+
+		public void setResultCode(int resultCode) {
+			this.resultCode = resultCode;
+		}
+
+		public String getMessage() {
+			return message;
+		}
+
+		public void setMessage(String message) {
+			this.message = message;
+		}
+
+		public String getDescription() {
+			return description;
+		}
+
+		public void setDescription(String description) {
+			this.description = description;
+		}
+		
+		
+	}
+	
+	public static Map<String, Object> getErrorInfo(int code){
+		Map<String, Object> error = new HashMap<String, Object>();
+		
+		for(KAKAO_RESULT.CODE CODE : KAKAO_RESULT.CODE.values()) {
+			if(code == CODE.getResultCode()) {
+				error.put(KEY_INFO.ERROR_KEY_CODE, Integer.toString(code));
+				error.put(KEY_INFO.ERROR_KEY_MESSAGE, CODE.getMessage());
+				
+				return error;
+			}
+		}
+		return null;
+	}
+}

+ 36 - 0
src/main/java/com/icontrols/kakao/common/KEY_INFO.java

@@ -0,0 +1,36 @@
+package com.icontrols.kakao.common;
+
+public class KEY_INFO {
+	// kakao request response 시 http body의 key 값
+	public static final String 	HTTP_KEY_ID 			= "id",
+								HTTP_KEY_TYPE 			= "type",
+								HTTP_KEY_AUTHORIZATION 	= "authorization",
+								HTTP_KEY_VERSION 		= "version",
+								HTTP_KEY_UNREACHABLEIDS	= "unreachableIds",
+								HTTP_KEY_BODY 			= "body",
+								HTTP_KEY_ERROR 			= "error",
+								HTTP_KEY_ACCESS_TOKEN	= "access_token",
+								HTTP_KEY_REFRESH_TOKEN	= "refresh_token",
+								HTTP_KEY_TOKEN_TYPE		= "token_type",
+								HTTP_KEY_EXPIRES_IN		= "expires_in"
+								;
+	
+	// kakao request response Body의 key 값
+	public static final String 	BODY_KEY_ACCESS_TOKEN	= "accessToken",
+								BODY_KEY_REFRESH_TOKEN	= "refreshToken",
+								BODY_KEY_EXPIRES_IN		= "expiresIn",
+								BODY_KEY_ENDPOINT_ID	= "endpointId",
+								BODY_KEY_ENDPOINT_IDS	= "endpointIds",
+								BODY_KEY_NAME			= "name",
+								BODY_KEY_DISPLAY_TYPE	= "displayType",
+								BODY_KEY_CAPABILITIES	= "capabilities",
+								BODY_KEY_STATE			= "state",
+								BODY_KEY_CAPABILITY_NAME= "capabilityName",
+								BODY_KEY_DESIRED		= "desired"
+								;
+	
+	// error request response의 key 값
+	public static final String 	ERROR_KEY_CODE 		= "code",
+								ERROR_KEY_MESSAGE	= "message";
+}
+  

+ 36 - 0
src/main/java/com/icontrols/kakao/common/SERVERINFO.java

@@ -0,0 +1,36 @@
+package com.icontrols.kakao.common;
+
+public class SERVERINFO {
+	public static final int debugFlag = 1;
+	
+	// 월패드 전송 포트 - 신규 프로토콜 적용( 월패드에서 이통사 연동 관련 프로토콜은 기존 수신 소켓이 아닌 독립적인 서비스 수신소켓으로
+	// 실행됨 )
+	public static final int clientNewPort = 10002;
+
+	// 월패드에서 수신되는 데이터를 처리 하기 위한 소켓 포트
+	public static final int WalladReceivePort = 8098;
+
+	public static final String DanjiServerIP = "10.0.1.1";
+	public static final int DanjiServerPort = 10000;
+
+	// public static final String SendEventIP = "http://61.33.215.56"; // 이통사 개발서버
+	// public static final String SendEventIP = "http://112.220.212.226"; //이통사 상용서버
+	public static final String SendDevEventURL = "http://localhost"; // 중계서버로 변경해야함.
+	public static String SendEventURL = "https://hdc-smart.com";
+
+	public static String SendEventPort = "8005";
+	public static final String SendDevEventPort = "8002";
+
+	public static final String UPDATE_NOTICE_KIND = "/service/new_server.dat";
+	public static final String DEV_SERVER = "/service/dev_server.dat";
+
+	public static final String ONLY_ONE_GAS = "/service/only_one_gas.dat";
+	public static final String WIN_ONLY_ONE_GAS = "C:\\service\\only_one_gas.dat";
+
+	public static boolean GasCount_HardCoding_One = false;
+
+	public static final String KAKAO_EVENT_CLIENT_ID = "client-1";
+	public static final String KAKAO_EVENT_CLIENT_SECRET = "client-1-secret";
+	
+//	public static LogInfo m_LogInfo;
+}

+ 116 - 0
src/main/java/com/icontrols/kakao/common/THINGS_INFO.java

@@ -0,0 +1,116 @@
+package com.icontrols.kakao.common;
+
+public final class THINGS_INFO {
+	public static final int THINGS_LIGHT 		= 1,  THINGS_TEMPER  	= 2,  THINGS_ELECPLUG 	= 3,  THINGS_GAS 		= 4,
+							THINGS_ILGALSWITCH 	= 5,  THINGS_WALLPAD  	= 6,  THINGS_VENTIL 	= 7,  THINGS_NOTICE 	= 8,
+							THINGS_VOTE			= 9,  THINGS_PARKING  	= 10, THINGS_PARCEL 	= 11, THINGS_HEMS 		= 12,
+							THINGS_CARIN 		= 13, THINGS_ELEVATOR 	= 14, THINGS_VISITOR 	= 15, THINGS_EMERGENCY 	= 16;
+	
+	public enum THINGS_CAPABILITY{
+		LIGHT("light","{\"capabilities\" : [ { \"name\" : \"Power\" } ]}"),
+		TEMPER("temper", "{\"capabilities\" : [ { \"name\" : \"Power\" }, { \"name\" : \"Temperature\" }, { \"name\" : \"TemperatureSetPoint\" } ]}"),
+		VENTIL("ventil","{\"capabilities\" : [ { \"name\" : \"Power\" }, { \"name\" : \"AirFlow\" } ]}"),
+		GAS("gas","{\"capabilities\" : [ { \"name\" : \"Lock\" } ] }");
+		
+		private String thingsName;
+		private String capJsonStr;
+		
+		private THINGS_CAPABILITY(String thingsName, String capJsonStr) {
+			this.thingsName = thingsName;
+			this.capJsonStr = capJsonStr;
+		}
+		public String getThingsName() {
+			return thingsName;
+		}
+		public void setThingsName(String thingsName) {
+			this.thingsName = thingsName;
+		}
+		public String getCapJsonStr() {
+			return capJsonStr;
+		}
+		public void setCapJsonStr(String capJsonStr) {
+			this.capJsonStr = capJsonStr;
+		}
+		
+		
+	}
+	public enum THINGS{
+		//endpoint는 카카오 API와 연동될 기기 endpoint
+		// name 은 우리쪽에서 정의된 기기 명
+		// desc 는 기기 설명
+		LIGHT(1,"LIGHT", "Light", "조명스위치"),				
+		TEMPER(2, "TEMPER", "FloorHeater", "난방"),
+		ELECPLUG(3, "ELECTRIC", "PowerOutlet", "전기"),
+		GAS(4, "GAS", "GasValve", "가스밸브"),
+		ILGALSWITCH(5, "ILGALSWITCH", "ilgal", "일괄조명스위치"),
+		WALLPAD(6, "WALLPAD", "walpad", "월패드"),
+		VENTIL(7, "VENTIL", "AirVentilator", "환기시스템"),
+		NOTICE(8, "NOTICE", "InformationReporter", "공지사항"),
+		VOTE(9, "VOTE", "vote", "주민투표"),
+		PARKING(10, "PARKING", "parking", "주차위치"),
+		PARCEL(11, "PARCEL", "parcel", "택배도착"),
+		HEMS(12, "HEMS", "hems", "원격검침"),
+		CARIN(13, "CARIN", "carin", "주차관제(입차/출차)"),
+		ELEVATOR(14, "ELEVATOR", "ElevatorCaller", "엘리베이터"),
+		VISITOR(15, "VISITOR", "visitor", "방문자"),
+		EMERGENCY(16, "EMERGENCY", "emergency", "방범")
+		;
+		
+		private int endPoint;
+		private String thingsName;
+		private String kakaoDisplayName;
+		private String desc;
+		
+		private THINGS(int endPoint, String thingsName, String kakaoDisplayName, String desc) {
+			this.endPoint = endPoint;
+			this.thingsName = thingsName;
+			this.kakaoDisplayName = kakaoDisplayName;
+			this.desc = desc;
+		}
+
+		public int getEndPoint() {
+			return endPoint;
+		}
+
+		public void setEndPoint(int endPoint) {
+			this.endPoint = endPoint;
+		}
+
+		public String getThingsName() {
+			return thingsName;
+		}
+
+		public void setThingsName(String thingsName) {
+			this.thingsName = thingsName;
+		}
+
+		public String getKakaoDisplayName() {
+			return kakaoDisplayName;
+		}
+
+		public void setKakaoDisplayName(String kakaoDisplayName) {
+			this.kakaoDisplayName = kakaoDisplayName;
+		}
+
+		public String getDesc() {
+			return desc;
+		}
+
+		public void setDesc(String desc) {
+			this.desc = desc;
+		}
+	}
+	
+	public static THINGS_INFO.THINGS getThingsType(String modelId){    
+		
+		THINGS_INFO.THINGS[] list = THINGS_INFO.THINGS.values();
+		
+		for(THINGS_INFO.THINGS things:list) {
+			if(modelId.toLowerCase().contains(things.getThingsName().toLowerCase())) {
+				return things;
+			}
+		}
+		
+		return null;
+	}
+}

+ 82 - 0
src/main/java/com/icontrols/kakao/common/TYPE.java

@@ -0,0 +1,82 @@
+package com.icontrols.kakao.common;
+
+public final class TYPE {
+	public enum REQ_TYPE {
+		
+		DISCOVER	("Discover", 			"제어 가능한 Thing 목록을 제공한다."),
+		UPDATESTATE	("UpdateState", 		"요청 받은  Thing에 대한 상태를 변경한다."),
+		QUERYSTATE	("QueryState", 			"현재 Thing 상태를 조회한다."),
+		GRANTAUTH	("GrantAuthorization", 	"유저가 연동을 마치면 카카오가 보내주는 타입."),
+		REPORTSTATE	("ReportState", 		"기기의 상태가 변경되어 이벤트를 받았을때 카카오 home에 전달하는 타입");
+		
+		private String typeName;
+		private String desc;
+		
+		private REQ_TYPE(String typeName, String desc) {
+			this.typeName = typeName;
+			this.desc = desc;
+		}
+
+		public String getTypeName() {
+			return typeName;
+		}
+
+		public void setTypeName(String typeName) {
+			this.typeName = typeName;
+		}
+
+		public String getDesc() {
+			return desc;
+		}
+
+		public void setDesc(String desc) {
+			this.desc = desc;
+		}
+		
+	}
+	public enum XML_LIGHT {
+		LIVING_LIGHT(0, "livinglight"), LIGHT01(1, "light01"), LIGHT02(2, "light02"), LIGHT03(3, "light03"),
+		LIGHT04(4, "light04"), LIGHT05(5, "light05"), LIGHT06(6, "light06"), LIGHT07(7, "light07"),
+		LIGHT08(8, "light08");
+
+		private int mIdx;
+		private String mContent;
+
+		private XML_LIGHT(int idx, final String txt) {
+			this.mIdx = idx;
+			this.mContent = txt;
+		}
+
+		public String getContents() {
+			return this.mContent;
+		}
+
+		public int getIndex() {
+			return this.mIdx;
+		}
+	}
+
+	public enum XML_ELECT {
+		ELECTRIC01(1, "electric01"), ELECTRIC02(2, "electric02"), ELECTRIC03(3, "electric03"),
+		ELECTRIC04(4, "electric04"), ELECTRIC05(5, "electric05"), ELECTRIC06(6, "electric06"),
+		ELECTRIC07(7, "electric07"), ELECTRIC08(8, "electric08"), ELECTRIC09(9, "electric09");
+
+		private int mIdx;
+		private String mContent;
+
+		private XML_ELECT(int idx, final String txt) {
+			this.mIdx = idx;
+			this.mContent = txt;
+		}
+
+		public String getContents() {
+			return this.mContent;
+		}
+
+		public int getIndex() {
+			return this.mIdx;
+		}
+
+	}
+
+}

+ 73 - 0
src/main/java/com/icontrols/kakao/common/utils/AES256Util.java

@@ -0,0 +1,73 @@
+package com.icontrols.kakao.common.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.tomcat.util.codec.binary.Base64;
+
+public class AES256Util {
+	private String iv;
+	private Key keySpec;
+
+	/**
+	 * 16자리의 키값을 입력하여 객체를 생성한다.
+	 * 
+	 * @param key 암/복호화를 위한 키값
+	 * @throws UnsupportedEncodingException 키값의 길이가 16이하일 경우 발생
+	 */
+	final static String key = "icontrolsrndcenter";
+
+	public AES256Util() throws UnsupportedEncodingException {
+		this.iv = key.substring(0, 16);
+		byte[] keyBytes = new byte[16];
+		byte[] b = key.getBytes("UTF-8");
+		int len = b.length;
+		if (len > keyBytes.length) {
+			len = keyBytes.length;
+		}
+		System.arraycopy(b, 0, keyBytes, 0, len);
+		SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
+
+		this.keySpec = keySpec;
+	}
+
+	/**
+	 * AES256 으로 암호화 한다.
+	 * 
+	 * @param str 암호화할 문자열
+	 * @return
+	 * @throws NoSuchAlgorithmException
+	 * @throws GeneralSecurityException
+	 * @throws UnsupportedEncodingException
+	 */
+	public String encrypt(String str)
+			throws NoSuchAlgorithmException, GeneralSecurityException, UnsupportedEncodingException {
+		Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
+		c.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes()));
+		byte[] encrypted = c.doFinal(str.getBytes("UTF-8"));
+		String enStr = new String(Base64.encodeBase64(encrypted));
+		return enStr;
+	}
+
+	/**
+	 * AES256으로 암호화된 txt 를 복호화한다.
+	 * 
+	 * @param str 복호화할 문자열
+	 * @return
+	 * @throws NoSuchAlgorithmException
+	 * @throws GeneralSecurityException
+	 * @throws UnsupportedEncodingException
+	 */
+	public String decrypt(String str)
+			throws NoSuchAlgorithmException, GeneralSecurityException, UnsupportedEncodingException {
+		Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
+		c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes()));
+		byte[] byteStr = Base64.decodeBase64(str.getBytes());
+		return new String(c.doFinal(byteStr), "UTF-8");
+	}
+}

+ 14 - 0
src/main/java/com/icontrols/kakao/common/utils/BeanUtils.java

@@ -0,0 +1,14 @@
+package com.icontrols.kakao.common.utils;
+
+import org.springframework.context.ApplicationContext;
+
+import com.icontrols.kakao.config.ApplicationContextProvider;
+
+
+public class BeanUtils {
+	 public static Object getBean(String beanName) {
+	        ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
+	        return applicationContext.getBean(beanName);
+	    }
+
+}

+ 116 - 0
src/main/java/com/icontrols/kakao/common/utils/CommonUtil.java

@@ -0,0 +1,116 @@
+package com.icontrols.kakao.common.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.icontrols.kakao.common.SERVERINFO;
+
+public class CommonUtil {
+	public static boolean isWindows() {
+		String OS = System.getProperty("os.name").toLowerCase();
+
+		if (OS.indexOf("win") >= 0) {
+			return true;
+		}
+		return false;
+	}
+	
+	public static boolean getDevServer() {
+		File checkFile = new File(SERVERINFO.DEV_SERVER);
+
+		if (checkFile.exists()) {
+			SERVERINFO.SendEventURL = SERVERINFO.SendDevEventURL;
+			SERVERINFO.SendEventPort = SERVERINFO.SendDevEventPort;
+			return true;
+		} else
+			return false;
+	}
+	
+	
+	public static boolean getUpdateNoticeNewDanjiServer() {
+		File checkFile = new File(SERVERINFO.UPDATE_NOTICE_KIND);
+
+		if (checkFile.exists())
+			return true;
+		else
+			return false;
+	}
+	
+	/**
+	 * Gas Only One은 왜 그런걸까요오
+	 * 나는 무엇을 위해 존재할 까요??
+	 * @return
+	 */
+	public static boolean isGasOnlyOne() {
+		String checkFileName;
+		
+		checkFileName = CommonUtil.isWindows()?SERVERINFO.WIN_ONLY_ONE_GAS:SERVERINFO.ONLY_ONE_GAS;
+		
+		File checkFile = new File(checkFileName);
+		
+		return checkFile.exists();
+	}
+	
+	public static Map<String, Object> jsonToMap(String jsonString){
+		ObjectMapper mapper = new ObjectMapper();
+		Map<String, Object> map = new HashMap<String, Object>();
+		
+		//Convert JSON string to map
+		try {
+			map = mapper.readValue(jsonString, new TypeReference<Map<String, Object>>(){});
+		} catch (JsonParseException e) {
+			e.printStackTrace();
+		} catch (JsonMappingException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return map;
+	}
+	
+	public static String getStrTypeCurTime() {
+		Date now = new Date();
+		String currentTime = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(now);
+		
+		return currentTime;
+	}
+	
+	public static String getStrTypeCurTimeAfterSec(int seconds) {
+		Date today = new Date();
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(today);
+		cal.add(Calendar.SECOND, + seconds);
+		
+		SimpleDateFormat sdformat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); 
+		String EndTime = sdformat.format(cal.getTime());  
+		
+		return EndTime;
+	}
+	// String 타입의 두 날짜를 비교하기
+	// -1 면 startTime 이 크다.
+	// 0 이면 두 시간은 같다
+	// 1 이면 endTime이 더 크다.
+	public static int compareStringTypeTime(String startTime, String endTime) {
+		try {
+			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
+			Date start_time = dateFormat.parse(startTime);
+			Date end_time = dateFormat.parse(endTime);
+			
+			if(start_time.getTime() < end_time.getTime()) return 1;
+			
+			if(start_time.getTime() > end_time.getTime()) return -1;
+		}catch(Exception e) {
+			return -2;
+		}
+		return 0;
+	}
+}

+ 88 - 0
src/main/java/com/icontrols/kakao/common/utils/JWTUtils2.java

@@ -0,0 +1,88 @@
+package com.icontrols.kakao.common.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import java.util.Base64.Decoder;
+
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+public class JWTUtils2 {
+	private static final Logger logger = LoggerFactory.getLogger(JWTUtils2.class);
+	private static final String ACCESS_TOEKN_SECRET_KEY = "dlswmdaWNvbnRyb2xzMTc5MyFAIw==";
+	
+	public JWTUtils2() {
+		super();
+	}
+
+//	// icontrols1793!@# base64인코딩하고앞에 난수(dlswmd) 붙임
+//	public static String ACCESS_TOEKN_SECRET_KEY = "dlswmdaWNvbnRyb2xzMTc5MyFAIw==";
+//	// icontrolsrefresh1793!@#
+//	public static String REFRESH_TOKEN_SECRET_KEY = "aWNvbnRyb2xzcmVmcmVzaDE3OTMhQCM=";
+
+	/**
+	 * 
+	 * @param token
+	 * @return 
+	 * @throws UnsupportedEncodingException
+	 */
+	public static boolean validateToken(String token) throws UnsupportedEncodingException {
+
+		String secretKey = "";
+
+		secretKey = ACCESS_TOEKN_SECRET_KEY;
+
+		logger.info(token);
+		Decoder decoder = Base64.getDecoder();
+		// 시그니처가 맞는지 확인
+		String enPayload = token.split("\\.")[1];
+		String dePayload = new String(decoder.decode(enPayload));
+		
+		JSONObject obj = new JSONObject(dePayload);
+
+		String jwt = Jwts.builder().claim("exp", obj.get("exp")) // 1시간 -> 설정정보로 변경해야함
+				.claim("cmpIp", obj.get("cmpIp")).claim("homeId", obj.get("homeId")).claim("iat", obj.get("iat"))
+				.signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8")).compact();
+		
+		if (token.equals(jwt)) {
+			long iat = Long.parseLong(obj.get("iat").toString());
+			long currentTime = System.currentTimeMillis() / 1000;
+			if (currentTime > iat + Long.parseLong(obj.get("exp").toString())) {
+				return false;
+			}
+			return true;
+		}
+		return false;
+
+	}
+	/**
+	 * 
+	 * @param token
+	 * @return 동/호
+	 * @throws UnsupportedEncodingException
+	 * @throws NoSuchAlgorithmException
+	 * @throws GeneralSecurityException
+	 */
+
+	public static String getHomeIdFromToken(String token)
+			throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
+
+		Decoder decoder = Base64.getDecoder();
+		String enPayload = token.split("\\.")[1];
+		String dePayload = new String(decoder.decode(enPayload));
+		JSONObject obj = new JSONObject(dePayload);
+
+		String encodedHomeId = obj.get("homeId").toString();
+		AES256Util aes = new AES256Util();
+		String homeId = aes.decrypt(encodedHomeId);
+
+		return homeId;
+	}
+
+}

+ 43 - 0
src/main/java/com/icontrols/kakao/common/utils/UtilLib.java

@@ -0,0 +1,43 @@
+package com.icontrols.kakao.common.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.icontrols.kakao.controller.MainController;
+
+public class UtilLib {
+	private static final Logger logger = LoggerFactory.getLogger(MainController.class);
+
+	// 인코딩하고 뒤에 난수 붙여서 리턴
+	public static String generateDeviceID(String modelId, int dong, int ho, int roomNum, int cirNum)
+			throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
+
+		String result = null;
+
+		String deviceID = dong + "_" + ho + "_" + modelId + "_" + roomNum + "_" + cirNum;
+		AES256Util aes = new AES256Util();
+		String enc = aes.encrypt(deviceID);
+		logger.info(enc);
+		String random = UUID.randomUUID().toString().substring(0, 3);
+		result = random + enc;
+
+		return result;
+	}
+
+	// 디코딩 진행
+	public static String decodeDeviceID(String deviceId)
+			throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
+		String result = null;
+		String dec = deviceId.substring(3, deviceId.length());
+		AES256Util aes = new AES256Util();
+		result = aes.decrypt(dec);
+		// 디코딩진행
+		return result;
+	}
+
+}

+ 18 - 0
src/main/java/com/icontrols/kakao/common/utils/XmlUtil.java

@@ -0,0 +1,18 @@
+package com.icontrols.kakao.common.utils;
+
+import org.w3c.dom.Node;
+
+public class XmlUtil {
+	/**
+	 * XML의 ID로 뽑아낸 노드에서 원하는 속성 key로 값을 리턴한다.
+	 * @param _node
+	 * @param _itemName
+	 * @return
+	 */
+	public static String getAttributeWithItem(Node _node, String _itemName) {
+		if(null == _itemName || _itemName.length() == 0) return null;
+		
+		
+		return _node.getAttributes().getNamedItem(_itemName).getTextContent();
+	}
+}

+ 20 - 0
src/main/java/com/icontrols/kakao/config/ApplicationContextProvider.java

@@ -0,0 +1,20 @@
+package com.icontrols.kakao.config;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ApplicationContextProvider implements ApplicationContextAware {
+private static ApplicationContext applicationContext;
+    
+    @Override
+    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
+        applicationContext = ctx;
+    }
+    
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+}

+ 147 - 0
src/main/java/com/icontrols/kakao/config/AsyncConfig.java

@@ -0,0 +1,147 @@
+package com.icontrols.kakao.config;
+
+import java.util.concurrent.Executor;
+
+import javax.annotation.Resource;
+
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+@Configuration
+@EnableAsync
+public class AsyncConfig implements AsyncConfigurer{
+
+	/** 샘플 기본 Thread 수 */
+    private static int TASK_SAMPLE_CORE_POOL_SIZE = 20;
+    /** 샘플 최대 Thread 수 */
+    private static int TASK_SAMPLE_MAX_POOL_SIZE = 30;
+    /** 샘플 QUEUE 수 */
+    private static int TASK_SAMPLE_QUEUE_CAPACITY = 0;
+    /** 샘플 Thread Bean Name */
+    private static String EXECUTOR_SAMPLE_BEAN_NAME = "wallpadExecutor";
+    /** 샘플 Thread */
+    
+    @Resource(name = "wallpadExecutor")
+    private ThreadPoolTaskExecutor wallpadExecutor;
+
+    @Bean(name = "wallpadExecutor")
+    @Override
+    public Executor getAsyncExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(TASK_SAMPLE_CORE_POOL_SIZE);
+        executor.setMaxPoolSize(TASK_SAMPLE_MAX_POOL_SIZE);
+        executor.setQueueCapacity(TASK_SAMPLE_QUEUE_CAPACITY);
+        executor.setBeanName(EXECUTOR_SAMPLE_BEAN_NAME);
+        executor.initialize();
+        return executor;
+    }
+
+	@Override
+	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+		// TODO Auto-generated method stub
+		return new AsyncExceptionHandler();
+	}
+	
+	/**
+     * 샘플 Thread 등록 가능 여부
+     *
+     * @return 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+     */
+    public boolean isTaskExecute() {
+        boolean rtn = true;
+ 
+        System.out.println("EXECUTOR_SAMPLE.getActiveCount() : " + wallpadExecutor.getActiveCount());
+ 
+        // 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+        if (wallpadExecutor.getActiveCount() >= (TASK_SAMPLE_MAX_POOL_SIZE + TASK_SAMPLE_QUEUE_CAPACITY)) {
+            rtn = false;
+        }
+ 
+        return rtn;
+    }
+ 
+    /**
+     * 샘플 Thread 등록 가능 여부
+     *
+     * @param createCnt : 생성 개수
+     * @return 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크면 false
+     */
+    public boolean isTaskExecute(int createCnt) {
+        boolean rtn = true;
+ 
+        // 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+        if ((wallpadExecutor.getActiveCount() + createCnt) > (TASK_SAMPLE_MAX_POOL_SIZE + TASK_SAMPLE_QUEUE_CAPACITY)) {
+            rtn = false;
+        }
+ 
+        return rtn;
+    }
+    
+    
+    @Resource(name = "eventExecutor")
+    private ThreadPoolTaskExecutor eventExecutor;
+    
+    
+    /** 기본 Thread 수 */
+    private static int EVENT_TASK_CORE_POOL_SIZE = 20;
+    /** 최대 Thread 수 */
+    private static int EVENT_TASK_MAX_POOL_SIZE = 30;
+    /** QUEUE 수 */
+    private static int EVENT_TASK_QUEUE_CAPACITY = 0;
+    /** Thread Bean Name */
+    private static String EVENT_EXECUTOR_BEAN_NAME = "wallpadExecutor";
+    /** Thread */
+    
+    
+    @Bean(name = "eventExecutor")
+    @Qualifier // 타입충돌 방지
+    public Executor taskExecutorEtc() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(EVENT_TASK_CORE_POOL_SIZE);
+        executor.setMaxPoolSize(EVENT_TASK_MAX_POOL_SIZE);
+        executor.setQueueCapacity(EVENT_TASK_QUEUE_CAPACITY);
+        executor.setBeanName(EVENT_EXECUTOR_BEAN_NAME);
+        executor.initialize();
+        return executor;
+    }
+    
+    /**
+     * 샘플 Thread 등록 가능 여부
+     *
+     * @return 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+     */
+    public boolean isEventTaskExecute() {
+        boolean rtn = true;
+ 
+        System.out.println("eventExecutor.getActiveCount() : " + eventExecutor.getActiveCount());
+ 
+        // 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+        if (eventExecutor.getActiveCount() >= (EVENT_TASK_MAX_POOL_SIZE + EVENT_TASK_QUEUE_CAPACITY)) {
+            rtn = false;
+        }
+ 
+        return rtn;
+    }
+ 
+    /**
+     * 샘플 Thread 등록 가능 여부
+     *
+     * @param createCnt : 생성 개수
+     * @return 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크면 false
+     */
+    public boolean isEventTaskExecute(int createCnt) {
+        boolean rtn = true;
+ 
+        // 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+        if ((eventExecutor.getActiveCount() + createCnt) > (EVENT_TASK_MAX_POOL_SIZE + EVENT_TASK_QUEUE_CAPACITY)) {
+            rtn = false;
+        }
+ 
+        return rtn;
+    }
+}

+ 85 - 0
src/main/java/com/icontrols/kakao/config/AsyncConfigReportState.java

@@ -0,0 +1,85 @@
+package com.icontrols.kakao.config;
+
+import java.util.concurrent.Executor;
+
+import javax.annotation.Resource;
+
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+@Configuration
+@EnableAsync
+public class AsyncConfigReportState implements AsyncConfigurer{
+
+	/** 기본 Thread 수 */
+    private static int EVENT_TASK_CORE_POOL_SIZE = 20;
+    /** 최대 Thread 수 */
+    private static int EVENT_TASK_MAX_POOL_SIZE = 30;
+    /** QUEUE 수 */
+    private static int EVENT_TASK_QUEUE_CAPACITY = 0;
+    /** Thread Bean Name */
+    private static String EVENT_EXECUTOR_BEAN_NAME = "wallpadExecutor";
+    /** Thread */
+
+    @Resource(name = "eventExecutor")
+    private ThreadPoolTaskExecutor eventExecutor;
+
+
+    @Bean(name = "eventExecutor")
+    @Override
+    public Executor getAsyncExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(EVENT_TASK_CORE_POOL_SIZE);
+        executor.setMaxPoolSize(EVENT_TASK_MAX_POOL_SIZE);
+        executor.setQueueCapacity(EVENT_TASK_QUEUE_CAPACITY);
+        executor.setBeanName(EVENT_EXECUTOR_BEAN_NAME);
+        executor.initialize();
+        return executor;
+    }
+
+	@Override
+	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+		// TODO Auto-generated method stub
+		return new AsyncExceptionHandler();
+	}
+	
+	/**
+     * 샘플 Thread 등록 가능 여부
+     *
+     * @return 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+     */
+    public boolean isEventTaskExecute() {
+        boolean rtn = true;
+ 
+        System.out.println("eventExecutor.getActiveCount() : " + eventExecutor.getActiveCount());
+ 
+        // 실행중인 task 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+        if (eventExecutor.getActiveCount() >= (EVENT_TASK_MAX_POOL_SIZE + EVENT_TASK_QUEUE_CAPACITY)) {
+            rtn = false;
+        }
+ 
+        return rtn;
+    }
+ 
+    /**
+     * 샘플 Thread 등록 가능 여부
+     *
+     * @param createCnt : 생성 개수
+     * @return 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크면 false
+     */
+    public boolean isEventTaskExecute(int createCnt) {
+        boolean rtn = true;
+ 
+        // 실행중인 task 개수 + 실행할 개수가 최대 개수(max + queue)보다 크거나 같으면 false
+        if ((eventExecutor.getActiveCount() + createCnt) > (EVENT_TASK_MAX_POOL_SIZE + EVENT_TASK_QUEUE_CAPACITY)) {
+            rtn = false;
+        }
+ 
+        return rtn;
+    }
+
+}

+ 23 - 0
src/main/java/com/icontrols/kakao/config/AsyncExceptionHandler.java

@@ -0,0 +1,23 @@
+package com.icontrols.kakao.config;
+
+import java.lang.reflect.Method;
+
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+
+public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
+
+	@Override
+	public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
+		System.out.println("==============>>>>>>>>>>>> THREAD ERROR");
+        System.out.println("Exception Message :: " + throwable.getMessage());
+        System.out.println("Method Name :: " + method.getName());
+        for (Object param : obj) {
+            System.out.println("Parameter Value :: " + param);
+        }
+        
+        // JOB_LOG : 종료 입력
+        // ...
+        System.out.println("==============>>>>>>>>>>>> THREAD ERROR END");
+	}
+
+}

+ 24 - 0
src/main/java/com/icontrols/kakao/config/DanziConfig.java

@@ -0,0 +1,24 @@
+package com.icontrols.kakao.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@EnableWebMvc
+public class DanziConfig implements WebMvcConfigurer {
+
+	@Bean
+	public DanziTokenInterceptor createInterceptor() { return new DanziTokenInterceptor(); }
+	
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		// TODO Auto-generated method stub
+		WebMvcConfigurer.super.addInterceptors(registry);
+		registry.addInterceptor(createInterceptor()).addPathPatterns("/kakao/*");
+	}
+
+ 
+}

+ 32 - 0
src/main/java/com/icontrols/kakao/config/DanziFilter.java

@@ -0,0 +1,32 @@
+package com.icontrols.kakao.config;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class DanziFilter implements Filter{
+	private final static Logger logger = LoggerFactory.getLogger(DanziFilter.class);
+	@Override
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+			throws IOException, ServletException {
+		
+		logger.info("[Request Access]");
+		
+		HttpServletResponse res = (HttpServletResponse) response;
+		
+		logger.debug("[Request Header Names] - " + res.getHeaderNames().toString());
+		chain.doFilter(request, response);
+	}
+
+}

+ 40 - 0
src/main/java/com/icontrols/kakao/config/DanziTokenInterceptor.java

@@ -0,0 +1,40 @@
+package com.icontrols.kakao.config;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+
+public class DanziTokenInterceptor implements HandlerInterceptor {
+	
+	private final static Logger logger = LoggerFactory.getLogger(DanziTokenInterceptor.class);
+	
+	private final static String HEADER_AUTH = "Authorization";
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+			throws Exception {
+		logger.info("[Interceptor preHandle] - " + request.getHeader(HEADER_AUTH));
+		
+		
+		return HandlerInterceptor.super.preHandle(request, response, handler);
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+			ModelAndView modelAndView) throws Exception {
+		// TODO Auto-generated method stub
+		HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
+	}
+
+	@Override
+	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
+			throws Exception {
+		// TODO Auto-generated method stub
+		HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
+	}
+
+}

+ 43 - 0
src/main/java/com/icontrols/kakao/controller/AuthController.java

@@ -0,0 +1,43 @@
+package com.icontrols.kakao.controller;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.icontrols.kakao.service.AuthService;
+
+
+@RestController
+public class AuthController {
+	private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
+
+	@Autowired
+	AuthService authService;
+	
+	@RequestMapping(value = "/kakao/auth", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> reqCode(@RequestBody Map<String, Object> body) throws Exception {
+		logger.info(body.toString());
+		Map<String, Object> retMap = authService.authProcess(body);
+		
+		return retMap;
+	}
+	
+	@RequestMapping(value = "/kakao/test", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> test(@RequestBody Map<String, Object> body) throws Exception {
+		logger.info("kakao test --<>>>>>");
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		
+		
+		retMap.put("UUID", UUID.randomUUID().toString());
+		return retMap;
+	}
+}

+ 106 - 0
src/main/java/com/icontrols/kakao/controller/KakaoDanziController.java

@@ -0,0 +1,106 @@
+package com.icontrols.kakao.controller;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.icontrols.kakao.common.KAKAO_RESULT;
+import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.TYPE;
+import com.icontrols.kakao.common.utils.JWTUtils2;
+import com.icontrols.kakao.service.KakaoExtensionService;
+
+@RestController
+public class KakaoDanziController {
+	
+	private static final Logger logger = LoggerFactory.getLogger(KakaoDanziController.class);
+	
+	@Autowired
+	KakaoExtensionService kakaoService;
+	
+	/**
+	 * 중계 서버에서 오는 요청을         분기 처리를 진행한다.
+	 * @param header
+	 * @return
+	 */
+	@RequestMapping(value = "/kakao/danzi", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> getRequest(
+														@RequestHeader HttpHeaders header,
+														@RequestBody Map<String, Object> params,
+														HttpServletResponse response, 
+														HttpServletRequest request
+														) throws Exception{
+		logger.debug("KAKAO/DANZI %s"+ params.toString());
+		
+		logger.debug("dong / ho request --> " + JWTUtils2.getHomeIdFromToken((String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION)));
+		
+		// 인증은 여기서 해야하나??  Interceptor에서 해야하나?? 일단 그부분은 고민을 해보자.
+		if(!params.containsKey(KEY_INFO.HTTP_KEY_AUTHORIZATION)) {
+			logger.error("[ERROR] - 인증토큰 존재하지 않음");
+			
+			params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_TOKEN));
+			return params;
+		}
+		
+		if(!JWTUtils2.validateToken((String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION))) {
+			logger.error("[ERROR] - 인 증 토 큰 만 료");
+			logger.error("[ERROR] - " + (String)params.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
+			
+			params.remove(KEY_INFO.HTTP_KEY_AUTHORIZATION);
+			if(params.containsKey(KEY_INFO.HTTP_KEY_BODY)) params.remove(KEY_INFO.HTTP_KEY_BODY);
+			
+			params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+			
+			return params;
+		}
+		
+		// 받은 요청에 대해 반환 해주는 결과 JSON 객체
+		Map<String, Object> retMap = null;
+		
+		try {
+			//Request Parameter에서  type값을 통해 분기를 태운다. 
+			String reqType = (params.containsKey(KEY_INFO.HTTP_KEY_TYPE))?(String) params.get(KEY_INFO.HTTP_KEY_TYPE):"";
+			logger.info("[REQUEST INBOUD] reqType = " + reqType);
+			
+			if(reqType.equals(TYPE.REQ_TYPE.GRANTAUTH.getTypeName())) {
+				// 카카오 홈과 유저의 인증 완료시 카카오에서 전송해주는 API TYPE
+				// 건내받은 토큰 정보를 DB에 저장해야 한다.
+				retMap = kakaoService.request_grantAuthorization(params);
+			} else if(reqType.equals(TYPE.REQ_TYPE.DISCOVER.getTypeName())) {
+				// 월패드와 연결된 기기목록들을 요청하는 API TYPE
+				retMap = kakaoService.request_discover(params);
+			} else if(reqType.equals(TYPE.REQ_TYPE.QUERYSTATE.getTypeName())) {
+				// 월패드와 연결된 기기중 endpointIds에 포함된 기기의 상태값을 확인한다.
+				retMap = kakaoService.request_queryState(params);
+			} else if(reqType.equals(TYPE.REQ_TYPE.UPDATESTATE.getTypeName())) {
+				// 월패드와 연결된 기기의 상태를 번경한다. 
+				retMap = kakaoService.request_updateState(params);
+			}else {
+				// 정의되지않은 type이 들어왔을경우.
+				params.remove(KEY_INFO.HTTP_KEY_BODY);
+				params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_REQ));
+				retMap = params;
+			}
+		}catch(Exception e) {
+			logger.error("[Unknown ERROR]");
+			e.printStackTrace();
+			retMap = KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL);
+		}finally {
+			retMap.remove(KEY_INFO.HTTP_KEY_AUTHORIZATION);
+		}
+
+		return retMap;
+	}
+}

+ 166 - 0
src/main/java/com/icontrols/kakao/controller/MainController.java

@@ -0,0 +1,166 @@
+package com.icontrols.kakao.controller;
+
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import javax.annotation.Resource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.icontrols.kakao.common.KAKAO_RESULT;
+import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.utils.CommonUtil;
+import com.icontrols.kakao.service.AuthService;
+import com.icontrols.kakao.service.KakaoExtensionService;
+import com.icontrols.kakao.socket.wallpad.KakaoMsgHandler;
+
+@RestController
+public class MainController {
+
+	private static final Logger logger = LoggerFactory.getLogger(MainController.class);
+	
+	@Resource( name = "kakaoMsgHandler")
+	KakaoMsgHandler kakaoMsgHandler;
+	
+	@Autowired
+	AuthService authService;
+	
+	@Autowired
+	KakaoExtensionService service;
+	
+	@RequestMapping(value = "/async", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> asyncTest(@RequestBody Map<String, Object> param){
+		
+		try {
+			service.asyncTest(param);
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		
+		return param;
+	}
+	
+	@RequestMapping(value = "/test", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> recieveTest(@RequestHeader HttpHeaders header,
+														@RequestBody Map<String, Object> params) throws Exception {
+		
+		logger.debug("kakaoMsgHandler is null" + kakaoMsgHandler);
+		logger.info("===================================================================================");
+		logger.info("==========================================이벤트 수신=================================");
+		logger.info("===================================================================================");
+		logger.info("body :" + params.toString());
+
+		Map<String,Object> retMap = new HashMap<String, Object>();
+		
+		retMap.put(KEY_INFO.HTTP_KEY_ID, params.get(KEY_INFO.HTTP_KEY_ID));
+		retMap.put(KEY_INFO.HTTP_KEY_TYPE, params.get(KEY_INFO.HTTP_KEY_TYPE));
+		retMap.put(KEY_INFO.HTTP_KEY_VERSION, params.get(KEY_INFO.HTTP_KEY_VERSION));
+		
+		String jsonString = "{\"name\":\"mkyong\", \"age\":29}";
+		Map<String, Object> testmap = CommonUtil.jsonToMap(jsonString);
+		
+		logger.debug("--------->>>>>  "+testmap.get("name").toString());
+
+		retMap.putAll(KakaoMsgHandler.getDeviceCapabilities("light"));
+		
+		
+		String encode = Base64.getEncoder().encodeToString("client-1:client-1-secret".getBytes());
+		
+		logger.debug("____________" + encode);
+		
+		return retMap;
+	}
+	
+	@RequestMapping(value = "/events", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> recieveEvents(@RequestHeader HttpHeaders header,
+														@RequestBody Map<String, Object> params) throws Exception {
+		
+		logger.info("===================================================================================");
+		logger.info("==========================================이벤트 수신=================================");
+		logger.info("===================================================================================");
+		logger.info("header" + header.toString());
+		logger.info("header access = " + header.getOrigin());
+		logger.info("body :" + params.toString());
+
+		Map<String,Object> retMap = new HashMap<String, Object>();
+		
+		retMap.put(KEY_INFO.HTTP_KEY_ID, params.get(KEY_INFO.HTTP_KEY_ID));
+		retMap.put(KEY_INFO.HTTP_KEY_TYPE, params.get(KEY_INFO.HTTP_KEY_TYPE));
+		retMap.put(KEY_INFO.HTTP_KEY_VERSION, params.get(KEY_INFO.HTTP_KEY_VERSION));
+		
+		Random rd = new Random();
+		
+		int rdNum = rd.nextInt(10);
+		if(rdNum % 2 == 0) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_EXPIREDTOKEN));
+		}
+		return retMap;
+	}
+	
+	@RequestMapping(value = "/events/token", method = RequestMethod.POST)
+	public @ResponseBody Map<String, Object> recieveEventsToken(@RequestHeader HttpHeaders header,
+														@RequestBody(required = false) String grant_type,
+														@RequestBody(required = false) String refresh_token) throws Exception {
+		
+		logger.info("===================================================================================");
+		logger.info("==========================================토큰 재생성=================================");
+		logger.info("===================================================================================");
+		logger.info("header" + header.toString());
+		logger.info("header access = " + header.getOrigin());
+		logger.info("granttype = " + grant_type);
+		logger.info("refresh token = " + refresh_token);
+
+		Map<String,Object> retMap = new HashMap<String, Object>();
+		
+		retMap.put("access_token", "refresh_access_token");
+		retMap.put("refresh_token", "refresh_token_token_token");
+		retMap.put("expires_in", 3600);
+		retMap.put("token_type", "bearer");
+		
+		return retMap;
+	}
+	
+	@RequestMapping(value = "/subs", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> subscription(@RequestHeader HttpHeaders header,
+														@RequestBody Map<String, Object> params) throws Exception {
+
+		logger.info("===================================================================================");
+		logger.info("================================기-기-구-독-/-해-제===================================");
+		logger.info("===================================================================================");
+		logger.info("body :" + params.toString());
+
+		/*
+		 *  type -> subs/unSubs
+		 *  dong -> 802
+		 *  ho   -> 1103
+		 *  company -> kakao 
+		 *  int dong = Integer.parseInt((String)param.get("dong"));
+			int ho = Integer.parseInt((String)param.get("ho"));
+			
+			String type = (String) param.get("type");
+			String company = (String) param.get("company");
+			
+			int devNum = Integer.parseInt((String)param.get("devNum"));
+		 */
+		Map<String,Object> retMap = new HashMap<String, Object>();
+		authService.subscriptionProcess(params);
+		
+		retMap.put(KEY_INFO.HTTP_KEY_ID, params.get(KEY_INFO.HTTP_KEY_ID));
+		retMap.put(KEY_INFO.HTTP_KEY_TYPE, params.get(KEY_INFO.HTTP_KEY_TYPE));
+		retMap.put(KEY_INFO.HTTP_KEY_VERSION, params.get(KEY_INFO.HTTP_KEY_VERSION));
+		
+		return retMap;
+	}
+}

+ 133 - 0
src/main/java/com/icontrols/kakao/entity/DonghoInfo.java

@@ -0,0 +1,133 @@
+package com.icontrols.kakao.entity;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "tbl_dongho_info")
+public class DonghoInfo implements Serializable{
+
+	private static final long serialVersionUID = 4832595590796050945L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.AUTO)
+	private long id;
+	
+	@Column(name = "dong")
+	private String dong;
+	
+	@Column(name = "ho")
+	private String ho;
+	
+	@Column(name = "kakao_access_token")
+	private String kakaoAccessToken;
+	
+	@Column(name = "kakao_refresh_token")
+	private String kakaoRefreshToken;
+	
+	@Column(name = "kakao_expires_time")
+	private String kakaoExpiresTime;
+
+	@Column(name = "kakao_token_create_time")
+	private String kakaoTokenCreateTime;
+	
+	@Column(name = "kakao_token_expire_time")
+	private String kakaoTokenExpireTime;
+	
+	public DonghoInfo() {
+		super();
+		// TODO Auto-generated constructor stub
+	}
+
+	public DonghoInfo(String dong, String ho, String kakaoAccessToken, String kakaoRefreshToken,
+			String kakaoExpiresTime, String kakaoTokenCreateTime, String kakaoTokenExpireTime) {
+		super();
+		this.dong = dong;
+		this.ho = ho;
+		this.kakaoAccessToken = kakaoAccessToken;
+		this.kakaoRefreshToken = kakaoRefreshToken;
+		this.kakaoExpiresTime = kakaoExpiresTime;
+		this.kakaoTokenCreateTime = kakaoTokenCreateTime;
+		this.kakaoTokenExpireTime = kakaoTokenExpireTime;
+	}
+
+
+
+	public long getId() {
+		return id;
+	}
+
+	public void setId(long id) {
+		this.id = id;
+	}
+
+	public String getDong() {
+		return dong;
+	}
+
+	public void setDong(String dong) {
+		this.dong = dong;
+	}
+
+	public String getHo() {
+		return ho;
+	}
+
+	public void setHo(String ho) {
+		this.ho = ho;
+	}
+
+	public String getKakaoAccessToken() {
+		return kakaoAccessToken;
+	}
+
+	public void setKakaoAccessToken(String kakaoAccessToken) {
+		this.kakaoAccessToken = kakaoAccessToken;
+	}
+
+	public String getKakaoRefreshToken() {
+		return kakaoRefreshToken;
+	}
+
+	public void setKakaoRefreshToken(String kakaoRefreshToken) {
+		this.kakaoRefreshToken = kakaoRefreshToken;
+	}
+
+	public String getKakaoExpiresTime() {
+		return kakaoExpiresTime;
+	}
+
+	public void setKakaoExpiresTime(String kakaoExpiresTime) {
+		this.kakaoExpiresTime = kakaoExpiresTime;
+	}
+	
+
+	public String getKakaoTokenCreateTime() {
+		return kakaoTokenCreateTime;
+	}
+
+	public void setKakaoTokenCreateTime(String kakaoTokenCreateTime) {
+		this.kakaoTokenCreateTime = kakaoTokenCreateTime;
+	}
+
+	public String getKakaoTokenExpireTime() {
+		return kakaoTokenExpireTime;
+	}
+
+	public void setKakaoTokenExpireTime(String kakaoTokenExpireTime) {
+		this.kakaoTokenExpireTime = kakaoTokenExpireTime;
+	}
+
+	@Override
+	public String toString() {
+		return "DognhoInfo [id=" + id + ", dong=" + dong + ", ho=" + ho + ", kakaoAccessToken=" + kakaoAccessToken
+				+ ", kakaoRefreshToken=" + kakaoRefreshToken + ", kakaoExpiresTime=" + kakaoExpiresTime + "]";
+	}
+	
+}

+ 12 - 0
src/main/java/com/icontrols/kakao/repo/DonghoInfoRepository.java

@@ -0,0 +1,12 @@
+package com.icontrols.kakao.repo;
+
+import java.util.List;
+
+import org.springframework.data.repository.CrudRepository;
+
+import com.icontrols.kakao.entity.DonghoInfo;
+
+public interface DonghoInfoRepository extends CrudRepository<DonghoInfo, Long> {
+	List<DonghoInfo> findByDongAndHo(String dong, String ho);
+	List<DonghoInfo> findByKakaoAccessTokenAndKakaoRefreshToken(String kakaoAccessToken, String kakaoRefreshToken);
+}

+ 8 - 0
src/main/java/com/icontrols/kakao/service/AuthService.java

@@ -0,0 +1,8 @@
+package com.icontrols.kakao.service;
+
+import java.util.Map;
+
+public interface AuthService {
+	public Map<String, Object> authProcess(Map<String, Object> param) throws Exception;
+	public Map<String, Object> subscriptionProcess(Map<String, Object> param) throws Exception;
+}

+ 15 - 0
src/main/java/com/icontrols/kakao/service/KakaoExtensionService.java

@@ -0,0 +1,15 @@
+package com.icontrols.kakao.service;
+
+import java.util.Map;
+
+public interface KakaoExtensionService {
+	public Map<String, Object> asyncTest(Map<String, Object> param) throws Exception;
+	
+	public Map<String, Object> request_grantAuthorization(Map<String, Object> param) throws Exception;
+	
+	public Map<String, Object> request_discover(Map<String, Object> param) throws Exception;
+	
+	public Map<String, Object> request_queryState(Map<String, Object> param) throws Exception;
+	
+	public Map<String, Object> request_updateState(Map<String, Object> param) throws Exception;
+}

+ 29 - 0
src/main/java/com/icontrols/kakao/service/impl/AuthServiceImpl.java

@@ -0,0 +1,29 @@
+package com.icontrols.kakao.service.impl;
+
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.springframework.stereotype.Service;
+
+import com.icontrols.kakao.service.AuthService;
+import com.icontrols.kakao.socket.wallpad.KakaoMsgHandler;
+
+@Service
+public class AuthServiceImpl implements AuthService {
+	@Resource(name="kakaoMsgHandler")
+	KakaoMsgHandler kMsgHandler;
+	
+	@Override
+	public Map<String, Object> authProcess(Map<String, Object> param) throws Exception {
+		
+		return kMsgHandler.doAuthProcess(param);
+	}
+
+	@Override
+	public Map<String, Object> subscriptionProcess(Map<String, Object> param) throws Exception {
+		// TODO Auto-generated method stub
+		return kMsgHandler.subscriptionProcess(param);
+	}
+
+}

+ 265 - 0
src/main/java/com/icontrols/kakao/service/impl/KakaoExtensionServiceImpl.java

@@ -0,0 +1,265 @@
+package com.icontrols.kakao.service.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import javax.annotation.Resource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.icontrols.kakao.common.KAKAO_RESULT;
+import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.utils.CommonUtil;
+import com.icontrols.kakao.common.utils.JWTUtils2;
+import com.icontrols.kakao.config.AsyncConfig;
+import com.icontrols.kakao.entity.DonghoInfo;
+import com.icontrols.kakao.repo.DonghoInfoRepository;
+import com.icontrols.kakao.service.KakaoExtensionService;
+import com.icontrols.kakao.socket.wallpad.KakaoMsgHandler;
+import com.icontrols.kakao.socket.wallpad.WallpadMsgSender;
+
+@Service
+public class KakaoExtensionServiceImpl implements KakaoExtensionService {
+	private final Logger logger = LoggerFactory.getLogger(KakaoExtensionService.class);
+	
+	@Resource(name="asyncConfig")
+	private AsyncConfig asyncConfig;
+	
+	@Resource(name="kakaoMsgHandler")
+	KakaoMsgHandler kMsgHandler;
+	
+	@Resource(name="wallpadMsgSender")
+	WallpadMsgSender wallpadMsgSender;
+	
+	@Autowired
+	DonghoInfoRepository donghoRepository;
+	@Override
+	public Map<String, Object> asyncTest(Map<String, Object> param) throws Exception {
+
+//		List<DonghoInfo> dList = donghoRepo.getDetailInfoByDongHo("108", "1103");
+		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
+		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
+		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
+		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
+		
+//		logger.debug(dList.get(0).toString());
+		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
+		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
+		logger.debug("!!!!!!!!!!!!!!!!!!!!!!!:");
+		
+		//async 1
+		logger.debug("is able = " + asyncConfig.isTaskExecute());
+		Map<String, Object> param1 = new HashMap<String, Object>();
+		param1.put("name", "async 1");
+		param1.put("time", (long)4000);
+		
+		
+		//async 2
+		logger.debug("is able = " + asyncConfig.isTaskExecute());
+		Map<String, Object> param2 = new HashMap<String, Object>();
+		param2.put("name", "async 2");
+		param2.put("time", (long)2000);
+		
+		//async 3
+		logger.debug("is able = " + asyncConfig.isTaskExecute());
+		Map<String, Object> param3 = new HashMap<String, Object>();
+		param3.put("name", "async 3");
+		param3.put("time", (long)6000);
+		//async 4
+		logger.debug("is able = " + asyncConfig.isTaskExecute());
+		Map<String, Object> param4 = new HashMap<String, Object>();
+		param4.put("name", "async 4");
+		param4.put("time", (long)3000);
+		
+		//async 5
+		logger.debug("is able = " + asyncConfig.isTaskExecute());
+		Map<String, Object> param5 = new HashMap<String, Object>();
+		param5.put("name", "async 5");
+		param5.put("time", (long)1000);
+		
+		CompletableFuture<Map<String, Object>> future1 = wallpadMsgSender.getAsyncTest(param1);
+		CompletableFuture<Map<String, Object>> future2 = wallpadMsgSender.getAsyncTest(param2);
+		CompletableFuture<Map<String, Object>> future3 = wallpadMsgSender.getAsyncTest(param3);
+		CompletableFuture<Map<String, Object>> future4 = wallpadMsgSender.getAsyncTest(param4);
+		CompletableFuture<Map<String, Object>> future5 = wallpadMsgSender.getAsyncTest(param5);
+
+		String args[] = new String[5];
+		List<CompletableFuture<?>> fList = new ArrayList<>();
+		
+		fList.add(future1);
+		fList.add(future2);
+		fList.add(future3);
+		fList.add(future4);
+		fList.add(future5);
+		
+		wallpadMsgSender.getAsyncTest(param5).thenComposeAsync(wallpadMsgSender::getAsyncTest);
+//		CompletableFuture.allOf(future1, future2, future3, future4, future5).join();
+		CompletableFuture.allOf(fList.toArray(new CompletableFuture[fList.size()])).join();
+		
+		Map<String, Object> tmpMap = (Map<String, Object>)future1.get();
+		logger.debug("future1 = " + tmpMap.get("result") + "END");
+		
+		tmpMap = (Map<String, Object>)future2.get();
+		logger.debug("future2 = " + tmpMap.get("result") + "END");
+
+		tmpMap = (Map<String, Object>)future3.get();
+		logger.debug("future3 = " + tmpMap.get("result") + "END");
+		
+		tmpMap = (Map<String, Object>)future4.get();
+		logger.debug("future4 = " + tmpMap.get("result") + "END");
+		
+		tmpMap = (Map<String, Object>)future5.get();
+		logger.debug("future5 = " + tmpMap.get("result") + "END");
+		return null;
+	}
+	
+	@Override
+	public Map<String, Object> request_grantAuthorization(Map<String, Object> param) throws Exception {
+		String dong_ho = JWTUtils2.getHomeIdFromToken((String)param.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
+//		int dong = Integer.parseInt(dong_ho.split("/")[0]);
+//		int ho = Integer.parseInt(dong_ho.split("/")[1]);
+		
+		int dong = 802;
+		int ho = 1103;
+		
+		Map<String, Object> body = (HashMap)param.get(KEY_INFO.HTTP_KEY_BODY);
+		
+		try {
+			DonghoInfo donghoInfo = new DonghoInfo(
+					Integer.toString(dong), 
+					Integer.toString(ho), 
+					(String) body.get(KEY_INFO.BODY_KEY_ACCESS_TOKEN),
+					(String) body.get(KEY_INFO.BODY_KEY_REFRESH_TOKEN),
+					Integer.toString((int)body.get(KEY_INFO.BODY_KEY_EXPIRES_IN)),
+					CommonUtil.getStrTypeCurTime(),
+					CommonUtil.getStrTypeCurTimeAfterSec((int)body.get(KEY_INFO.BODY_KEY_EXPIRES_IN)));
+			
+//			List<DonghoInfo> dList = donghoRepository.findByDongAndHo(donghoInfo.getDong(), donghoInfo.getHo());
+			List<DonghoInfo> dList = donghoRepository.findByKakaoAccessTokenAndKakaoRefreshToken(
+																									(String) body.get(KEY_INFO.BODY_KEY_ACCESS_TOKEN), 
+																									(String) body.get(KEY_INFO.BODY_KEY_REFRESH_TOKEN)
+																								);
+			
+			if(dList.size() > 0)
+				donghoInfo.setId(dList.get(0).getId());
+
+			donghoRepository.save(donghoInfo);
+			
+			logger.debug("dongho info saved complete " );
+			logger.debug(donghoInfo.toString());
+			
+		}catch(Exception e) {
+			logger.error("[Auth Database Insert Error]");
+			e.printStackTrace();
+			
+			param.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+		}finally {
+			param.remove(KEY_INFO.HTTP_KEY_BODY);
+		}
+		return param;
+	}
+	
+	@Override
+	public Map<String, Object> request_discover(Map<String, Object> param) throws Exception{
+		String dong_ho = JWTUtils2.getHomeIdFromToken((String)param.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
+//		int dong = Integer.parseInt(dong_ho.split("/")[0]);
+//		int ho = Integer.parseInt(dong_ho.split("/")[1]);
+		
+		int dong = 802;
+		int ho = 1103;
+		
+		Map<String, Object> listMap = kMsgHandler.getDevicesStatusList(dong, ho);
+		param.putAll(listMap);
+		return param;
+	}
+	
+	@Override
+	public Map<String, Object> request_queryState(Map<String, Object> param) throws Exception {
+		String dong_ho = JWTUtils2.getHomeIdFromToken((String)param.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
+//		int dong = Integer.parseInt(dong_ho.split("/")[0]);
+//		int ho = Integer.parseInt(dong_ho.split("/")[1]);
+		
+		int dong = 802;
+		int ho = 1103;
+		Map <String, Object> body = (HashMap)param.get(KEY_INFO.HTTP_KEY_BODY);
+		
+		List<String> endpointIds = (ArrayList)body.get(KEY_INFO.BODY_KEY_ENDPOINT_IDS);
+		List<String> unreachableIds = new ArrayList<String>();
+		
+		List<CompletableFuture<Map<String, Object>>> fList = new ArrayList<>();
+		
+		body.clear();
+		for(String endpointId:endpointIds) {
+			while(true) {
+    			if(asyncConfig.isTaskExecute()) {
+    				fList.add(wallpadMsgSender.getDeviceStatus(dong, ho, endpointId));
+    				break;
+    			}
+    			Thread.sleep(1000);
+    		}
+		}
+		
+		
+		CompletableFuture.allOf(fList.toArray(new CompletableFuture[fList.size()])).join();
+		
+		logger.debug("******************************************************************************");
+        logger.debug("								ASYNC D END");
+        logger.debug("******************************************************************************");
+		
+		for(CompletableFuture<Map<String, Object>> future:fList) {
+        	Map<String, Object> retMap = future.get();
+        	
+        	String endpointId = (String)retMap.get(KEY_INFO.BODY_KEY_ENDPOINT_ID);
+        	logger.debug(endpointId + "-------->" + retMap.toString());
+        	
+        	if(retMap.containsKey(KEY_INFO.HTTP_KEY_ERROR)) {
+				unreachableIds.add(endpointId);
+				
+				HashMap<String, Object> tmpMap = new HashMap<String, Object>();
+				tmpMap.put("power", "off");
+				
+				retMap.clear();
+				retMap.put(endpointId, tmpMap);
+			}
+        	
+        	retMap.remove(KEY_INFO.BODY_KEY_ENDPOINT_ID);
+        	
+        	body.putAll(retMap);
+        }
+		
+		param.put(KEY_INFO.HTTP_KEY_UNREACHABLEIDS, unreachableIds);
+		param.put(KEY_INFO.HTTP_KEY_BODY, body);
+		
+		return param;
+	}
+	
+	
+	@Override
+	public Map<String, Object> request_updateState(Map<String, Object> param) throws Exception {
+		String dong_ho = JWTUtils2.getHomeIdFromToken((String)param.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
+//		int dong = Integer.parseInt(dong_ho.split("/")[0]);
+//		int ho = Integer.parseInt(dong_ho.split("/")[1]);
+		
+		int dong = 802;
+		int ho = 1103;
+		
+		@SuppressWarnings("unchecked")
+		Map <String, Object> body = (HashMap<String, Object>)param.get(KEY_INFO.HTTP_KEY_BODY);
+		
+		@SuppressWarnings("unchecked")
+		Map<String, Object> desired = (HashMap<String, Object>)body.get(KEY_INFO.BODY_KEY_DESIRED);
+		
+		String endpointId = (String)body.get(KEY_INFO.BODY_KEY_ENDPOINT_ID);
+		String capabilityName = (String)body.get(KEY_INFO.BODY_KEY_CAPABILITY_NAME);
+		
+		param.put(KEY_INFO.HTTP_KEY_BODY, kMsgHandler.sendDeviceAction(dong, ho, endpointId, capabilityName, desired));
+		return param;
+	}
+}

+ 35 - 0
src/main/java/com/icontrols/kakao/socket/utils/IPGenerator.java

@@ -0,0 +1,35 @@
+package com.icontrols.kakao.socket.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IPGenerator {
+	private static final Logger logger = LoggerFactory.getLogger(IPGenerator.class);
+
+	public static boolean checkDongHo(String Dong, String Ho) {
+		try {
+			Integer.parseInt(Dong);
+			Integer.parseInt(Ho);
+
+			// 범위 검사
+
+		} catch (Exception e) {
+			logger.error("message : " + e);
+			return false;
+		}
+		return true;
+	}
+
+	public static String getIPFromDongHo(int Dong, int Ho) {
+		String strIP = null;
+		int strIP_2 = Dong % 100;
+		int strIP_3 = Ho / 100;
+		int strIP_4 = Ho % 100;
+		// int strIP_4 = Integer.parseInt(Ho)%100;
+
+		strIP = "10." + strIP_2 + "." + strIP_3 + "." + strIP_4;
+		// System.out.println( strIP );
+		//
+		return strIP;
+	}
+}

+ 244 - 0
src/main/java/com/icontrols/kakao/socket/utils/SSLClient.java

@@ -0,0 +1,244 @@
+package com.icontrols.kakao.socket.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @description SSL Client 용 소켓 생성을 지원하는 api 이다.
+ *
+ * @import kr.co.icontrols.sslapi.SslClient
+ */
+public class SSLClient {
+	private static final Logger logger = LoggerFactory.getLogger(SSLClient.class);
+
+	private Socket sslSocket = null;
+	private byte[] ServerIP = null;
+
+	/**
+	 * SSL 암호화된 Socket 을 생성한다. (client 전용)
+	 *
+	 * @param host - 소켓에 연결되어야 하는 호스트 주소
+	 * @param port - 소켓이 접속되고있는 호스트의 포트 번호
+	 *
+	 *             <br>
+	 *             <br>
+	 * @Example
+	 * 
+	 *          <pre class="prettyprint">
+	 *          // SslClient 정의
+	 *          SslClient sslClientSocket = new SslClient("10.0.1.1", 10000);
+	 *          // Socket 가져오기
+	 *          Socket socket = sslClientSocket.getSocket();
+	 *          </pre>
+	 */
+	public SSLClient(String host, int port) {
+		// 1. Param Check
+		if (host == null) {
+			logger.info("[SSLClient] Param 'host' is null !!!");
+			return;
+		}
+
+		// 2. IP Convert & Check
+		byte[] retIP = IPConverting(host);
+		if (retIP == null) {
+			logger.info("[SSLClient] IPConverting Error !!!");
+			return;
+		}
+		ServerIP = retIP;
+
+		// 3. TLS Socket Make
+		try {
+			SSLContext context = SSLContext.getInstance("TLS");
+			context.init(null, trustAllCerts, null);
+			SSLSocketFactory sf = context.getSocketFactory();
+
+			// [2018-02-08]
+			// * 서버에 DNS Server (bind9)를 설치하여 문제는 해결하였으나, 불필요한 DNS Lookup을 막고자
+			// 호스트네임이 정의된 InetAddress 로 IP를 부여함.
+			// DNS Lookup 이 일어나지 않게 하기 위하여 호스트네임이 정의된 InetAddress 로 주소를 부여함.
+			// IP 만을 "10.0.1.1" 이런 형태로 부여할 경우 Cache 에 접속정보가 없을경우 DNS Lookup이 일어나게되며,
+			// DNS Server 가 없을 경우 지연현상이 발생됨.
+			logger.info("--> server IP = " + ServerIP);
+			InetAddress mInetAddress = InetAddress.getByAddress("", ServerIP);
+			logger.info("-->ip address -- " + mInetAddress + " -- " + port);
+			sslSocket = sf.createSocket(mInetAddress, port);
+		} catch (Exception e) {
+			logger.info("[SSLClient] TLS Socket Make Exception Error");
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 생성된 SSL Socket 을 반환한다.
+	 *
+	 * @return (Socket) sslSocket
+	 */
+	public Socket getSocket() {
+		return sslSocket;
+	}
+
+	/**
+	 * 신뢰할 수 있는 인증기관의 공개키 인지 검증하는 코드를 사용자 코드로 대체 아무런 동작을 하지 않으며 모든 서버 연결을 신뢰한다는
+	 * 의미이다.
+	 */
+	private TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
+		public X509Certificate[] getAcceptedIssuers() {
+			X509Certificate[] myTrustedAnchors = new X509Certificate[0];
+			return myTrustedAnchors;
+		}
+
+		@Override
+		public void checkClientTrusted(X509Certificate[] certs, String authType) {
+		}
+
+		@Override
+		public void checkServerTrusted(X509Certificate[] certs, String authType) {
+		}
+	} };
+
+	/**
+	 * String IP 를 Byte [] IP 로 변환한다.
+	 *
+	 * @param inIP - (String) input IP
+	 *
+	 * @return (byte []) output IP
+	 */
+	private byte[] IPConverting(String inIP) {
+		// 1. Param Check
+		if (inIP == null)
+			return null;
+
+		// 2. Convert
+		char[] CharArray = inIP.toCharArray();
+		if (CharArray == null)
+			return null;
+		for (int i = 0; i < CharArray.length; i++)
+			if (CharArray[i] == '.')
+				CharArray[i] = ';';
+
+		String newIP = new String(CharArray);
+		String[] splitData = newIP.split(";");
+
+		if (splitData == null)
+			return null;
+		if (splitData.length != 4)
+			return null;
+
+		byte[] outIParray = new byte[4];
+		for (int i = 0; i < splitData.length; i++)
+			outIParray[i] = (byte) Integer.parseInt(splitData[i]);
+
+		// 3. return
+		return outIParray;
+	}
+
+	public static String sendToWallpad(String ip, String xml) {
+		int port = 10000; // 접속할 port - 월 패드/단지서버 포트번호
+		Socket requestSock = null;
+		SocketAddress socketAddress = new InetSocketAddress(ip, port); // 접속할
+		BufferedReader br = null;
+		InputStream is = null;
+		OutputStream os = null;
+		PrintWriter pw = null;
+
+		String sendMsg = null; // 보내는 메시지
+		String recvMsg = ""; // 받는 메시지
+		StringBuilder sb = new StringBuilder();
+
+		try {
+			port += 1000;
+			SSLClient sslClientSocket = new SSLClient(ip, port);
+			requestSock = sslClientSocket.getSocket();
+
+			sendMsg = xml; // 요청 xml
+
+			os = requestSock.getOutputStream();
+			is = requestSock.getInputStream();
+			pw = new PrintWriter(new OutputStreamWriter(os)); // 입력한 문자 전송
+//			br = new BufferedReader(new InputStreamReader(is));
+
+			pw.println(sendMsg); // sendMsg출력
+			pw.flush(); // 버퍼메모리 사용
+
+			// light request를 보내서 reply 수신&출력.
+//			String line = null;
+//			while ((line = br.readLine()) != null) {
+//				logger.info("line = " + line);
+//				sb.append(line);
+//
+//				// </imap>을 받는데 딜레이 생겨서 안받음.... readline()의 문제? *********
+//				if (line.equals("   </service>")) {
+//					// System.out.println("service same~~~");
+//					break;
+//				}
+//			}
+//			recvMsg = sb.toString();
+//			// System.out.println("recvMsg1 = " + recvMsg);
+//			if (recvMsg.length() != 0) {
+//				recvMsg += "</imap>"; // service에서 멈추기 때문에 마지막 XML 구문을 닫아줘야 함.
+//			}
+//			// System.out.println("recvMsg2 = " + recvMsg);
+//
+			pw.close();
+//			br.close();
+			requestSock.close();
+
+		} catch (ConnectException ce) {
+			// TODO Auto-generated catch block
+			recvMsg = "ConnectionException";
+			logger.info("[sendToWallpad] - Exception !!! ~~~");
+
+			try {
+				Thread.sleep(2000);
+			} catch (InterruptedException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+
+			ce.printStackTrace();
+		} catch (SocketTimeoutException se) {
+			// TODO Auto-generated catch block
+
+			recvMsg = "SocketTimeoutException";
+			logger.info("connection TIME OUT!!!");
+
+			try {
+				Thread.sleep(100);
+			} catch (InterruptedException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+
+		} catch (IOException ioe) {
+			// TODO Auto-generated catch block
+
+			recvMsg = "IOException";
+			ioe.printStackTrace();
+		}
+
+		// System.out.println("return recvMsg = " + recvMsg);
+//		logger.info("recv = " + recvMsg);
+		return recvMsg;
+	}
+
+}

+ 107 - 0
src/main/java/com/icontrols/kakao/socket/utils/XMLConstants.java

@@ -0,0 +1,107 @@
+package com.icontrols.kakao.socket.utils;
+
+public class XMLConstants {
+	public enum REMOTE_KIND {
+		// 기존 iCMD 프로토콜
+		remote_get_home_devices(0, "remote_get_home_devices"),
+		remote_access_livinglight(1, "remote_access_livinglight"), remote_access_gas(2, "remote_access_gas"),
+		remote_access_doorlock(3, "remote_access_doorlock"), remote_access_temper(4, "remote_access_temper"),
+		remote_access_mode(5, "remote_access_mode"), remote_access_ventil(6, "remote_access_ventil"),
+		remote_access_curtain(7, "remote_access_curtain"), remote_access_sysaircon(8, "remote_access_sysaircon"),
+		remote_access_light(9, "remote_access_light"), remote_access_electric(10, "remote_access_electric"),
+		remote_access_light_batch_control(11, "remote_access_light_batch_control"),
+
+		// 신규 추가 iCMD 프로토콜
+		msg_elevator_call(12, "msg_elevator_call"), msg_notice_list(13, "msg_notice_list"),
+		msg_device_subscription(14, "msg_device_subscription"),
+		msg_device_unsubscription(15, "msg_device_unsubscription"),
+		renew_remote_get_home_devices(16, "renew_remote_get_home_devices"),
+		msg_home_device_set_control(17, "msg_home_device_set_control"),
+		msg_home_device_get_status(18, "msg_home_device_get_status"), msg_visitor_list(19, "msg_visitor_list"),
+		msg_car_parking_list(20, "msg_car_parking_list"),
+		remote_register_new_device_auth_number(21, "remote_register_new_device_auth_number"),
+		msg_total_devices_status(22, "msg_total_devices_status");
+
+		private int mIdx;
+		private String mTextXmlData;
+
+		private REMOTE_KIND(int idx, final String xmlContents) {
+			this.setmIdx(idx);
+			this.mTextXmlData = xmlContents;
+		}
+
+		public String getContents() {
+			return mTextXmlData;
+		}
+
+		public int getmIdx() {
+			return mIdx;
+		}
+
+		public void setmIdx(int mIdx) {
+			this.mIdx = mIdx;
+		}
+
+	}
+
+	public enum ACTION_XML {
+		STATUS(0, "status"), CONTROL(1, "control");
+
+		private int idx;
+		private String content;
+
+		private ACTION_XML(final int idx, final String content) {
+			this.setIdx(idx);
+			this.content = content;
+		}
+
+		public String getConents() {
+			return this.content;
+		}
+
+		public int getIdx() {
+			return idx;
+		}
+
+		public void setIdx(int idx) {
+			this.idx = idx;
+		}
+
+	}
+
+	public enum COM_MSG {
+		SUCCESS(1, "success"), // 정상 요청 응답
+		TIMEOUT(98, "timeout"), INVALID_INDEX(97, "invalid index"), INVALID_ITEMSEQ(96, "invalid itemseq"),
+		NO_SEARCH_DATA(95, "no search data"), ERROR(99, "error");
+
+		private int idx;
+		private String content;
+
+		private COM_MSG(final int idx, final String content) {
+			this.setIdx(idx);
+			this.content = content;
+		}
+
+		public String getConents() {
+			return this.content;
+		}
+
+		public int getIdx() {
+			return idx;
+		}
+
+		public void setIdx(int idx) {
+			this.idx = idx;
+		}
+
+	}
+
+	public static int msg_num = 1;
+
+	public static String getImapMsgNum() {
+		msg_num++;
+		if (msg_num > 255)
+			msg_num = 1;
+		return String.valueOf(msg_num);
+	}
+}

+ 43 - 0
src/main/java/com/icontrols/kakao/socket/utils/XMLGenerator.java

@@ -0,0 +1,43 @@
+package com.icontrols.kakao.socket.utils;
+
+public class XMLGenerator {
+
+	public static String MakeXMLHeader(int Dong, int Ho, String kind) {
+		// XML Header
+		String targetIP = IPGenerator.getIPFromDongHo(Dong, Ho);
+		StringBuffer sb = new StringBuffer();
+		sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n")
+		.append("<imap ver = \"1.0\" address = \"").append(targetIP).append("\" sender = \"").append(Dong).append("동").append(Ho).append("호").append("\">\r\n")
+		.append("   <service type = \"request\" name = \"").append(kind).append("\">\r\n")
+		.append("      <target name = \"mobile\" id = \"1\" msg_no = \"").append(XMLConstants.getImapMsgNum()).append("\" />\r\n")
+		;
+
+		return sb.toString();
+	}
+	public static String MakeXMLHeaderForSUBS(String kind) {
+		// XML Header
+		String retXML = null;
+		retXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
+				+ "<imap ver = \"1.0\" address = \"10.0.1.1\" sender = \"valley\">\r\n"
+				+ "   <service type = \"request\" name = \"" + kind + "\">\r\n";
+
+		return retXML;
+	}
+	
+	public static String MakeXMLHeaderForNotice(String kind) {
+		// XML Header
+		String retXML = null;
+		retXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
+				+ "<imap ver = \"1.0\" address = \"10.0.1.1\" sender = \"valley\">\r\n"
+				+ "   <service type = \"notice\" name = \"" + kind + "\">\r\n";
+
+		return retXML;
+	}
+
+	public static String MakeXMLTail() {
+		String retXML = null;
+		retXML = "   </service>\r\n" + "</imap>";
+		return retXML;
+	}
+
+}

+ 266 - 0
src/main/java/com/icontrols/kakao/socket/utils/iMapRequestSync.java

@@ -0,0 +1,266 @@
+package com.icontrols.kakao.socket.utils;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class iMapRequestSync {
+
+	private static final Logger logger = LoggerFactory.getLogger(iMapRequestSync.class);
+
+	public static final int APT_SERVER = 1;
+	public static final int APT_LOBBY = 2;
+
+	public static final int TYPE_REQUEST = 1;
+	public static final int TYPE_NOTICE = 2;
+	public static final int TYPE_RESPONSE = 3;
+
+	public static final int COMM_COMPLITE = 1;
+	public static final int COMM_ERROR = 2;
+	public static final int COMM_DOING = 3;
+	public static final int COMM_NONE = 4;
+
+	public static final int SOCKET_BUFFER_SIZE = 100;
+	public static final int SOCKET_READ_BUFFER = 100;
+
+	int SOCKET_WAIT_COUNT = 30;
+	int SOCKET_WAIT_TIME = 3000;
+
+	long socket_time = 0;
+	Socket isock;
+	String ClientIP = "10.2.14.1";
+	int ClientPort = 10000;
+
+	boolean log_sw = true;
+	private String iMapXML = "";
+	private Document doc = null;
+
+	NodeList items;
+	int Svrtarget;
+
+	boolean LogSW = true;
+
+	String cmdDoc;
+
+	void iMAPClientLog(String msg) {
+		if (LogSW)
+			logger.info("iMAPClient :" + msg);
+	}
+
+	public iMapRequestSync(String svrIP, int svrPORT) 
+	{
+		ClientIP = svrIP;
+		ClientPort     = svrPORT;
+	}
+
+	public void SetSocketWaitCount(int count) {
+		SOCKET_WAIT_TIME = count;
+	}
+
+	public int doWork(String XML) {
+		int retValue = 0;
+		iMAPClientLog("Send XML : \n " + XML);
+		boolean bRet = reqSocketCommunication(XML, ClientIP, ClientPort, SOCKET_WAIT_TIME); //실제 실행
+
+		if (bRet == true) {
+			retValue = 0; // 커넥션 정상
+		} else {
+			retValue = -1; // 커넥션 안됨
+		}
+
+		return retValue;
+	}
+
+	public String getResponseXML() {
+		return iMapXML;
+	}
+
+	public Document getDocument() {
+		return doc;
+	}
+
+	public boolean reqSocketCommunication(String XML, String SoIPa, int SoIPport, int TimeOut) {
+		logger.info("reqSocketCommunication");
+		try {
+			cmdDoc = XML + "\r\n"; // 보낼 XML 메시지
+
+			// 소켓 설정
+			isock = new Socket();
+			isock.setSoLinger(true, 3000);
+			isock.setReceiveBufferSize(SOCKET_BUFFER_SIZE * 1024);
+			isock.setSendBufferSize(SOCKET_BUFFER_SIZE * 1024);
+			isock.setSoTimeout(TimeOut);
+			InetSocketAddress iSocAddr = new InetSocketAddress(SoIPa, SoIPport); 
+			
+			//소켓 연결 
+			isock.connect(iSocAddr, SOCKET_WAIT_TIME);
+			
+			InputStream In = isock.getInputStream();
+			BufferedReader XMLReader = new BufferedReader(new InputStreamReader(In, "euc-kr"));
+			DataOutputStream outstream = new DataOutputStream(isock.getOutputStream());
+			byte[] outBF = cmdDoc.getBytes("euc-kr");
+			outstream.write(outBF);
+			outstream.flush();
+
+			iMapXML = "";
+			String readbuff;
+			int i = 0;
+			boolean succ = false;
+			socket_time = System.currentTimeMillis() + SOCKET_WAIT_TIME;
+			while (System.currentTimeMillis() < socket_time) {
+				if (XMLReader.ready()) {
+
+					char[] indata = new char[SOCKET_READ_BUFFER * 1024];
+					readbuff = "";
+
+					while (XMLReader.ready()) {
+						long lenofxML = XMLReader.read(indata);
+						readbuff += new String(indata, 0, (int) lenofxML);
+
+					}
+
+					if (readbuff != null)
+						iMapXML += readbuff;
+
+					if (-1 < readbuff.indexOf("</imap>")) {
+						succ = true;
+						break;
+					}
+				} else {
+					// imapAPIlog("LV1", "Socket sleep\n");
+					try {
+						Thread.sleep(100);
+					} catch (Exception e) {
+						logger.error("message : " + e);
+						isock.close();
+						return false;
+					}
+					i++;
+				}
+			}
+
+			if (i == SOCKET_WAIT_COUNT || succ == false) {
+
+				isock.close();
+				return false;
+			}
+		} catch (Exception e) {
+			logger.error("message : " + e);
+			return false;
+		}
+
+		try {
+			Thread.sleep(30);
+			isock.close();
+			SetXMLDoc(iMapXML); //변수 doc iMapXML 내용 선언해주는 과정 
+			logger.info(iMapXML);
+			logger.info("&&&&&&&&&&&&&&&&&    COMM_COMPLITE     &&&&&&&&&& 1");
+			return true;
+		} catch (IOException e) {
+			logger.error("message : " + e);
+		} catch (InterruptedException e) {
+			logger.error("message : " + e);
+		}
+
+		return false;
+	}
+
+	private void SetXMLDoc(String XMLDoc) {
+
+		try {
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			DocumentBuilder builder = factory.newDocumentBuilder();
+
+			iMapXML = XMLDoc;
+			logger.debug("length :" + iMapXML.getBytes());
+			InputStream istream = new ByteArrayInputStream(iMapXML.getBytes("UTF-8"));
+			doc = builder.parse(istream);
+			logger.debug(" iMapXML : " + iMapXML.length());
+			if (doc == null) {
+				try {
+					for (int i = 0; i < 10; i++) {
+						Thread.sleep(10);
+						if (doc != null) {
+							logger.debug(" doc parsing log wait  : " + i);
+
+							break;
+						}
+					}
+				} catch (Exception ex) {
+					logger.error("message : " + ex);
+
+				}
+			}
+
+		} catch (Exception e) {
+			logger.error("message : " + e);
+		}
+	}
+
+	public void SetEncType() {
+
+	}
+
+	public int SetItem(String name) {
+		if (doc == null)
+			return 0;
+		items = doc.getElementsByTagName(name);
+		return items.getLength();
+	}
+
+	public String GetAttributeVal(int index, String attname) {
+		if (items == null)
+			return null;
+		int count = items.getLength();
+		if (count < 1 || index - 1 > count)
+			return null;
+		Node item = items.item(index);
+		NamedNodeMap attrs = item.getAttributes();
+		for (int j = 0; j < attrs.getLength(); j++) {
+			Node attr = attrs.item(j);
+
+			if (attr.getNodeName().equalsIgnoreCase(attname))
+				return attr.getNodeValue();
+		}
+		return null;
+	}
+
+	public String GetItemVal(int index) {
+		int count = items.getLength();
+
+		if (count < 1 || index - 1 > count)
+			return null;
+		Node item = items.item(index);
+
+		return trimquotation(item.getTextContent());
+	}
+
+	protected String trimquotation(String sen) {
+		sen = sen.replaceAll("\"", "");
+		return sen.trim();
+	}
+
+	public void SetServerIPConfig(String svrIP, int setport) {
+		ClientIP = svrIP;
+		ClientPort = setport;
+	}
+
+	public String GetServerIPAddress() {
+		return ClientIP;
+	}
+}

+ 96 - 0
src/main/java/com/icontrols/kakao/socket/wallpad/KakaoEventHandler.java

@@ -0,0 +1,96 @@
+package com.icontrols.kakao.socket.wallpad;
+
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+
+import javax.annotation.Resource;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.icontrols.kakao.common.ERROR_INFO;
+import com.icontrols.kakao.common.KAKAO_RESULT;
+import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.SERVERINFO;
+import com.icontrols.kakao.common.THINGS_INFO;
+import com.icontrols.kakao.common.TYPE;
+import com.icontrols.kakao.common.TYPE.XML_LIGHT;
+import com.icontrols.kakao.common.utils.BeanUtils;
+import com.icontrols.kakao.common.utils.CommonUtil;
+import com.icontrols.kakao.config.AsyncConfig;
+import com.icontrols.kakao.entity.DonghoInfo;
+import com.icontrols.kakao.repo.DonghoInfoRepository;
+import com.icontrols.kakao.socket.utils.IPGenerator;
+import com.icontrols.kakao.socket.utils.SSLClient;
+import com.icontrols.kakao.socket.utils.XMLConstants.REMOTE_KIND;
+import com.icontrols.kakao.socket.utils.XMLGenerator;
+
+@Service("kakaoEventHandler")
+public class KakaoEventHandler {
+	private final static Logger logger = LoggerFactory.getLogger(KakaoEventHandler.class);
+	
+	@Resource(name="asyncConfig")
+	private AsyncConfig asyncConfig;
+	
+	@Resource(name="kakaoMsgHandler")
+	private KakaoMsgHandler kakaoMsgHandler;
+	
+	@Resource(name="wallpadMsgSender")
+	private WallpadMsgSender wallpadMsgSender;
+	
+	@Async("eventExecutor")
+	public CompletableFuture<Map<String, Object>> getDeviceStatus(DonghoInfo donghoInfo, Map<String, Object> params) throws InterruptedException{
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		
+		try {
+			// 인증토큰 검증
+			String tokenStartTime = donghoInfo.getKakaoTokenCreateTime();
+			String tokenExpiresTime = donghoInfo.getKakaoTokenExpireTime();
+			String currentTime = CommonUtil.getStrTypeCurTime();
+			
+			int result = CommonUtil.compareStringTypeTime(currentTime, tokenExpiresTime);
+
+			// 현재 시간이 토큰 만료시간보다 크다면 토큰이 만료 됐다는 뜻.
+			// 카카오 이벤트 서버에서 토큰을 갱신 받은 뒤 이벤트를 전송해야 한다.
+			if(result < 1) {
+//				sendKakaoTokenRefresh(dong, ho, params);
+			}
+			
+			retMap.put("result", "SUCCESS");
+		}catch(Exception e) {
+			retMap.put("result", "FAIL");
+			retMap.put("error", e.getMessage());
+		}
+		
+		return CompletableFuture.completedFuture(retMap);
+	}
+}

+ 680 - 0
src/main/java/com/icontrols/kakao/socket/wallpad/KakaoMsgHandler.java

@@ -0,0 +1,680 @@
+package com.icontrols.kakao.socket.wallpad;
+
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+
+import javax.annotation.Resource;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.icontrols.kakao.common.ERROR_INFO;
+import com.icontrols.kakao.common.KAKAO_RESULT;
+import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.SERVERINFO;
+import com.icontrols.kakao.common.THINGS_INFO;
+import com.icontrols.kakao.common.TYPE;
+import com.icontrols.kakao.common.TYPE.XML_LIGHT;
+import com.icontrols.kakao.common.utils.BeanUtils;
+import com.icontrols.kakao.common.utils.CommonUtil;
+import com.icontrols.kakao.config.AsyncConfig;
+import com.icontrols.kakao.entity.DonghoInfo;
+import com.icontrols.kakao.repo.DonghoInfoRepository;
+import com.icontrols.kakao.socket.utils.IPGenerator;
+import com.icontrols.kakao.socket.utils.SSLClient;
+import com.icontrols.kakao.socket.utils.XMLConstants.REMOTE_KIND;
+import com.icontrols.kakao.socket.utils.XMLGenerator;
+
+@Service("kakaoMsgHandler")
+public class KakaoMsgHandler {
+	private final static Logger logger = LoggerFactory.getLogger(KakaoMsgHandler.class);
+	
+	@Resource(name="asyncConfig")
+	private AsyncConfig asyncConfig;
+	
+	@Resource(name="wallpadMsgSender")
+	WallpadMsgSender wallpadMsgSender;
+	
+	@Autowired
+	DonghoInfoRepository donghoRepo;
+	
+	private static final int REPORTSTATE_REQUEST_FIRST = 0;
+	private static final int REPORTSTATE_REQUEST_RETRY = 1;
+	/**
+	 * 카카오 전용 인증 프로세스
+	 * 월패드에 인증번호를 노출시키는 메시지를 전송한다.
+	 * @param param
+	 * @return
+	 */
+	public Map<String, Object> doAuthProcess(Map<String, Object> param){
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		
+		try {
+			int dong = Integer.parseInt((String)param.get("dong"));
+			int ho = Integer.parseInt((String)param.get("ho"));
+			String code = (String)param.get("code");
+			
+			String targetIP = IPGenerator.getIPFromDongHo(dong, ho);
+			String twinid = UUID.randomUUID().toString();
+			String alias = "Kakao";
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeaderForNotice(REMOTE_KIND.remote_register_new_device_auth_number.getContents()))
+			.append("      <home_info dong=\"").append(dong).append("\" ho=\"").append(ho).append("\" ip=\"").append(targetIP).append("\" type=\"main\"/>\r\n")
+			.append("      <device_info alias=\"").append(alias).append("\" twinid=\"").append(twinid).append("\" auth_number=\"" + code + "\" />\r\n")
+			.append(XMLGenerator.MakeXMLTail());
+			
+			logger.info(sb.toString());
+			
+			SSLClient.sendToWallpad(targetIP, sb.toString());
+			
+			retMap.put("result", ERROR_INFO.CODE.SUCCESS.getContents());
+			retMap.put("resultCode", ERROR_INFO.CODE.SUCCESS.getmIdx());
+		}catch(Exception e) {
+			retMap.put("result", ERROR_INFO.CODE.FAIL_999.getContents());
+			retMap.put("resultCode", ERROR_INFO.CODE.FAIL_999.getmIdx());
+			e.printStackTrace();
+		}
+		return retMap;
+	}
+	
+	/**
+	 * 기기 구독 / 해제 에 대한 메시지를 월패드에 전송한다. 
+	 * 기기 구독이 돼 있지 않으면 이벤트를 받지 못한다.
+	 * @param param
+	 * @return
+	 */
+	public Map<String, Object> subscriptionProcess(Map<String, Object> param) {
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		
+		try {
+			Document doc = new WallpadMsgSender().remoteDeviceSubscription(param);
+			
+			if(doc == null) {
+				logger.error("[subscriptionProcess ERROR] - Document null");
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				return retMap;
+			}
+			
+			retMap.put("result", "success");
+		}catch(Exception e) {
+			logger.error("[subscriptionProcess ERROR] - " + e.getStackTrace());
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+		}
+		return retMap;
+	}
+	
+	/**
+	 * 월패드와 연결된 기기들의 전체 상태를 월패드에 요청하고 응답 받는다.
+	 * 지원 기기가 추가되면 해당 부분은 수정이 필요하다. 
+	 * 자식 노드로 subdevinfo가 있는 기기들을 잘 확인해야 한다.
+	 * @param dong
+	 * @param ho
+	 * @return
+	 */
+	public Map<String, Object> getDevicesStatusList(int dong, int ho) {
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		Map<String, Object> body = new HashMap<String, Object>();
+		List<String> unreachableIds = new ArrayList<String>();
+		
+		try {
+			Document doc = new WallpadMsgSender().remoteDeviceStatusList(dong, ho);
+			
+			if(doc == null) {
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				logger.error("[ERROR]-[Document is Null] - [" + dong + "-" + ho + "]");
+				return retMap;
+			}
+			
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			Node iMap = (Node)xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+			String xmlResult = iMap.getAttributes().getNamedItem("result").getTextContent();
+			
+			logger.debug("[KakaoMsgHandler - getDeviceList] - xmlResult -> " + xmlResult);
+			
+			if(!xmlResult.equalsIgnoreCase("ok")) {
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				logger.error("[KakaoMsgHandler - getDeviceList] xmlResult not ok");
+				return retMap;
+			}
+			
+			// 월패드와 연결된 상위 THINGS 갯수 확인
+			Node dCount =  null;
+	        
+			NodeList devList = (NodeList)xpath.evaluate("//imap/service/devinfo", doc, XPathConstants.NODESET);
+			for(int i=0; i < devList.getLength(); i++) {
+				Node pNode = devList.item(i);
+				
+				String tmpModelId = pNode.getAttributes().getNamedItem("name").getTextContent();
+				int childCount = Integer.parseInt(pNode.getAttributes().getNamedItem("value").getTextContent());
+				
+				// 아직 카카오에서 electric과 batchlight는 지원하지 않는다.
+				// 나중에 지원하게 되면 그때 추가하면 된다.
+				if(tmpModelId.equalsIgnoreCase("electric") || tmpModelId.equalsIgnoreCase("batchlight")) continue;
+				
+				List<Map<String, Object>> childNodeInfoList = null;
+				
+				// subdevinfo 를 가지고 있는 THINGS일 경우는 subdevinfo 들을 다시 구해야 한다.
+				// 예를들어 light는 light01 ~ 05 등의 subdevinfo를 가진다.
+				if(tmpModelId.equalsIgnoreCase("light")) {
+					for(int j=1; j <= childCount; j++) {
+						String childName = j < 10 ? tmpModelId+"0"+j : tmpModelId+j;
+						Node childNode = (Node)xpath.evaluate("//imap/service/devinfo/*[@name='"+ childName +"']", doc, XPathConstants.NODE);
+						
+						childCount = Integer.parseInt(childNode.getAttributes().getNamedItem("value").getTextContent());
+						
+						NodeList childList = (NodeList)xpath.evaluate("//imap/service/devinfo/subdevinfo/*[@name='"+ childName +"']", doc, XPathConstants.NODESET);
+						childNodeInfoList = getChildDeviceInfo(childCount, childList);
+						
+						if(childNodeInfoList != null)
+							for(Map<String, Object> tMap : childNodeInfoList) body.putAll(tMap);
+					}
+					continue;
+				}
+				// 해당 THINGS의 자식노드 status 목록들을 조회한다.
+				NodeList childList = (NodeList)xpath.evaluate("//imap/service/devinfo/*[@name='"+ tmpModelId +"']", doc, XPathConstants.NODESET);
+				
+				
+				childNodeInfoList = getChildDeviceInfo(childCount, childList);
+				
+				if(childNodeInfoList != null)
+					for(Map<String, Object> tMap : childNodeInfoList) body.putAll(tMap);
+	        }
+		}catch(Exception e) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+			logger.error("[ERROR] - " + e.getMessage());
+		}finally {
+			retMap.put(KEY_INFO.HTTP_KEY_UNREACHABLEIDS, unreachableIds);
+			retMap.put(KEY_INFO.HTTP_KEY_BODY, body);
+		}
+		
+		return retMap;
+	}
+	/**
+	 * 동 호 정보 EndpointID 로 월패드에 상태 조회 메세지를 전송하고 결과를 반환 받는다. 
+	 * @param dong
+	 * @param ho
+	 * @param endpointId
+	 * @return
+	 */
+	public Map<String, Object> getDeviceStatus(int dong, int ho, String endpointId){
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		
+		String[] tmpSplit = endpointId.split("_");
+		
+		String modelId = tmpSplit[0];				// 기기 ID
+		int devNum = Integer.parseInt(tmpSplit[1]);	// 회로 번호
+		
+		Document doc = new WallpadMsgSender().remoteDeviceStatus(dong, ho, modelId, devNum);
+		
+		if(doc == null) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+			logger.error("[ERROR]-[Document is Null] - [" + dong + "-" + ho + "] ["+ modelId +"]");
+			return retMap;
+		}
+		
+		return getStatusMap(endpointId, doc);
+	}
+	
+	/**
+	 * 월패드에 장치 제어 명령을 보내고 결과를 리턴한다.
+	 * @param dong
+	 * @param ho
+	 * @param endpointId
+	 * @param actionName
+	 * @param desired
+	 * @return
+	 */
+	public Map<String, Object> sendDeviceAction(int dong, int ho, String endpointId, String actionName, Map<String, Object> desired){
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		
+		String[] tmpSplit = endpointId.split("_");
+		String modelId = tmpSplit[0];
+		int devNum = tmpSplit.length == 1?1:Integer.parseInt(tmpSplit[1]);
+		
+		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(endpointId);
+		
+		if(things == null) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_REQ));
+			return retMap;
+		}
+		String action = KakaoMsgHandler.setDeviceActionStr(modelId, desired);
+		
+		if(action.length() == 0) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_REQ));
+			return retMap;
+		}
+		Document doc = new WallpadMsgSender().remoteDeviceControl(
+																dong, 
+																ho, 
+																modelId, 
+																devNum, 
+																action
+															);
+		
+		if(doc == null) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+			return retMap;
+		}
+		
+		return getStatusMap(endpointId, doc);
+	}
+	
+	/**
+	 * 상태조회 요청 메세지에 대한 응답 메세지를 파싱하여 카카오 형식으로 변환한다.
+	 * @param endpointId
+	 * @param doc
+	 * @return
+	 */
+	public static Map<String, Object> getStatusMap(String endpointId, Document doc){
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		
+		XPath xpath = XPathFactory.newInstance().newXPath();
+		try {
+			Node iMap = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+			String xmlResult = iMap.getAttributes().getNamedItem("result").getTextContent();
+			
+			if(!xmlResult.equalsIgnoreCase("ok")) {
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_REQ)); 
+				logger.error("[ERROR]-[xmlResult is not ok");
+				return retMap;
+			}
+			
+			String status = (String) xpath.evaluate("//imap/service/status", doc, XPathConstants.STRING);
+			
+			Map<String, Object> state = setDeviceState(endpointId, status);
+			retMap.put(endpointId, state);
+			
+		} catch (XPathExpressionException e) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+			e.printStackTrace();
+		}
+		return retMap;
+	}
+	
+	/**
+	 * 상태 변경 이벤트 발생시 이벤트 결과를 카카오 이벤트 서버에 전송한다. 
+	 * 전송시 토큰이 만료 되면 [sendKakaoTokenRefresh] method를 호출하여 토큰을 갱신한뒤 다시 재시도 한다. 
+	 * @param dong
+	 * @param ho
+	 * @param params
+	 * @param reqType
+	 */
+	public void SendStatusResult(int dong, int ho, Map<String, Object> params, int reqType) {
+		try {
+			
+			if(params == null) return;
+			
+			// 먼저 이벤트 전송전 인증 토큰이 만료 됐는지 확인한다.
+			// 인증 토큰이 만료 됐으면 인증토큰을 재생성 한뒤 이벤트를 전송한다.
+			// reqType이 REPORTSTATE_REQUEST_FIRST 이면 이벤트 전송을 한번도 하지 않은 경우
+			// reqType이 REPORTSTATE_REQUEST_RETRY 이면 이벤트 전송 후 인증토큰 만료 에러 때문에 인증토큰 갱신하고 재전송하는 경우
+			DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+			List<DonghoInfo> dList = repo.findByDongAndHo(Integer.toString(dong), Integer.toString(ho));
+			
+			if(dList.size() != 1) {
+				logger.error("[" + dong + "/"+ ho +"]-유저정보 없음- 이벤트 전송 불가.");
+				return;
+			}
+			
+			String tokenStartTime = dList.get(0).getKakaoTokenCreateTime();
+			String tokenExpiresTime = dList.get(0).getKakaoTokenExpireTime();
+			String currentTime = CommonUtil.getStrTypeCurTime();
+			
+			int result = CommonUtil.compareStringTypeTime(currentTime, tokenExpiresTime);
+
+			// 현재 시간이 토큰 만료시간보다 크다면 토큰이 만료 됐다는 뜻.
+			// 카카오 이벤트 서버에서 토큰을 갱신 받은 뒤 이벤트를 전송해야 한다.
+			if(result < 1) {
+				sendKakaoTokenRefresh(dong, ho, params);
+				return;
+			}
+			
+			String authorization = SERVERINFO.KAKAO_EVENT_CLIENT_ID+":"+SERVERINFO.KAKAO_EVENT_CLIENT_SECRET;
+			String encoding = Base64.getEncoder().encodeToString(authorization.getBytes());
+			
+			CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
+			HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+			
+			requestFactory.setHttpClient(httpClient);
+			requestFactory.setConnectTimeout(3000);
+			
+			RestTemplate restTemplate = new RestTemplate(requestFactory);
+
+			HttpHeaders headers = new HttpHeaders();
+			
+			headers.setContentType(MediaType.APPLICATION_JSON);
+			headers.set("Authorization", "Basic " + encoding);
+			// 제어기기 별로 상태 이벤트 구분하여 처리하도록 해야 한다
+			HttpEntity<Object> entity = new HttpEntity<Object>(params, headers);
+			
+			String url = SERVERINFO.SendDevEventURL + ":" + SERVERINFO.SendDevEventPort + "/events";
+			
+			logger.info("SendStatusResult URL : " + url);
+			logger.info("SendStatusResult header : " + headers.toString());
+			logger.info("SendStatusResult return value : " + params.toString());
+			
+			String answer = restTemplate.postForObject(url, entity, String.class);
+			
+			logger.info("SendStatusResult answer :" + answer);
+			
+			// 결과에 error가 있으면 error에 맞게 대응해줘야 한다.
+			Map<String, Object> retMap = CommonUtil.jsonToMap(answer);
+			
+			if(retMap.containsKey(KEY_INFO.HTTP_KEY_ERROR)) {
+				//error 결과가 -201 이면 인증토큰이 만료 됐다는 뜻
+				Map<String, Object> errMap = (Map<String, Object>)retMap.get(KEY_INFO.HTTP_KEY_ERROR);
+				int errorCode = Integer.parseInt((String)errMap.get(KEY_INFO.ERROR_KEY_CODE));
+				String message = (String)errMap.get(KEY_INFO.ERROR_KEY_MESSAGE);
+				
+				logger.info("[KAKAO - ERROR] - [ CODE ] - " + errorCode + " || [message] - " + message );
+				
+				// 인증토큰 만료 에러
+				if(reqType == REPORTSTATE_REQUEST_FIRST && errorCode == KAKAO_RESULT.CODE.FAIL_EXPIREDTOKEN.getResultCode()) {
+					// 인증토큰 갱신 요청 보내기 
+					sendKakaoTokenRefresh(dong, ho, params);
+				}
+			}else {
+				logger.info("EVENT SEND SUCCESS -- " + params.toString());
+			}
+		} catch (Exception e) {
+			logger.info("SendStatusResult Exception e :" + e.toString());
+		}
+	}
+	
+	/**
+	 * 이벤트 전송시 토큰이 만료되면 해당 메서드가 호출된다.
+	 * 이벤트 토큰 만료시 카카오 이벤트 토큰 서버에 토큰 갱신 요청을 보낸다.
+	 * 토큰이 갱신 되면 이벤트 전송을 시도한다.
+	 * @param dong
+	 * @param ho
+	 * @param params
+	 */
+	public boolean sendKakaoTokenRefresh(int dong, int ho, Map<String, Object> params){
+		DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+		List<DonghoInfo> dList = repo.findByDongAndHo(Integer.toString(dong), Integer.toString(ho));
+		
+		
+		if(dList.size() != 1) return false;
+		DonghoInfo donghoInfo = dList.get(0);
+		
+		String refreshToken = donghoInfo.getKakaoRefreshToken();
+		
+		String authorization = SERVERINFO.KAKAO_EVENT_CLIENT_ID+":"+SERVERINFO.KAKAO_EVENT_CLIENT_SECRET;
+		String encoding = Base64.getEncoder().encodeToString(authorization.getBytes());
+		
+		
+		HttpHeaders headers = new HttpHeaders();
+		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+		headers.add("Authorization", "Basic " + encoding);
+		
+		MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
+		parameters.add("grant_type", "refresh_token");
+		parameters.add("refresh_token", refreshToken);
+		
+		HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(parameters, headers);
+		
+		String url = SERVERINFO.SendDevEventURL + ":" + SERVERINFO.SendDevEventPort + "/events/token";
+		
+		logger.info("[TOKEN REFRESH REQUEST] URL : " + url);
+		logger.info("[TOKEN REFRESH REQUEST] header : " + headers.toString());
+		logger.info("[TOKEN REFRESH REQUEST] return value : " + entity.toString());
+		URI uri = URI.create(url);
+		
+		CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
+		HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+		
+		requestFactory.setHttpClient(httpClient);
+		requestFactory.setConnectTimeout(3000);
+		
+		RestTemplate restTemplate = new RestTemplate(requestFactory);
+		
+		String answer = restTemplate.postForObject(url, entity, String.class);
+		logger.debug("[TOKEN REFRESH REQUEST] = " + answer);
+		
+		Map<String, Object> retMap = CommonUtil.jsonToMap(answer);
+		
+		if(retMap.containsKey(KEY_INFO.HTTP_KEY_ACCESS_TOKEN) && retMap.containsKey(KEY_INFO.HTTP_KEY_REFRESH_TOKEN) && retMap.containsKey(KEY_INFO.HTTP_KEY_EXPIRES_IN)) {
+			donghoInfo.setKakaoAccessToken((String)retMap.get(KEY_INFO.HTTP_KEY_ACCESS_TOKEN));
+			donghoInfo.setKakaoRefreshToken((String)retMap.get(KEY_INFO.HTTP_KEY_REFRESH_TOKEN));
+			donghoInfo.setKakaoExpiresTime(Integer.toString((int)retMap.get(KEY_INFO.HTTP_KEY_EXPIRES_IN)));
+			
+			donghoInfo.setKakaoTokenCreateTime(CommonUtil.getStrTypeCurTime());
+			donghoInfo.setKakaoTokenExpireTime(CommonUtil.getStrTypeCurTimeAfterSec(Integer.parseInt(donghoInfo.getKakaoExpiresTime())));
+			
+			repo.save(donghoInfo);
+			
+			SendStatusResult(dong, ho, params, REPORTSTATE_REQUEST_FIRST);
+			return true;
+		}else {
+			logger.error("****************************************************");
+			logger.error("토클 리프레시 실패");
+			logger.error("****************************************************");
+			
+			return false;
+		}
+	}
+	
+	/**
+	 * 이벤트 발생시 카카오에 전송하는 Body를 반환한다.
+	 * @param dong
+	 * @param ho
+	 * @param body
+	 * @return
+	 * @throws Exception
+	 */
+	public Map<String, Object> setKakaoEventMsg(int dong, int ho, Map<String, Object> body) throws Exception{
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		List<String> unreachableIds = new ArrayList<>();
+		DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+		List<DonghoInfo> dList = repo.findByDongAndHo(Integer.toString(dong), Integer.toString(ho));
+		
+		if(dList.size() != 1) return null;
+		
+		retMap.put(KEY_INFO.HTTP_KEY_ID, UUID.randomUUID().toString()); 
+		retMap.put(KEY_INFO.HTTP_KEY_TYPE, TYPE.REQ_TYPE.REPORTSTATE.getTypeName());
+		retMap.put(KEY_INFO.HTTP_KEY_AUTHORIZATION, dList.get(0).getKakaoAccessToken());
+		retMap.put(KEY_INFO.HTTP_KEY_VERSION, "1");
+		retMap.put(KEY_INFO.HTTP_KEY_UNREACHABLEIDS, unreachableIds);
+		retMap.put(KEY_INFO.HTTP_KEY_BODY, body);
+		
+		return retMap;
+	}
+	
+	/**
+	 * 지원하는 기기들의 Capability 를 카카오 형식에 맞게 추출한다.
+	 * 기기가 추가되게 되면 목록을 수정해야한다.
+	 * @param modelId
+	 * @return
+	 */
+	public static Map<String, Object> getDeviceCapabilities(String modelId){
+		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(modelId);
+		
+		String jsonString = "";
+		
+		switch(things.getEndPoint()) {
+		case THINGS_INFO.THINGS_LIGHT:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.LIGHT.getCapJsonStr();
+			break;
+			
+		case THINGS_INFO.THINGS_TEMPER:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.TEMPER.getCapJsonStr();
+			break;
+			
+		case THINGS_INFO.THINGS_GAS:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.GAS.getCapJsonStr();
+			break;
+			
+		case THINGS_INFO.THINGS_VENTIL:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.VENTIL.getCapJsonStr();
+			break;
+		case THINGS_INFO.THINGS_ELECPLUG:
+			break;
+		}
+		
+		Map<String, Object> capabilities = CommonUtil.jsonToMap(jsonString);
+		
+		return capabilities;
+	}
+	
+	/**
+	 * Document에서 추출한 Device의 자식 노드들을 추출한다.
+	 * @param childCount
+	 * @param childList
+	 * @return
+	 */
+	public List<Map<String, Object>> getChildDeviceInfo(int childCount, NodeList childList){
+		List<Map<String, Object>> deviceList = new ArrayList<>();
+		
+		for(int j=0; j < childCount; j++) {
+			Map<String, Object> deviceMap = new HashMap<>();
+			
+			Node cNode = childList.item(j);
+			if(cNode == null) {
+				logger.error("cNode is null");
+				return null;
+			}
+			
+			String cNodeName = cNode.getAttributes().getNamedItem("name").getTextContent();
+			String cNodeDevNum = cNode.getAttributes().getNamedItem("dev_num").getTextContent();
+			String status = cNode.getTextContent();
+			String endpointId = cNodeName+"_"+cNodeDevNum;
+			
+			THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(cNodeName);
+			
+			Map<String, Object> deviceInfo =  new HashMap<String, Object>();
+			
+			deviceInfo.put(KEY_INFO.BODY_KEY_ENDPOINT_ID, endpointId);
+    		deviceInfo.put(KEY_INFO.BODY_KEY_NAME, cNodeName.toUpperCase() + " " + cNodeDevNum);
+    		deviceInfo.put(KEY_INFO.BODY_KEY_DISPLAY_TYPE, things.getKakaoDisplayName());
+    		
+    		deviceInfo.putAll(getDeviceCapabilities(endpointId));
+    		deviceInfo.put(KEY_INFO.BODY_KEY_STATE, setDeviceState(endpointId, status));
+    		
+    		deviceMap.put(endpointId, deviceInfo);
+    		deviceList.add(deviceMap);
+		}
+		
+		return deviceList;
+	}
+	
+	/**
+	 * 디바이스의 상태 명령어를 디바이스 종류에 맞게 생성한다.
+	 * @param modelId
+	 * @param desired
+	 * @return
+	 */
+	public static String setDeviceActionStr(String modelId, Map<String, Object> desired) {
+		StringBuffer sb = new StringBuffer();
+		
+		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(modelId);
+		
+		switch(things.getEndPoint()) {
+		case THINGS_INFO.THINGS_LIGHT:
+			sb.append(desired.get("power"));
+			break;
+			
+		case THINGS_INFO.THINGS_TEMPER:
+			if(desired.containsKey("power")) sb.append(desired.get("power"));
+			else if(desired.containsKey("targetSetpoint")) sb.append("on/").append(desired.get("targetSetpoint"));
+			
+			break;
+			
+		case THINGS_INFO.THINGS_GAS:
+			if(desired.containsKey("locked")){
+				if((boolean)desired.get("locked"))
+					sb.append("close");
+			}
+			break;
+			
+		case THINGS_INFO.THINGS_VENTIL:
+			if(desired.containsKey("power")) sb.append(desired.get("power"));
+			else if(desired.containsKey("fanSpeed")) sb.append("on/").append(desired.get("fanSpeed"));
+			break;
+		case THINGS_INFO.THINGS_ELECPLUG:
+			if(desired.containsKey("power")) sb.append(desired.get("power"));
+			else if(desired.containsKey("mode")) sb.append(desired.get("mode"));
+			break;
+		case THINGS_INFO.THINGS_ELEVATOR:
+			if(desired.containsKey("callDirection")) {
+				if("down".equalsIgnoreCase((String)desired.get("callDirection"))) {
+					sb.append(desired.get("callDirection"));
+				}
+			}
+		}
+		
+		logger.info("[---->>]" + sb.toString());
+		return sb.toString();
+	}
+	
+	/**
+	 * 상태조회 이후 월패드에서 받은 상태 값을 디바이스의 속성에 맞게 파싱하고 
+	 * Map에 <"속성명", "상태값"> 형태로 담아준다. 
+	 * @param modelId
+	 * @param status
+	 * @return
+	 */
+	public static Map<String, Object> setDeviceState(String modelId, String status){
+		Map<String,Object> state = new HashMap<String, Object>();
+		
+		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(modelId);
+		String[] splitStr = status.split("\\/");
+		
+		switch(things.getEndPoint()) {
+		case THINGS_INFO.THINGS_LIGHT:
+			state.put("power", status);
+			break;
+		case THINGS_INFO.THINGS_TEMPER:
+			
+			state.put("power", splitStr[0]);
+			state.put("targetSetpoint", Double.parseDouble(splitStr[1]));
+			state.put("currentTemperature", Double.parseDouble(splitStr[2]));
+			
+			break;
+		case THINGS_INFO.THINGS_GAS:
+			if(status.equalsIgnoreCase("close"))
+				state.put("locked", true);
+			else
+				state.put("locked", false);
+			break;
+			
+		case THINGS_INFO.THINGS_VENTIL:
+			state.put("power", splitStr[0]);
+			if(splitStr[0].equalsIgnoreCase("on")) state.put("fanSpeed", splitStr[1]);
+			break;
+		case THINGS_INFO.THINGS_ELEVATOR:
+			state.put("callDirection", "down");
+			break;
+		}
+		
+		return state;
+	}
+}

+ 69 - 0
src/main/java/com/icontrols/kakao/socket/wallpad/LogMsgHandler.java

@@ -0,0 +1,69 @@
+package com.icontrols.kakao.socket.wallpad;
+
+import java.net.InetAddress;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.icontrols.kakao.common.SERVERINFO;
+
+public class LogMsgHandler extends Thread {
+	private static final Logger logger = LoggerFactory.getLogger(LogMsgHandler.class);
+
+	// 생성자
+	public LogMsgHandler() {
+	}
+
+	public void run() {
+		try {
+			while (true) {
+
+				Calendar cal = new GregorianCalendar();
+				if (cal.get(Calendar.MINUTE) == 0 && (cal.get(Calendar.SECOND) == 0 || cal.get(Calendar.SECOND) == 1)) {
+
+					// reset
+					if (cal.get(Calendar.HOUR) == 0) {
+
+					}
+				}
+				Thread.sleep(1000);
+				// PingTest();
+			}
+		} catch (Exception e) {
+
+			logger.error("message : " + e);
+		}
+	}
+
+	public boolean PingTest() {
+		try {
+			InetAddress inet = InetAddress.getByName("hdc-smart.com");
+
+			if (inet.isReachable(500) == true) {
+				logger.error(SERVERINFO.SendEventURL + " ping Host is reachable");
+				return true;
+			} else {
+				logger.error(SERVERINFO.SendEventURL + " ping Host is NOT reachable");
+			}
+
+			// http://112.220.212.226
+
+			InetAddress inet2 = InetAddress.getByName("112.220.212.226");
+
+			if (inet2.isReachable(500) == true) {
+				logger.error("http://112.220.212.226" + " ping Host is reachable");
+				return true;
+			} else {
+				logger.error("http://112.220.212.226" + " ping Host is NOT reachable");
+			}
+		} catch (Exception e) {
+			logger.error("message : " + e);
+		}
+
+		return false;
+
+	}
+
+}

+ 89 - 0
src/main/java/com/icontrols/kakao/socket/wallpad/RecieveiMapMultiSocket.java

@@ -0,0 +1,89 @@
+package com.icontrols.kakao.socket.wallpad;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.icontrols.kakao.common.SERVERINFO;
+import com.icontrols.kakao.common.utils.CommonUtil;
+
+public class RecieveiMapMultiSocket {
+	private static final Logger logger = LoggerFactory.getLogger(RecieveiMapMultiSocket.class);
+
+	private static boolean bNewDanjiServer = true;
+	
+	public RecieveiMapMultiSocket() {
+		boolean bDevVersion = CommonUtil.getDevServer();
+		logger.info("checkFile.exists() : ", bDevVersion);
+		
+		bNewDanjiServer = CommonUtil.getUpdateNoticeNewDanjiServer();
+		logger.info("getUpdateNoticeNewDanjiServer checkFile.exists() : " + bNewDanjiServer);
+		
+		SERVERINFO.GasCount_HardCoding_One = CommonUtil.isGasOnlyOne();
+		logger.info("Gas Hard Coding checkFile.exists() : " + SERVERINFO.GasCount_HardCoding_One);
+		
+		logger.info("****************************************");
+		logger.info("Thread를 이용한 다중 접속 서버 작동됨...단지서버2.0 : " + bNewDanjiServer + " , 개발서버 : " + bDevVersion + " , 가스 1개 고정 : " + SERVERINFO.GasCount_HardCoding_One);
+		logger.info("연동 IP :" + SERVERINFO.SendEventURL + " , Port : " + SERVERINFO.SendEventPort);
+		logger.info("월패드 수신 포트 :" + SERVERINFO.WalladReceivePort);
+		logger.info("****************************************");
+
+		ServerSocket server = null;
+		long connectCount = 0;
+		try {
+			try {
+				LogMsgHandler logHandler = new LogMsgHandler();
+				logHandler.start();
+			} catch (Exception e) {
+				logger.error("message : " + e);
+			}
+			
+			server = new ServerSocket(SERVERINFO.WalladReceivePort);
+
+			while (true) {
+				logger.debug("클라이언트가 접속해 오기를 기다리고 있습니다....");
+				logger.info("[Waiting....");
+
+				Socket connectedClientSocket = server.accept();
+
+				InetAddress ia 	= connectedClientSocket.getInetAddress();	// 접속에 사용된 서버측 Address
+				int port 		= connectedClientSocket.getLocalPort();		// 접속에 사용된 서버측 PORT
+				String ip 		= ia.getHostAddress(); 						// 접속된 원격 Client IP
+
+				connectCount += 1; 											// 접속자수 카운트 증가
+				logger.info(" Connect Count : "+ connectCount + " , 접속-Local Port: " + port, " , Client IP: " + ip);
+
+				if (connectCount > 2000000000) connectCount = 0;			// [질문]- 아직 초기화 하는 이유를 모르겠슴돠.. int의 MAX_VALUE에 근접해서 인가요? ? ? ? ? .황.
+																			// [답변]- 
+				
+				// -------------------------------------------
+				// 스레드 관련 부분
+				// -------------------------------------------
+				// Handler 클래스로 client 소켓 전송
+				ThreadServerHandler handler = new ThreadServerHandler(connectedClientSocket, bNewDanjiServer);
+				// 스레드 시작, run()호출
+				handler.start(); // start() --> run() 호출
+				// -------------------------------------------
+			}
+		} catch (IOException ioe) {
+			logger.error("Exception generated...", ioe.getMessage());
+			System.err.println(ioe.getStackTrace());
+		} finally {
+			try {
+				server.close();
+			} catch (IOException ignored) {
+			}
+		}
+	}
+	
+
+	public static boolean isNewDanjiServer() {
+		return bNewDanjiServer;
+	}
+}
+
+

+ 245 - 0
src/main/java/com/icontrols/kakao/socket/wallpad/ThreadServerHandler.java

@@ -0,0 +1,245 @@
+package com.icontrols.kakao.socket.wallpad;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Resource;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import com.icontrols.kakao.common.ERROR_INFO;
+import com.icontrols.kakao.common.utils.XmlUtil;
+
+/**
+ * 
+ * @author 황의현 / 2019-03-13
+ * 클라이언트로 데이터를 전송할 스레드 클래스
+ * 
+ * 월패드에서 전송한 이벤트 메세지를 받아서 처리를 하는 스레드 클래스
+ *  
+ */
+public class ThreadServerHandler extends Thread {
+	private static final Logger logger = LoggerFactory.getLogger(ThreadServerHandler.class);
+
+	private Socket connectedClientSocket;					// 클라이언트와 연결된 소켓 객체
+	String XMLHeader;										// 사용 하지는 않지만 XML의 Header 역할을 하는 String --> 추후에도 사용하지 않으면 삭제 예정 
+	
+	private String strRcvMSG;								//  입력 받은 XML String 
+	private Document doc;									// 
+	
+	static final int EVENTTIMELIMITE = 2000;
+	static long lSocketConnectionPeriod = 0;
+	static int TIMEOUT_iMAP_WAIT = 3000; // iMAP 타임아웃
+	static int SOCKET_BUFFER_SIZE_READ = 50;
+	static boolean newDanjiServerNoticeUpdate = false;
+
+	
+	// 생성자
+	public ThreadServerHandler(Socket connectedClientSocket, boolean server) {
+		
+		// Client와 통신할 객체를 초기값으로 받아 할당합니다.
+		this.connectedClientSocket = connectedClientSocket;
+		this.newDanjiServerNoticeUpdate = server;
+		StringBuffer sb = new StringBuffer();
+		
+		sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n")
+		.append("	<imap ver = \"1.0\" address = \"10.0.1.1\" sender = \"단지서버\">\r\n");
+		XMLHeader = sb.toString();
+	}
+
+	public void run() {
+		try {
+			// 클라이언트로 내용을 출력 할 객체 생성
+			InputStream InStm = connectedClientSocket.getInputStream();									// Socket으로 연결된 클라이언트에게 Input Stream 을 얻는다.
+			connectedClientSocket.setSoLinger(true, 3000);												// 소켓 옵션 설정 소켓이 닫힐 때 아직 전송되지 않은 데이터 처리에 대한 옵션 --> 3000초 의 linger time을 설정
+			BufferedReader MsgReader = new BufferedReader(new InputStreamReader(InStm, "euc-kr"));
+			strRcvMSG = "";
+			
+			StringBuffer sbRcvMSG = new StringBuffer(); 												// Client에게 수신받은 iMap 메시지를 append한 뒤에 strRcvMSG에 할당한다.
+			StringBuffer sbInBuffer = null;
+			
+//			String InBuffer;
+			
+			boolean bRcvOK = false;
+			lSocketConnectionPeriod = System.currentTimeMillis() + TIMEOUT_iMAP_WAIT; // 타임아웃시간을 설정한다.
+
+			while (System.currentTimeMillis() < lSocketConnectionPeriod) {
+				try {
+					if (MsgReader.ready()) {
+						try {
+							char[] ReadData = new char[SOCKET_BUFFER_SIZE_READ * 1024];
+							sbInBuffer = new StringBuffer();
+							while (MsgReader.ready()) {
+								long charCnt = MsgReader.read(ReadData);
+								sbInBuffer.append(new String(ReadData, 0, (int) charCnt));
+							}
+
+							if (sbInBuffer != null || sbInBuffer.toString().trim().length() > 0)
+								sbRcvMSG.append(sbInBuffer.toString());
+							
+							if (0 < sbInBuffer.toString().indexOf("</imap>")) { // </imap> 으로 제대로 닫혔다면 클라이언트의 이벤트 메세지를 제대로 수신 했다는 말.
+								bRcvOK = true;
+								logger.info("[iMAPSvrProc().run()] Received DONE!!");
+								break;
+							}
+						} catch (Exception e) {
+							logger.error("message : " + e);
+							return;
+						}
+					} else {
+						Thread.sleep(100);
+					}
+				} catch (Exception e) {
+					logger.info("[Exception] iMAPSvrProc.run() ===== 2 =====");
+					connectedClientSocket.close();
+					logger.error("message : " + e);
+					return;
+				}finally {
+					strRcvMSG = sbRcvMSG.toString();
+				}
+			}
+
+			if (!bRcvOK) {
+				logger.error("[iMAPSvrProc().run()] iMAP message is unacceptable!! :\n\r" + strRcvMSG);
+				connectedClientSocket.close();
+				return;
+			}
+			
+			// 응답을 제대로 받은 이후의 처리 .. 제대로 된 응다블 받지 못했다면 위에서 소켓 연결 해제 
+			logger.info("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-");
+			logger.info("strRcvMSG :: > " + strRcvMSG);
+			
+			strRcvMSG = strRcvMSG.substring(strRcvMSG.indexOf("<")); // 이 부분에 대한 예시 데이터가 있으면 이해하기 좋을거 같다. 테스트 하면서 뽑아내고 주석으로 잘아보자.
+			
+			
+			// XML문서를 읽어보자구요!																		
+			// XML을 Document라는 객체로 인식
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();						// XML 문서를 읽기 위한 Factory 객체 생성
+			DocumentBuilder builder = factory.newDocumentBuilder();										// Builder 객체 생성
+			InputStream istream = new ByteArrayInputStream(strRcvMSG.getBytes("UTF-8"));
+			doc = builder.parse(istream);
+
+			// xpath 생성
+			XPath xpath = XPathFactory.newInstance().newXPath();
+
+			// id 가 c2 인 Node의 val attribute 값 가져오기
+			Node service = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+			String requestType = XmlUtil.getAttributeWithItem(service, "type");
+			logger.info("[ThreadServerHandler]-service type : " + requestType);
+			
+			String retXML = "";
+			if (requestType.equalsIgnoreCase("notice")) {
+				String requestCmd = XmlUtil.getAttributeWithItem(service, "name");
+				
+				logger.info("[ThreadServerHandler]-requestCmd : " + requestCmd);
+//				if (requestCmd.equalsIgnoreCase("msg_device_event")) { // 상태변경이벤트인경우에 처리
+					logger.info("[ThreadServerHandler]-strRcvMSG : " + strRcvMSG);
+					// 빈번하게 실행 될 가능성이 높다.
+					DeviceEventMsgHandler emh = new DeviceEventMsgHandler(doc, requestCmd);
+					emh.start();
+//				}
+			}
+		} catch (Exception e) {
+			try {
+				connectedClientSocket.close();
+			} catch (Exception E) {
+				logger.error("message : " + E);
+			}
+			logger.error("message : " + e);
+		}
+	}
+
+	// 클라이언트로 데이터를 전송할 스레드 클래스
+	class DeviceEventMsgHandler extends Thread {
+		// 멤버변수
+		private Document doc;
+		private String cmd;
+		
+		
+		// 생성자
+		public DeviceEventMsgHandler(Document doc, String cmd) {
+			this.doc = doc;
+		}
+
+		public void run() {
+			try {
+				// 데이터 뽑아서 이통사 연동 서버에 전달한다.
+				//
+				String model_id, status;
+				int dev_num;
+
+				// xpath 생성
+				XPath xpath = XPathFactory.newInstance().newXPath();
+
+				// id 가 c2 인 Node의 val attribute 값 가져오기
+				Node iMap = (Node) xpath.evaluate("//imap", doc, XPathConstants.NODE);
+				logger.info("[DeviceEventMsgHandler] - address : " + iMap.getAttributes().getNamedItem("address").getTextContent());
+				logger.info("[DeviceEventMsgHandler] - sender : "  + iMap.getAttributes().getNamedItem("sender").getTextContent());
+
+
+				String dongho = iMap.getAttributes().getNamedItem("sender").getTextContent();
+				String searchDong = (dongho.indexOf("d") >= 0)?"d":"동";
+				String searchHo = (dongho.indexOf("d") >= 0)?"h":"호";
+
+				int idxdong = dongho.indexOf(searchDong);
+				int idxho = dongho.indexOf(searchHo);
+
+				String dong = dongho.substring(0, idxdong).trim();
+				String ho = dongho.substring(idxdong + 1, idxho).trim();
+				
+				logger.info("[DeviceEventMsgHandler] - [동:" + dong + "호:" + ho + "]");
+
+				Node service = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+				logger.info(service.getAttributes().getNamedItem("type").getTextContent());
+
+				model_id = (String) xpath.evaluate("//imap/service/model_id ", doc, XPathConstants.STRING);
+				dev_num = Integer.parseInt((String) xpath.evaluate("//imap/service/dev_num", doc, XPathConstants.STRING));
+				status = (String) xpath.evaluate("//imap/service/status", doc, XPathConstants.STRING);
+				
+				logger.info("[DeviceEventMsgHandler] - model_id : " + model_id);
+				logger.info("[DeviceEventMsgHandler] - dev_num  : " + dev_num);
+				logger.info("[DeviceEventMsgHandler] - status   : " + status);
+
+				String endpointId = model_id + "_" + dev_num;
+				int int_dong = Integer.parseInt(dong);
+				int int_ho = Integer.parseInt(ho);
+				
+				Map <String, Object> body = new HashMap<String, Object>();
+				
+				KakaoMsgHandler kakaoMsgHandler = new KakaoMsgHandler();
+				
+				if(!status.equalsIgnoreCase("unsubscription")) {
+					logger.debug("--> endpointId : " + endpointId + " --> status -=-> [" + status+"]");
+					body.put(endpointId, KakaoMsgHandler.setDeviceState(endpointId, status));
+					logger.debug("body" + body.toString());
+					logger.debug("kakaoMshHandler is null = " + kakaoMsgHandler);
+					Map<String, Object> sendMap = kakaoMsgHandler.setKakaoEventMsg(int_dong, int_ho, body);
+					logger.debug("---->> "+sendMap.toString());
+					kakaoMsgHandler.SendStatusResult(int_dong, int_ho, sendMap, 0);
+				}
+			} catch (Exception e) {
+				logger.error("message : " + e);
+				e.printStackTrace();
+				
+			}
+		}
+
+		private String trimquotation(String sen) {
+			sen = sen.replaceAll("\"", "");
+			return sen.trim();
+		}
+	}
+}

+ 320 - 0
src/main/java/com/icontrols/kakao/socket/wallpad/WallpadMsgSender.java

@@ -0,0 +1,320 @@
+package com.icontrols.kakao.socket.wallpad;
+
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+
+import javax.annotation.Resource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.w3c.dom.Document;
+
+import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.SERVERINFO;
+import com.icontrols.kakao.common.THINGS_INFO;
+import com.icontrols.kakao.socket.utils.IPGenerator;
+import com.icontrols.kakao.socket.utils.XMLConstants.REMOTE_KIND;
+import com.icontrols.kakao.socket.utils.XMLGenerator;
+import com.icontrols.kakao.socket.utils.iMapRequestSync;
+
+
+@Service("wallpadMsgSender")
+public class WallpadMsgSender {
+	private final Logger logger = LoggerFactory.getLogger(WallpadMsgSender.class);
+	
+	@Resource(name="kakaoMsgHandler")
+	KakaoMsgHandler kMsgHandler;
+	
+	@Async("wallpadExecutor")
+	public CompletableFuture<Map<String, Object>> getAsyncTest(Map<String, Object> param){
+		Map<String, Object> map = new HashMap<String, Object>();
+		
+		try {
+			
+			logger.debug((String) param.get("name") + " -- Start");
+			
+			Thread.sleep(1000);
+			
+			
+			map.put("name", param.get("name"));
+			map.put("result", "success");
+			map.put("time", Calendar.DATE);
+			
+			Thread.sleep((long) param.get("time"));
+		} catch (InterruptedException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		
+		logger.debug((String) param.get("name") + "-- END");
+		return CompletableFuture.completedFuture(map);
+	}
+	
+	
+	/**
+	 * 비동기로 여러 기기의 상태조회 메세지를 호출한다.
+	 * @param dong
+	 * @param ho
+	 * @param endpointId
+	 * @return
+	 * @throws InterruptedException
+	 */
+	@Async("wallpadExecutor")
+	public CompletableFuture<Map<String, Object>> getDeviceStatus(int dong, int ho, String endpointId) throws InterruptedException{
+		Map<String, Object> retMap = new HashMap<String, Object> ();
+		retMap.putAll(kMsgHandler.getDeviceStatus(dong, ho, endpointId));
+		retMap.put(KEY_INFO.BODY_KEY_ENDPOINT_ID, endpointId);
+		
+		return CompletableFuture.completedFuture(retMap);
+	}
+	
+	/**
+	 * 월패드에 구독/해제 메세지를 전송한다.
+	 * @param param
+	 * @return
+	 */
+	public Document remoteDeviceSubscription(Map<String, Object> param) {
+		Document doc = null;
+		
+		try {
+			int dong = Integer.parseInt((String)param.get("dong"));
+			int ho = Integer.parseInt((String)param.get("ho"));
+			
+			String type = (String) param.get("type");
+			String company = (String) param.get("company");
+			
+			int devNum = Integer.parseInt((String)param.get("devNum"));
+			
+			REMOTE_KIND kind = type.equalsIgnoreCase("subs")?REMOTE_KIND.msg_device_subscription:REMOTE_KIND.msg_device_unsubscription;
+			
+			iMapRequestSync imapSocket = new iMapRequestSync(
+					IPGenerator.getIPFromDongHo(dong, ho),
+					SERVERINFO.clientNewPort);
+			
+			String xmlData = null;
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeaderForSUBS(kind.getContents()))
+			.append("      <req_company>").append(company).append("</req_company>\r\n")
+			.append("      <dev_num>").append(devNum).append("</dev_num>")
+			.append("      <model_id>wallpad</model_id>\r\n")
+			.append(XMLGenerator.MakeXMLTail());
+		
+			xmlData = sb.toString();
+			
+			int bRet = imapSocket.doWork(xmlData);
+			
+			if(bRet < 0) {
+				logger.error("[remoteDeviceList ERROR] - document null");
+				return null;
+			}
+			
+			return imapSocket.getDocument();
+		}catch(Exception e) {
+			logger.error("Subscription ERROR - " + e.getStackTrace());
+		}
+		
+		return null;
+	}
+	
+	/**
+	 * 월패드에 인증 번호를 전송한다.
+	 * @param param
+	 * @return
+	 */
+	public Document remoteDeviceAuth(Map<String, Object> param) {
+		Document doc = null;
+		int dong = Integer.parseInt((String)param.get("dong"));
+		int ho = Integer.parseInt((String)param.get("ho"));
+		String code = (String)param.get("code");
+		int expire_in = (int) param.get("expire_in");
+		String alias = "Kakao";
+		String twinid = UUID.randomUUID().toString();
+		String targetIP = IPGenerator.getIPFromDongHo(dong, ho);
+		try {
+			iMapRequestSync imapSocket = new iMapRequestSync(
+					IPGenerator.getIPFromDongHo(dong, ho),
+					SERVERINFO.clientNewPort);
+			
+			String xmlData = null;
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeaderForNotice(REMOTE_KIND.remote_register_new_device_auth_number.getContents()))
+			.append("      <home_info dong=\"").append(dong).append("\" ho=\"").append(ho).append("\" ip=\"").append(targetIP).append("\" type=\"main\"/>\r\n")
+			.append("      <device_info alias=\"" + alias + "\" twinid=\"" + twinid)
+			.append("\" auth_number=\"" + code + "\" />\r\n")
+			.append(XMLGenerator.MakeXMLTail());
+		
+			xmlData = sb.toString();
+			
+			int bRet = imapSocket.doWork(xmlData);
+			
+			if(bRet < 0) {
+				logger.error("[remoteDeviceList ERROR] - document null");
+				return null;
+			}
+			
+			return imapSocket.getDocument();
+		}catch(Exception e) {
+			logger.error("[remoteDeviceList ERROR] - " + e.getMessage());
+			return null;
+		}
+	}
+	
+	/**
+	 * 월패드와 연결된 기기목록을 요청한다 
+	 * @param dong
+	 * @param ho
+	 * @return
+	 */
+	public Document remoteDeviceList(int dong, int ho) {
+		Document doc = null;
+		try {
+			iMapRequestSync imapSocket = new iMapRequestSync(
+					IPGenerator.getIPFromDongHo(dong, ho),
+					SERVERINFO.clientNewPort);
+			
+			String xmlData = null;
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeader(dong, ho,REMOTE_KIND.renew_remote_get_home_devices.getContents()))
+				.append(XMLGenerator.MakeXMLTail());
+		
+			xmlData = sb.toString();
+			
+			int bRet = imapSocket.doWork(xmlData);
+			
+			if(bRet < 0) {
+				logger.error("[remoteDeviceList ERROR] - document null");
+				return null;
+			}
+			
+			return imapSocket.getDocument();
+		}catch(Exception e) {
+			logger.error("[remoteDeviceList ERROR] - " + e.getMessage());
+			return null;
+		}
+	}
+	
+	/**
+	 * 월패드와 연결된 기기들의 전체 상태를 요청한다.
+	 * @param dong
+	 * @param ho
+	 * @return
+	 */
+	public Document remoteDeviceStatusList(int dong, int ho) {
+		Document doc = null;
+		try {
+			iMapRequestSync imapSocket = new iMapRequestSync(
+					IPGenerator.getIPFromDongHo(dong, ho),
+					SERVERINFO.clientNewPort);
+			
+			String xmlData = null;
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeader(dong, ho,REMOTE_KIND.msg_total_devices_status.getContents()))
+				.append(XMLGenerator.MakeXMLTail());
+		
+			xmlData = sb.toString();
+			
+			int bRet = imapSocket.doWork(xmlData);
+			
+			if(bRet < 0) {
+				logger.error("[remoteDeviceList ERROR] - document null");
+				return null;
+			}
+			
+			return imapSocket.getDocument();
+		}catch(Exception e) {
+			logger.error("[remoteDeviceList ERROR] - " + e.getMessage());
+			return null;
+		}
+	}
+	
+	/**
+	 * 월패드에 디바이스의 상태정보를 조회하는 XML을 전송하고 결과 Document를반환한다.
+	 * @param dong
+	 * @param ho
+	 * @param modelId
+	 * @param cirNum
+	 * @return
+	 */
+	public Document remoteDeviceStatus(int dong, int ho, String modelId, int cirNum) {
+		try {
+			iMapRequestSync imapSocket = new iMapRequestSync(
+					IPGenerator.getIPFromDongHo(dong, ho),
+					SERVERINFO.clientNewPort);
+
+			String xmlData = null;
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeader(dong, ho,REMOTE_KIND.msg_home_device_get_status.getContents()))
+			.append("      <model_id>").append(modelId).append("</model_id>\r\n")
+			.append("      <dev_num>").append(cirNum).append("</dev_num>\r\n")
+			.append("      <command_id>").append(UUID.randomUUID().toString()).append("</command_id>\r\n")
+			.append(XMLGenerator.MakeXMLTail());
+			
+			xmlData = sb.toString();
+
+			int bRet = imapSocket.doWork(xmlData);
+			logger.debug(" bRet : " + bRet);
+
+			if (bRet == 0) {
+				logger.debug(" xml : " + imapSocket.getResponseXML());
+				return imapSocket.getDocument();
+			}
+		}catch(Exception e) {
+			return null;
+		}
+				
+		return null;
+	}
+	
+	/**
+	 * 월패드에 디바이스의 상태를 번경하는 XML을 전송하고 결과 Document를 반환한다.
+	 * @param dong
+	 * @param ho
+	 * @param modelId
+	 * @param devNum
+	 * @param action
+	 * @return
+	 */
+	public Document remoteDeviceControl(int dong, int ho, String modelId, int devNum, String action) {
+		iMapRequestSync imapSocket = new iMapRequestSync(
+				IPGenerator.getIPFromDongHo(dong, ho),
+				SERVERINFO.clientNewPort);
+
+		String xmlData = null;
+		StringBuffer sb = new StringBuffer();
+		
+		if(modelId.toLowerCase().contains(THINGS_INFO.THINGS.ELEVATOR.getThingsName().toLowerCase())) {
+			if(action.equalsIgnoreCase("down")) {
+				return null;
+			}
+			sb.append(XMLGenerator.MakeXMLHeader(dong, ho, REMOTE_KIND.msg_elevator_call.getContents()))
+			.append(XMLGenerator.MakeXMLTail());			
+		}else {
+			sb.append(XMLGenerator.MakeXMLHeader(dong, ho, REMOTE_KIND.msg_home_device_set_control.getContents()))
+			.append("      <req_company>UPLUS</req_company>\r\n")
+			.append("      <model_id>").append(modelId).append("</model_id>\r\n")
+			.append("      <dev_num>").append(devNum).append( "</dev_num>\r\n")
+			.append("      <action>").append(action).append("</action>\r\n")
+			.append("      <command_id>").append(UUID.randomUUID().toString()).append("</command_id>\r\n")
+			.append(XMLGenerator.MakeXMLTail());
+		}
+		
+		xmlData = sb.toString();
+
+		int bRet = imapSocket.doWork(xmlData);
+		
+		logger.debug("xml : -----------  ");
+		logger.debug(imapSocket.getResponseXML());
+		
+		return bRet == -1 ? null : imapSocket.getDocument();
+	}
+}

+ 16 - 0
src/main/resources/application.properties

@@ -0,0 +1,16 @@
+logging.config=classpath:logback-spring.xml
+
+## local 황의현 개발 디비
+#spring.datasource.url=jdbc:mariadb://localhost:33060/platform_danzi?useUnicode=true&charaterEncoding=utf-8
+#spring.datasource.username=root
+#spring.datasource.password=pangpang
+
+## 단지서버 디비
+spring.datasource.url=jdbc:mariadb://192.168.100.155:8005/platform_danzi?useUnicode=true&charaterEncoding=utf-8
+spring.datasource.username=valley
+spring.datasource.password=valley123
+
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.generate-ddl=true
+
+server.port=8002

+ 28 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <include resource="org/springframework/boot/logging/logback/base.xml"/>
+
+    <appender name="dailyRollingFileAppender"
+              class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <append>true</append>
+        <prudent>true</prudent>
+
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>log/LogFile_%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>DEBUG</level>
+        </filter>
+
+        <encoder>
+            <pattern>[DANZI] %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <logger name="com.icontrols.kakao" level="DEBUG"></logger>
+
+    <root level="INFO">
+        <appender-ref ref="dailyRollingFileAppender" />
+    </root>
+</configuration>

+ 16 - 0
src/test/java/com/icontrols/kakao/KakaoApplicationTests.java

@@ -0,0 +1,16 @@
+package com.icontrols.kakao;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class KakaoApplicationTests {
+
+	@Test
+	public void contextLoads() {
+	}
+
+}